2 * Copyright (c) 1999-2003, 2005-2008 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: xxx put contents here xxx
28 Version: xxx put version here xxx
30 Copyright: © 1997-1999 by Apple Computer, Inc., all rights reserved.
33 #include "Scavenger.h"
35 static void CompareVolHeaderBTreeSizes( SGlobPtr GPtr
,
36 VolumeObjectPtr theVOPtr
,
37 HFSPlusVolumeHeader
* thePriVHPtr
,
38 HFSPlusVolumeHeader
* theAltVHPtr
);
39 static void GetEmbeddedVolumeHeaders( SGlobPtr GPtr
,
40 HFSMasterDirectoryBlock
* myMDBPtr
,
41 Boolean isPrimaryMDB
);
42 static OSErr
GetVolumeObjectBlock( VolumeObjectPtr theVOPtr
,
44 BlockDescriptor
* theBlockDescPtr
);
45 static OSErr
VolumeObjectFixPrimaryBlock( void );
50 * Encode a UCS-2 (Unicode) string to UTF-8
52 int utf_encodestr(ucsp
, ucslen
, utf8p
, utf8len
, utf8plen
)
53 const u_int16_t
* ucsp
;
55 unsigned char * utf8p
;
59 unsigned char * bufstart
;
66 while (charcnt
-- > 0 && utf8plen
> 0) {
69 if (ucs_ch
< 0x0080) {
71 continue; /* skip over embedded NULLs */
75 } else if (ucs_ch
< 0x800) {
76 if (utf8plen
< 2) /* We're about to over-flow the buffer */
79 *utf8p
++ = (ucs_ch
>> 6) | 0xc0;
80 *utf8p
++ = (ucs_ch
& 0x3f) | 0x80;
82 if (utf8plen
< 3) /* We're about to over-flow the buffer */
85 *utf8p
++ = (ucs_ch
>> 12) | 0xe0;
86 *utf8p
++ = ((ucs_ch
>> 6) & 0x3f) | 0x80;
87 *utf8p
++ = ((ucs_ch
) & 0x3f) | 0x80;
91 *utf8len
= utf8p
- bufstart
;
100 * Decode a UTF-8 string back to UCS-2 (Unicode)
102 * N.B.: ucslen on input describes the length of the buffer;
103 * on return, it describes how many bytes were used.
106 utf_decodestr(utf8p
, utf8len
, ucsp
, ucslen
, ucsplen
)
107 const unsigned char * utf8p
;
119 while (utf8len
-- > 0 && (byte
= *utf8p
++) != '\0' && ucsplen
> 0) {
120 /* check for ascii */
127 switch (byte
& 0xf0) {
131 /* extract bits 6 - 10 from first byte */
132 ucs_ch
= (byte
& 0x1F) << 6;
134 return (-1); /* seq not minimal */
138 /* extract bits 12 - 15 from first byte */
139 ucs_ch
= (byte
& 0x0F) << 6;
141 /* extract bits 6 - 11 from second byte */
142 if (((byte
= *utf8p
++) & 0xc0) != 0x80)
146 ucs_ch
+= (byte
& 0x3F);
149 return (-1); /* seq not minimal */
155 /* extract bits 0 - 5 from final byte */
156 if (((byte
= *utf8p
++) & 0xc0) != 0x80)
160 ucs_ch
+= (byte
& 0x3F);
165 *ucslen
= (u_int8_t
*)ucsp
- (u_int8_t
*)bufstart
;
171 OSErr
GetFBlk( SGlobPtr GPtr
, SInt16 fileRefNum
, SInt32 blockNumber
, void **bufferH
);
176 UInt32
GetDFAStage( void )
181 void SetDFAStage( UInt32 stage
)
187 /*------------------------------------------------------------------------------
191 Function: Record errors detetected by scavenging operation.
193 Input: GPtr - pointer to scavenger global area.
197 ------------------------------------------------------------------------------*/
199 void RcdError( SGlobPtr GPtr
, OSErr errorCode
)
201 GPtr
->ErrCode
= errorCode
;
203 WriteError( GPtr
, errorCode
, GPtr
->TarID
, GPtr
->TarBlock
); // log to summary window
207 /*------------------------------------------------------------------------------
211 Function: Records an internal Scavenger error.
213 Input: GPtr - pointer to scavenger global area.
214 ErrCode - internal error code
216 Output: IntError - function result:
218 ------------------------------------------------------------------------------*/
220 int IntError( SGlobPtr GPtr
, OSErr errorCode
)
222 GPtr
->RepLevel
= repairLevelUnrepairable
;
224 if ( errorCode
== ioErr
) // Cast I/O errors as read errors
227 if( (errorCode
== R_RdErr
) || (errorCode
== R_WrErr
) )
229 GPtr
->ErrCode
= GPtr
->volumeErrorCode
;
235 GPtr
->ErrCode
= R_IntErr
;
236 GPtr
->IntErr
= errorCode
;
244 /*------------------------------------------------------------------------------
246 Routine: AllocBTN (Allocate BTree Node)
248 Function: Allocates an BTree node in a Scavenger BTree bit map.
250 Input: GPtr - pointer to scavenger global area.
251 StABN - starting allocation block number.
252 NmABlks - number of allocation blocks.
254 Output: AllocBTN - function result:
257 ------------------------------------------------------------------------------*/
259 int AllocBTN( SGlobPtr GPtr
, SInt16 fileRefNum
, UInt32 nodeNumber
)
264 BTreeControlBlock
*calculatedBTCB
= GetBTreeControlBlock( fileRefNum
);
267 if ( calculatedBTCB
->refCon
== 0)
270 byteP
= ( (BTreeExtensionsRec
*)calculatedBTCB
->refCon
)->BTCBMPtr
+ (nodeNumber
/ 8 ); // ptr to starting byte
271 bitPos
= nodeNumber
% 8; // bit offset
272 mask
= ( 0x80 >> bitPos
);
273 if ( (*byteP
& mask
) != 0 )
275 RcdError( GPtr
, E_OvlNode
);
276 return( E_OvlNode
); // node already allocated
278 *byteP
= *byteP
| mask
; // allocate it
279 calculatedBTCB
->freeNodes
--; // decrement free count
285 OSErr
GetBTreeHeader( SGlobPtr GPtr
, SFCB
*fcb
, BTHeaderRec
*header
)
288 BTHeaderRec
*headerRec
;
289 BlockDescriptor block
;
291 GPtr
->TarBlock
= kHeaderNodeNum
;
293 if (fcb
->fcbBlockSize
== 0)
294 (void) SetFileBlockSize(fcb
, 512);
296 err
= GetFileBlock(fcb
, kHeaderNodeNum
, kGetBlock
, &block
);
299 err
= hfs_swap_BTNode(&block
, fcb
, kSwapBTNodeHeaderRecordOnly
);
302 (void) ReleaseFileBlock(fcb
, &block
, kReleaseBlock
| kTrashBlock
);
306 headerRec
= (BTHeaderRec
*)((char*)block
.buffer
+ sizeof(BTNodeDescriptor
));
307 CopyMemory(headerRec
, header
, sizeof(BTHeaderRec
));
309 err
= hfs_swap_BTNode(&block
, fcb
, kSwapBTNodeHeaderRecordOnly
);
312 (void) ReleaseFileBlock(fcb
, &block
, kReleaseBlock
| kTrashBlock
);
316 err
= ReleaseFileBlock (fcb
, &block
, kReleaseBlock
);
319 /* Validate Node Size */
320 switch (header
->nodeSize
) {
331 RcdError( GPtr
, E_InvalidNodeSize
);
332 err
= E_InvalidNodeSize
;
339 /*------------------------------------------------------------------------------
341 Routine: IsDuplicateRepairOrder
343 Function: Search a duplicate repair order node in the GPtr->MinorRepairP
344 list. This function traverses the entire list of minor
345 repairs, and compares the following fields to determine a
346 match - type, forkType, correct, incorrect, maskBit, hint,
349 Input: GPtr - scavenger globals
350 orig - repair order to search and compare
352 Output: 0 - no duplicate was found,
353 1 - duplicate was found.
354 ------------------------------------------------------------------------------*/
355 int IsDuplicateRepairOrder(SGlobPtr GPtr
, RepairOrderPtr orig
)
360 cur
= GPtr
->MinorRepairsP
;
363 /* If all these values match, this is a duplicate */
364 if ((orig
->type
== cur
->type
) &&
365 (orig
->correct
== cur
->correct
) &&
366 (orig
->incorrect
== cur
->incorrect
) &&
367 (orig
->parid
== cur
->parid
) &&
368 (orig
->forkType
== cur
->forkType
) &&
369 (orig
->maskBit
== cur
->maskBit
) &&
370 (orig
->hint
== cur
->hint
)) {
381 /*------------------------------------------------------------------------------
383 Routine: DeleteRepairOrder
385 Function: Deletes the minor repair order that matches the repair order
386 provided from the list. This function should be called when
387 a duplicate repair order is detected.
389 Input: GPtr - scavenger globals
390 orig - repair order to remove
393 ------------------------------------------------------------------------------*/
394 void DeleteRepairOrder(SGlobPtr GPtr
, RepairOrderPtr orig
)
397 RepairOrderPtr prev
= NULL
;
399 cur
= GPtr
->MinorRepairsP
;
403 prev
->link
= cur
->link
;
405 if (cur
== GPtr
->MinorRepairsP
) {
406 GPtr
->MinorRepairsP
= cur
->link
;
418 /*------------------------------------------------------------------------------
420 Routine: Alloc[Minor/Major]RepairOrder
422 Function: Allocate a repair order node and link into the 'GPtr->RepairXxxxxP" list.
423 These are descriptions of minor/major repairs that need to be performed;
424 they are compiled during verification, and executed during minor/major repair.
426 Input: GPtr - scavenger globals
427 n - number of extra bytes needed, in addition to standard node size.
429 Output: Ptr to node, or NULL if out of memory or other error.
430 ------------------------------------------------------------------------------*/
432 RepairOrderPtr
AllocMinorRepairOrder( SGlobPtr GPtr
, size_t n
) /* #extra bytes needed */
434 RepairOrderPtr p
; // the node we allocate
436 n
+= sizeof( RepairOrder
); // add in size of basic node
438 p
= (RepairOrderPtr
) AllocateClearMemory( n
); // get the node
440 if ( p
!= NULL
) // if we got one...
442 p
->link
= GPtr
->MinorRepairsP
; // then link into list of repairs
443 GPtr
->MinorRepairsP
= p
;
445 else if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
446 plog( "\t%s - AllocateClearMemory failed to allocate %d bytes \n", __FUNCTION__
, n
);
448 if ( GPtr
->RepLevel
== repairLevelNoProblemsFound
)
449 GPtr
->RepLevel
= repairLevelVolumeRecoverable
;
451 return( p
); // return ptr to node
456 void InvalidateCalculatedVolumeBitMap( SGlobPtr GPtr
)
463 //------------------------------------------------------------------------------
464 // Routine: GetVolumeFeatures
466 // Function: Sets up some OS and volume specific flags
468 // Input: GPtr->DrvNum The volume to check
470 // Output: GPtr->volumeFeatures Bit vector
471 // GPtr->realVCB Real in-memory vcb
472 //------------------------------------------------------------------------------
475 OSErr
GetVolumeFeatures( SGlobPtr GPtr
)
479 GetVolParmsInfoBuffer buffer
;
482 GPtr
->volumeFeatures
= 0; // Initialize to zero
484 // Get the "real" vcb
485 err
= GetVCBDriveNum( &GPtr
->realVCB
, GPtr
->DrvNum
);
486 ReturnIfError( err
);
488 if ( GPtr
->realVCB
!= nil
)
490 GPtr
->volumeFeatures
|= volumeIsMountedMask
;
492 pb
.ioParam
.ioNamePtr
= nil
;
493 pb
.ioParam
.ioVRefNum
= GPtr
->realVCB
->vcbVRefNum
;
494 pb
.ioParam
.ioBuffer
= (Ptr
) &buffer
;
495 pb
.ioParam
.ioReqCount
= sizeof( buffer
);
497 if ( PBHGetVolParms( &pb
, false ) == noErr
)
499 if ( buffer
.vMAttrib
& (1 << bSupportsTrashVolumeCache
) )
500 GPtr
->volumeFeatures
|= supportsTrashVolumeCacheFeatureMask
;
503 // Check if the running system is HFS+ savy
504 err
= Gestalt( gestaltFSAttr
, &response
);
505 ReturnIfError( err
);
506 if ( (response
& (1 << gestaltFSSupportsHFSPlusVols
)) != 0 )
507 GPtr
->volumeFeatures
|= supportsHFSPlusVolsFeatureMask
;
515 /*-------------------------------------------------------------------------------
516 Routine: ClearMemory - clear a block of memory
518 -------------------------------------------------------------------------------*/
520 void ClearMemory( void* start
, UInt32 length
)
525 UInt32 fragCount
; // serves as both a length and quadlong count
526 // for the beginning and main fragment
531 // is request less than 4 bytes?
532 if ( length
< 4 ) // length = 1,2 or 3
534 bytePtr
= (UInt8
*) start
;
538 *bytePtr
++ = zero
; // clear one byte at a time
545 // are we aligned on an odd boundry?
546 fragCount
= (UInt32
) start
& 3;
548 if ( fragCount
) // fragCount = 1,2 or 3
550 bytePtr
= (UInt8
*) start
;
554 *bytePtr
++ = zero
; // clear one byte at a time
558 while ( (fragCount
< 4) && (length
> 0) );
563 dataPtr
= (UInt32
*) (((UInt32
) start
& 0xFFFFFFFC) + 4); // make it long word aligned
567 dataPtr
= (UInt32
*) ((UInt32
) start
& 0xFFFFFFFC); // make it long word aligned
570 // At this point dataPtr is long aligned
572 // are there odd bytes to copy?
573 fragCount
= length
& 3;
577 bytePtr
= (UInt8
*) ((UInt32
) dataPtr
+ (UInt32
) length
- 1); // point to last byte
579 length
-= fragCount
; // adjust remaining length
583 *bytePtr
-- = zero
; // clear one byte at a time
585 while ( --fragCount
);
591 // At this point length is a multiple of 4
595 DebugStr("\p ClearMemory: length < 4");
598 // fix up beginning to get us on a 64 byte boundary
599 fragCount
= length
& (64-1);
602 if ( fragCount
< 4 && fragCount
> 0 )
603 DebugStr("\p ClearMemory: fragCount < 4");
608 length
-= fragCount
; // subtract fragment from length now
609 fragCount
>>= 2; // divide by 4 to get a count, for DBRA loop
612 // clear 4 bytes at a time...
618 // Are we finished yet?
622 // Time to turn on the fire hose
623 length
>>= 6; // divide by 64 to get count
626 // spray 64 bytes at a time...
627 *dataPtr
++ = zero
; *dataPtr
++ = zero
; *dataPtr
++ = zero
; *dataPtr
++ = zero
;
628 *dataPtr
++ = zero
; *dataPtr
++ = zero
; *dataPtr
++ = zero
; *dataPtr
++ = zero
;
629 *dataPtr
++ = zero
; *dataPtr
++ = zero
; *dataPtr
++ = zero
; *dataPtr
++ = zero
;
630 *dataPtr
++ = zero
; *dataPtr
++ = zero
; *dataPtr
++ = zero
; *dataPtr
++ = zero
;
639 CopyCatalogName(const CatalogName
*srcName
, CatalogName
*dstName
, Boolean isHFSPLus
)
643 if ( srcName
== NULL
)
645 if ( dstName
!= NULL
)
646 dstName
->ustr
.length
= 0; // set length byte to zero (works for both unicode and pascal)
651 length
= sizeof(UniChar
) * (srcName
->ustr
.length
+ 1);
653 length
= sizeof(UInt8
) + srcName
->pstr
[0];
656 CopyMemory(srcName
, dstName
, length
);
658 dstName
->ustr
.length
= 0; // set length byte to zero (works for both unicode and pascal)
663 CatalogNameLength(const CatalogName
*name
, Boolean isHFSPlus
)
666 return name
->ustr
.length
;
668 return name
->pstr
[0];
672 UInt32
CatalogNameSize( const CatalogName
*name
, Boolean isHFSPlus
)
674 UInt32 length
= CatalogNameLength( name
, isHFSPlus
);
677 length
*= sizeof(UniChar
);
683 //******************************************************************************
684 // Routine: BuildCatalogKey
686 // Function: Constructs a catalog key record (ckr) given the parent
687 // folder ID and CName. Works for both classic and extended
690 //******************************************************************************
693 BuildCatalogKey(HFSCatalogNodeID parentID
, const CatalogName
*cName
, Boolean isHFSPlus
, CatalogKey
*key
)
697 key
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
; // initial key length (4 + 2)
698 key
->hfsPlus
.parentID
= parentID
; // set parent ID
699 key
->hfsPlus
.nodeName
.length
= 0; // null CName length
702 CopyCatalogName(cName
, (CatalogName
*) &key
->hfsPlus
.nodeName
, isHFSPlus
);
703 key
->hfsPlus
.keyLength
+= sizeof(UniChar
) * cName
->ustr
.length
; // add CName size to key length
708 key
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
; // initial key length (1 + 4 + 1)
709 key
->hfs
.reserved
= 0; // clear unused byte
710 key
->hfs
.parentID
= parentID
; // set parent ID
711 key
->hfs
.nodeName
[0] = 0; // null CName length
714 UpdateCatalogName(cName
->pstr
, key
->hfs
.nodeName
);
715 key
->hfs
.keyLength
+= key
->hfs
.nodeName
[0]; // add CName size to key length
721 // Defined in BTreesPrivate.h, but not implemented in the BTree code?
722 // So... here's the implementation
723 SInt32
CompareKeys( BTreeControlBlockPtr btreePtr
, KeyPtr searchKey
, KeyPtr trialKey
)
725 KeyCompareProcPtr compareProc
= (KeyCompareProcPtr
)btreePtr
->keyCompareProc
;
727 return( compareProc(searchKey
, trialKey
) );
732 UpdateCatalogName(ConstStr31Param srcName
, Str31 destName
)
734 Size length
= srcName
[0];
736 if (length
> kHFSMaxFileNameChars
)
737 length
= kHFSMaxFileNameChars
; // truncate to max
739 destName
[0] = length
; // set length byte
741 CopyMemory(&srcName
[1], &destName
[1], length
);
746 UpdateVolumeEncodings(SVCB
*volume
, TextEncoding encoding
)
752 index
= MapEncodingToIndex(encoding
);
754 volume
->vcbEncodingsBitmap
|= (u_int64_t
)(1ULL << index
);
756 // vcb should already be marked dirty
760 //******************************************************************************
761 // Routine: VolumeObjectFixPrimaryBlock
763 // Function: Use the alternate Volume Header or Master Directory block (depending
764 // on the type of volume) to restore the primary block. This routine
765 // depends upon our intialization code to set up where are blocks are
768 // Result: 0 if all is well, noMacDskErr when we do not have a primary block
769 // number or whatever GetVolumeObjectAlternateBlock returns.
770 //******************************************************************************
772 static OSErr
VolumeObjectFixPrimaryBlock( void )
775 VolumeObjectPtr myVOPtr
;
776 UInt64 myPrimaryBlockNum
;
777 BlockDescriptor myPrimary
;
778 BlockDescriptor myAlternate
;
780 myVOPtr
= GetVolumeObjectPtr( );
781 myPrimary
.buffer
= NULL
;
782 myAlternate
.buffer
= NULL
;
784 GetVolumeObjectPrimaryBlockNum( &myPrimaryBlockNum
);
785 if ( myPrimaryBlockNum
== 0 )
786 return( noMacDskErr
);
788 // we don't care if this is a valid primary block since we're
789 // about to write over it
790 err
= GetVolumeObjectPrimaryBlock( &myPrimary
);
791 if ( !(err
== noErr
|| err
== badMDBErr
|| err
== noMacDskErr
) )
792 goto ExitThisRoutine
;
794 // restore the primary block from the alternate
795 err
= GetVolumeObjectAlternateBlock( &myAlternate
);
797 // invalidate if we have not marked the alternate as OK
798 if ( VolumeObjectIsHFS( ) ) {
799 if ( (myVOPtr
->flags
& kVO_AltMDBOK
) == 0 )
802 else if ( (myVOPtr
->flags
& kVO_AltVHBOK
) == 0 ) {
806 if ( err
== noErr
) {
807 CopyMemory( myAlternate
.buffer
, myPrimary
.buffer
, Blk_Size
);
808 (void) ReleaseVolumeBlock( myVOPtr
->vcbPtr
, &myPrimary
, kForceWriteBlock
);
809 myPrimary
.buffer
= NULL
;
810 if ( myVOPtr
->volumeType
== kHFSVolumeType
)
811 myVOPtr
->flags
|= kVO_PriMDBOK
;
813 myVOPtr
->flags
|= kVO_PriVHBOK
;
817 if ( myPrimary
.buffer
!= NULL
)
818 (void) ReleaseVolumeBlock( myVOPtr
->vcbPtr
, &myPrimary
, kReleaseBlock
);
819 if ( myAlternate
.buffer
!= NULL
)
820 (void) ReleaseVolumeBlock( myVOPtr
->vcbPtr
, &myAlternate
, kReleaseBlock
);
824 } /* VolumeObjectFixPrimaryBlock */
827 //******************************************************************************
828 // Routine: GetVolumeObjectVHBorMDB
830 // Function: Get the Volume Header block or Master Directory block (depending
831 // on type of volume). This will normally return the alternate, but
832 // it may return the primary when the alternate is damaged or cannot
835 // Result: returns 0 when all is well.
836 //******************************************************************************
837 OSErr
GetVolumeObjectVHBorMDB( BlockDescriptor
* theBlockDescPtr
)
840 VolumeObjectPtr myVOPtr
;
843 myVOPtr
= GetVolumeObjectPtr( );
844 GetVolumeObjectBlockNum( &myBlockNum
);
846 err
= GetVolumeObjectBlock( myVOPtr
, myBlockNum
, theBlockDescPtr
);
849 if ( myVOPtr
->volumeType
== kEmbededHFSPlusVolumeType
||
850 myVOPtr
->volumeType
== kPureHFSPlusVolumeType
)
852 err
= ValidVolumeHeader( (HFSPlusVolumeHeader
*) theBlockDescPtr
->buffer
);
854 else if ( myVOPtr
->volumeType
== kHFSVolumeType
)
856 HFSMasterDirectoryBlock
* myMDBPtr
;
857 myMDBPtr
= (HFSMasterDirectoryBlock
*) theBlockDescPtr
->buffer
;
858 if ( myMDBPtr
->drSigWord
!= kHFSSigWord
)
867 } /* GetVolumeObjectVHBorMDB */
870 //******************************************************************************
871 // Routine: GetVolumeObjectAlternateBlock
873 // Function: Get the alternate Volume Header block or Master Directory block
874 // (depending on type of volume).
875 // Result: returns 0 when all is well.
876 //******************************************************************************
877 OSErr
GetVolumeObjectAlternateBlock( BlockDescriptor
* theBlockDescPtr
)
880 VolumeObjectPtr myVOPtr
;
883 myVOPtr
= GetVolumeObjectPtr( );
884 GetVolumeObjectAlternateBlockNum( &myBlockNum
);
886 err
= GetVolumeObjectBlock( myVOPtr
, myBlockNum
, theBlockDescPtr
);
889 if ( myVOPtr
->volumeType
== kEmbededHFSPlusVolumeType
||
890 myVOPtr
->volumeType
== kPureHFSPlusVolumeType
)
892 err
= ValidVolumeHeader( (HFSPlusVolumeHeader
*) theBlockDescPtr
->buffer
);
894 else if ( myVOPtr
->volumeType
== kHFSVolumeType
)
896 HFSMasterDirectoryBlock
* myMDBPtr
;
897 myMDBPtr
= (HFSMasterDirectoryBlock
*) theBlockDescPtr
->buffer
;
898 if ( myMDBPtr
->drSigWord
!= kHFSSigWord
)
907 } /* GetVolumeObjectAlternateBlock */
910 //******************************************************************************
911 // Routine: GetVolumeObjectPrimaryBlock
913 // Function: Get the primary Volume Header block or Master Directory block
914 // (depending on type of volume).
915 // Result: returns 0 when all is well.
916 //******************************************************************************
917 OSErr
GetVolumeObjectPrimaryBlock( BlockDescriptor
* theBlockDescPtr
)
920 VolumeObjectPtr myVOPtr
;
923 myVOPtr
= GetVolumeObjectPtr( );
924 GetVolumeObjectPrimaryBlockNum( &myBlockNum
);
926 err
= GetVolumeObjectBlock( myVOPtr
, myBlockNum
, theBlockDescPtr
);
929 if ( myVOPtr
->volumeType
== kEmbededHFSPlusVolumeType
||
930 myVOPtr
->volumeType
== kPureHFSPlusVolumeType
)
932 err
= ValidVolumeHeader( (HFSPlusVolumeHeader
*) theBlockDescPtr
->buffer
);
934 else if ( myVOPtr
->volumeType
== kHFSVolumeType
)
936 HFSMasterDirectoryBlock
* myMDBPtr
;
937 myMDBPtr
= (HFSMasterDirectoryBlock
*) theBlockDescPtr
->buffer
;
938 if ( myMDBPtr
->drSigWord
!= kHFSSigWord
)
947 } /* GetVolumeObjectPrimaryBlock */
950 //******************************************************************************
951 // Routine: GetVolumeObjectVHB
953 // Function: Get the Volume Header block using either the primary or alternate
954 // block number as set up by InitializeVolumeObject. This will normally
955 // return the alternate, but it may return the primary when the
956 // alternate is damaged or cannot be found.
958 // Result: returns 0 when all is well or passes results of GetVolumeBlock or
959 // ValidVolumeHeader.
960 //******************************************************************************
961 OSErr
GetVolumeObjectVHB( BlockDescriptor
* theBlockDescPtr
)
964 VolumeObjectPtr myVOPtr
;
967 myVOPtr
= GetVolumeObjectPtr( );
968 myBlockNum
= ((myVOPtr
->flags
& kVO_AltVHBOK
) != 0) ? myVOPtr
->alternateVHB
: myVOPtr
->primaryVHB
;
969 err
= GetVolumeObjectBlock( myVOPtr
, myBlockNum
, theBlockDescPtr
);
971 err
= ValidVolumeHeader( (HFSPlusVolumeHeader
*) theBlockDescPtr
->buffer
);
975 } /* GetVolumeObjectVHB */
978 //******************************************************************************
979 // Routine: GetVolumeObjectAlternateMDB
981 // Function: Get the Master Directory Block using the alternate master directory
982 // block number as set up by InitializeVolumeObject.
984 // Result: returns 0 when all is well.
985 //******************************************************************************
986 OSErr
GetVolumeObjectAlternateMDB( BlockDescriptor
* theBlockDescPtr
)
988 VolumeObjectPtr myVOPtr
;
991 myVOPtr
= GetVolumeObjectPtr( );
992 err
= GetVolumeObjectBlock( NULL
, myVOPtr
->alternateMDB
, theBlockDescPtr
);
995 HFSMasterDirectoryBlock
* myMDBPtr
;
996 myMDBPtr
= (HFSMasterDirectoryBlock
*) theBlockDescPtr
->buffer
;
997 if ( myMDBPtr
->drSigWord
!= kHFSSigWord
)
1003 } /* GetVolumeObjectAlternateMDB */
1006 //******************************************************************************
1007 // Routine: GetVolumeObjectPrimaryMDB
1009 // Function: Get the Master Directory Block using the primary master directory
1010 // block number as set up by InitializeVolumeObject.
1012 // Result: returns 0 when all is well.
1013 //******************************************************************************
1014 OSErr
GetVolumeObjectPrimaryMDB( BlockDescriptor
* theBlockDescPtr
)
1016 VolumeObjectPtr myVOPtr
;
1019 myVOPtr
= GetVolumeObjectPtr( );
1020 err
= GetVolumeObjectBlock( NULL
, myVOPtr
->primaryMDB
, theBlockDescPtr
);
1023 HFSMasterDirectoryBlock
* myMDBPtr
;
1024 myMDBPtr
= (HFSMasterDirectoryBlock
*) theBlockDescPtr
->buffer
;
1025 if ( myMDBPtr
->drSigWord
!= kHFSSigWord
)
1031 } /* GetVolumeObjectPrimaryMDB */
1034 //******************************************************************************
1035 // Routine: GetVolumeObjectBlock
1037 // Function: Get the Volume Header block or Master Directory block using the
1038 // given block number.
1039 // Result: returns 0 when all is well or passes results of GetVolumeBlock or
1040 // ValidVolumeHeader.
1041 //******************************************************************************
1042 static OSErr
GetVolumeObjectBlock( VolumeObjectPtr theVOPtr
,
1044 BlockDescriptor
* theBlockDescPtr
)
1048 if ( theVOPtr
== NULL
)
1049 theVOPtr
= GetVolumeObjectPtr( );
1051 err
= GetVolumeBlock( theVOPtr
->vcbPtr
, theBlockNum
, kGetBlock
, theBlockDescPtr
);
1055 } /* GetVolumeObjectBlock */
1058 //******************************************************************************
1059 // Routine: GetVolumeObjectBlockNum
1061 // Function: Extract the appropriate block number for the volume header or
1062 // master directory (depanding on volume type) from the VolumeObject.
1063 // NOTE - this routine may return the primary or alternate block
1064 // depending on which one is valid. Preference is always given to
1067 // Result: returns block number of MDB or VHB or 0 if none are valid or
1068 // if volume type is unknown.
1069 //******************************************************************************
1070 void GetVolumeObjectBlockNum( UInt64
* theBlockNumPtr
)
1072 VolumeObjectPtr myVOPtr
;
1074 myVOPtr
= GetVolumeObjectPtr( );
1075 *theBlockNumPtr
= 0; // default to none
1077 // NOTE - we use alternate volume header or master directory
1078 // block before the primary because it is less likely to be damaged.
1079 if ( myVOPtr
->volumeType
== kEmbededHFSPlusVolumeType
||
1080 myVOPtr
->volumeType
== kPureHFSPlusVolumeType
) {
1081 if ( (myVOPtr
->flags
& kVO_AltVHBOK
) != 0 )
1082 *theBlockNumPtr
= myVOPtr
->alternateVHB
;
1084 *theBlockNumPtr
= myVOPtr
->primaryVHB
;
1086 else if ( myVOPtr
->volumeType
== kHFSVolumeType
) {
1087 if ( (myVOPtr
->flags
& kVO_AltMDBOK
) != 0 )
1088 *theBlockNumPtr
= myVOPtr
->alternateMDB
;
1090 *theBlockNumPtr
= myVOPtr
->primaryMDB
;
1095 } /* GetVolumeObjectBlockNum */
1098 //******************************************************************************
1099 // Routine: GetVolumeObjectAlternateBlockNum
1101 // Function: Extract the alternate block number for the volume header or
1102 // master directory (depanding on volume type) from the VolumeObject.
1104 // Result: returns block number of alternate MDB or VHB or 0 if none are
1105 // valid or if volume type is unknown.
1106 //******************************************************************************
1107 void GetVolumeObjectAlternateBlockNum( UInt64
* theBlockNumPtr
)
1109 VolumeObjectPtr myVOPtr
;
1111 myVOPtr
= GetVolumeObjectPtr( );
1112 *theBlockNumPtr
= 0; // default to none
1114 if ( myVOPtr
->volumeType
== kEmbededHFSPlusVolumeType
||
1115 myVOPtr
->volumeType
== kPureHFSPlusVolumeType
) {
1116 *theBlockNumPtr
= myVOPtr
->alternateVHB
;
1118 else if ( myVOPtr
->volumeType
== kHFSVolumeType
) {
1119 *theBlockNumPtr
= myVOPtr
->alternateMDB
;
1124 } /* GetVolumeObjectAlternateBlockNum */
1127 //******************************************************************************
1128 // Routine: GetVolumeObjectPrimaryBlockNum
1130 // Function: Extract the primary block number for the volume header or
1131 // master directory (depanding on volume type) from the VolumeObject.
1133 // Result: returns block number of primary MDB or VHB or 0 if none are valid
1134 // or if volume type is unknown.
1135 //******************************************************************************
1136 void GetVolumeObjectPrimaryBlockNum( UInt64
* theBlockNumPtr
)
1138 VolumeObjectPtr myVOPtr
;
1140 myVOPtr
= GetVolumeObjectPtr( );
1141 *theBlockNumPtr
= 0; // default to none
1143 if ( myVOPtr
->volumeType
== kEmbededHFSPlusVolumeType
||
1144 myVOPtr
->volumeType
== kPureHFSPlusVolumeType
) {
1145 *theBlockNumPtr
= myVOPtr
->primaryVHB
;
1147 else if ( myVOPtr
->volumeType
== kHFSVolumeType
) {
1148 *theBlockNumPtr
= myVOPtr
->primaryMDB
;
1153 } /* GetVolumeObjectPrimaryBlockNum */
1156 //******************************************************************************
1157 // Routine: InitializeVolumeObject
1159 // Function: Locate volume headers and / or master directory blocks for this
1160 // volume and fill where they are located on the volume and the type
1161 // of volume we are dealing with. We have three types of HFS volumes:
1162 // ¥ HFS - standard (old format) where primary MDB is 2nd block into
1163 // the volume and alternate MDB is 2nd to last block on the volume.
1164 // ¥ pure HFS+ - where primary volume header is 2nd block into
1165 // the volume and alternate volume header is 2nd to last block on
1167 // ¥ wrapped HFS+ - where primary MDB is 2nd block into the volume and
1168 // alternate MDB is 2nd to last block on the volume. The embedded
1169 // HFS+ volume header locations are calculated from drEmbedExtent
1172 // Result: returns nothing. Will fill in SGlob.VolumeObject data
1173 //******************************************************************************
1174 void InitializeVolumeObject( SGlobPtr GPtr
)
1177 HFSMasterDirectoryBlock
* myMDBPtr
;
1178 HFSPlusVolumeHeader
* myVHPtr
;
1179 VolumeObjectPtr myVOPtr
;
1180 HFSPlusVolumeHeader myPriVolHeader
;
1181 BlockDescriptor myBlockDescriptor
;
1183 myBlockDescriptor
.buffer
= NULL
;
1184 myVOPtr
= GetVolumeObjectPtr( );
1185 myVOPtr
->flags
|= kVO_Inited
;
1186 myVOPtr
->vcbPtr
= GPtr
->calculatedVCB
;
1188 // Determine volume size in sectors
1189 err
= GetDeviceSize( GPtr
->calculatedVCB
->vcbDriveNumber
,
1190 &myVOPtr
->totalDeviceSectors
,
1191 &myVOPtr
->sectorSize
);
1192 if ( (myVOPtr
->totalDeviceSectors
< 3) || (err
!= noErr
) ) {
1193 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
1194 plog("\tinvalid device information for volume - total sectors = %qd sector size = %d \n",
1195 myVOPtr
->totalDeviceSectors
, myVOPtr
->sectorSize
);
1200 // get the primary volume header or master directory block (depending on volume type)
1201 // should always be block 2 (relative to 0) into the volume.
1202 err
= GetVolumeObjectBlock( myVOPtr
, MDB_BlkN
, &myBlockDescriptor
);
1203 if ( err
== noErr
) {
1204 myMDBPtr
= (HFSMasterDirectoryBlock
*) myBlockDescriptor
.buffer
;
1205 if ( myMDBPtr
->drSigWord
== kHFSPlusSigWord
|| myMDBPtr
->drSigWord
== kHFSXSigWord
) {
1206 myVHPtr
= (HFSPlusVolumeHeader
*) myMDBPtr
;
1208 myVOPtr
->primaryVHB
= MDB_BlkN
; // save location
1209 myVOPtr
->alternateVHB
= myVOPtr
->totalDeviceSectors
- 2; // save location
1210 err
= ValidVolumeHeader( myVHPtr
);
1211 if ( err
== noErr
) {
1212 myVOPtr
->flags
|= kVO_PriVHBOK
;
1213 bcopy( myVHPtr
, &myPriVolHeader
, sizeof( *myVHPtr
) );
1216 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
1217 plog( "\tInvalid primary volume header - error %d \n", err
);
1221 else if ( myMDBPtr
->drSigWord
== kHFSSigWord
) {
1222 // we could have an HFS or wrapped HFS+ volume
1223 myVOPtr
->primaryMDB
= MDB_BlkN
; // save location
1224 myVOPtr
->alternateMDB
= myVOPtr
->totalDeviceSectors
- 2; // save location
1225 myVOPtr
->flags
|= kVO_PriMDBOK
;
1228 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
1229 plog( "\tBlock %d is not an MDB or Volume Header \n", MDB_BlkN
);
1232 (void) ReleaseVolumeBlock( GPtr
->calculatedVCB
, &myBlockDescriptor
, kReleaseBlock
);
1235 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
1236 plog( "\tcould not get volume block %d, err %d \n", MDB_BlkN
, err
);
1240 // get the alternate volume header or master directory block (depending on volume type)
1241 // should always be 2nd to last sector.
1242 err
= GetVolumeObjectBlock( myVOPtr
, myVOPtr
->totalDeviceSectors
- 2, &myBlockDescriptor
);
1243 if ( err
== noErr
) {
1244 myMDBPtr
= (HFSMasterDirectoryBlock
*) myBlockDescriptor
.buffer
;
1245 if ( myMDBPtr
->drSigWord
== kHFSPlusSigWord
|| myMDBPtr
->drSigWord
== kHFSXSigWord
) {
1246 myVHPtr
= (HFSPlusVolumeHeader
*) myMDBPtr
;
1248 myVOPtr
->primaryVHB
= MDB_BlkN
; // save location
1249 myVOPtr
->alternateVHB
= myVOPtr
->totalDeviceSectors
- 2; // save location
1250 err
= ValidVolumeHeader( myVHPtr
);
1251 if ( err
== noErr
) {
1252 // check to see if the primary and alternates are in sync. 3137809
1253 myVOPtr
->flags
|= kVO_AltVHBOK
;
1254 CompareVolHeaderBTreeSizes( GPtr
, myVOPtr
, &myPriVolHeader
, myVHPtr
);
1257 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
1258 plog( "\tInvalid alternate volume header - error %d \n", err
);
1262 else if ( myMDBPtr
->drSigWord
== kHFSSigWord
) {
1263 myVOPtr
->primaryMDB
= MDB_BlkN
; // save location
1264 myVOPtr
->alternateMDB
= myVOPtr
->totalDeviceSectors
- 2; // save location
1265 myVOPtr
->flags
|= kVO_AltMDBOK
;
1268 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
1269 plog( "\tBlock %qd is not an MDB or Volume Header \n", myVOPtr
->totalDeviceSectors
- 2 );
1273 (void) ReleaseVolumeBlock( GPtr
->calculatedVCB
, &myBlockDescriptor
, kReleaseBlock
);
1276 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
1277 plog( "\tcould not get alternate volume header at %qd, err %d \n",
1278 myVOPtr
->totalDeviceSectors
- 2, err
);
1282 // get the embedded volume header (if applicable).
1283 if ( (myVOPtr
->flags
& kVO_AltMDBOK
) != 0 ) {
1284 err
= GetVolumeObjectBlock( myVOPtr
, myVOPtr
->alternateMDB
, &myBlockDescriptor
);
1285 if ( err
== noErr
) {
1286 myMDBPtr
= (HFSMasterDirectoryBlock
*) myBlockDescriptor
.buffer
;
1287 GetEmbeddedVolumeHeaders( GPtr
, myMDBPtr
, false );
1288 (void) ReleaseVolumeBlock( GPtr
->calculatedVCB
, &myBlockDescriptor
, kReleaseBlock
);
1292 // Now we will look for embedded HFS+ volume headers using the primary MDB if
1293 // we haven't already located them.
1294 if ( (myVOPtr
->flags
& kVO_PriMDBOK
) != 0 &&
1295 ((myVOPtr
->flags
& kVO_PriVHBOK
) == 0 || (myVOPtr
->flags
& kVO_AltVHBOK
) == 0) ) {
1296 err
= GetVolumeObjectBlock( myVOPtr
, myVOPtr
->primaryMDB
, &myBlockDescriptor
);
1297 if ( err
== noErr
) {
1298 myMDBPtr
= (HFSMasterDirectoryBlock
*) myBlockDescriptor
.buffer
;
1299 GetEmbeddedVolumeHeaders( GPtr
, myMDBPtr
, true );
1300 (void) ReleaseVolumeBlock( GPtr
->calculatedVCB
, &myBlockDescriptor
, kReleaseBlock
);
1303 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
1304 plog( "\tcould not get primary MDB at block %qd, err %d \n", myVOPtr
->primaryMDB
, err
);
1310 // set the type of volume using the flags we set as we located the various header / master
1312 if ( ((myVOPtr
->flags
& kVO_PriVHBOK
) != 0 || (myVOPtr
->flags
& kVO_AltVHBOK
) != 0) &&
1313 ((myVOPtr
->flags
& kVO_PriMDBOK
) != 0 || (myVOPtr
->flags
& kVO_AltMDBOK
) != 0) ) {
1314 myVOPtr
->volumeType
= kEmbededHFSPlusVolumeType
;
1316 else if ( ((myVOPtr
->flags
& kVO_PriVHBOK
) != 0 || (myVOPtr
->flags
& kVO_AltVHBOK
) != 0) &&
1317 (myVOPtr
->flags
& kVO_PriMDBOK
) == 0 && (myVOPtr
->flags
& kVO_AltMDBOK
) == 0 ) {
1318 myVOPtr
->volumeType
= kPureHFSPlusVolumeType
;
1320 else if ( (myVOPtr
->flags
& kVO_PriVHBOK
) == 0 && (myVOPtr
->flags
& kVO_AltVHBOK
) == 0 &&
1321 ((myVOPtr
->flags
& kVO_PriMDBOK
) != 0 || (myVOPtr
->flags
& kVO_AltMDBOK
) != 0) ) {
1322 myVOPtr
->volumeType
= kHFSVolumeType
;
1325 myVOPtr
->volumeType
= kUnknownVolumeType
;
1329 } /* InitializeVolumeObject */
1332 //******************************************************************************
1333 // Routine: PrintVolumeObject
1335 // Function: Print out some helpful info about the state of our VolumeObject.
1337 // Result: returns nothing.
1338 //******************************************************************************
1339 void PrintVolumeObject( void )
1341 VolumeObjectPtr myVOPtr
;
1343 myVOPtr
= GetVolumeObjectPtr( );
1345 if ( myVOPtr
->volumeType
== kHFSVolumeType
)
1346 plog( "\tvolume type is HFS \n" );
1347 else if ( myVOPtr
->volumeType
== kEmbededHFSPlusVolumeType
)
1348 plog( "\tvolume type is embedded HFS+ \n" );
1349 else if ( myVOPtr
->volumeType
== kPureHFSPlusVolumeType
)
1350 plog( "\tvolume type is pure HFS+ \n" );
1352 plog( "\tunknown volume type \n" );
1354 plog( "\tprimary MDB is at block %qd 0x%02qx \n", myVOPtr
->primaryMDB
, myVOPtr
->primaryMDB
);
1355 plog( "\talternate MDB is at block %qd 0x%02qx \n", myVOPtr
->alternateMDB
, myVOPtr
->alternateMDB
);
1356 plog( "\tprimary VHB is at block %qd 0x%02qx \n", myVOPtr
->primaryVHB
, myVOPtr
->primaryVHB
);
1357 plog( "\talternate VHB is at block %qd 0x%02qx \n", myVOPtr
->alternateVHB
, myVOPtr
->alternateVHB
);
1358 plog( "\tsector size = %d 0x%02x \n", myVOPtr
->sectorSize
, myVOPtr
->sectorSize
);
1359 plog( "\tVolumeObject flags = 0x%02X \n", myVOPtr
->flags
);
1360 plog( "\ttotal sectors for volume = %qd 0x%02qx \n",
1361 myVOPtr
->totalDeviceSectors
, myVOPtr
->totalDeviceSectors
);
1362 plog( "\ttotal sectors for embedded volume = %qd 0x%02qx \n",
1363 myVOPtr
->totalEmbeddedSectors
, myVOPtr
->totalEmbeddedSectors
);
1367 } /* PrintVolumeObject */
1370 //******************************************************************************
1371 // Routine: GetEmbeddedVolumeHeaders
1373 // Function: Given a MDB (Master Directory Block) from an HFS volume, check
1374 // to see if there is an embedded HFS+ volume. If we find an
1375 // embedded HFS+ volume fill in relevant SGlob.VolumeObject data.
1377 // Result: returns nothing. Will fill in VolumeObject data
1378 //******************************************************************************
1380 static void GetEmbeddedVolumeHeaders( SGlobPtr GPtr
,
1381 HFSMasterDirectoryBlock
* theMDBPtr
,
1382 Boolean isPrimaryMDB
)
1385 HFSPlusVolumeHeader
* myVHPtr
;
1386 VolumeObjectPtr myVOPtr
;
1387 UInt64 myHFSPlusSectors
;
1388 UInt64 myPrimaryBlockNum
;
1389 UInt64 myAlternateBlockNum
;
1390 HFSPlusVolumeHeader myAltVolHeader
;
1391 BlockDescriptor myBlockDescriptor
;
1393 myBlockDescriptor
.buffer
= NULL
;
1394 myVOPtr
= GetVolumeObjectPtr( );
1396 // NOTE - If all of the embedded volume information is zero, then assume
1397 // this really is a plain HFS disk like it says. There could be ghost
1398 // volume headers left over when someone reinitializes a large HFS Plus
1399 // volume as HFS. The original embedded primary volume header and
1400 // alternate volume header are not zeroed out.
1401 if ( theMDBPtr
->drEmbedSigWord
== 0 &&
1402 theMDBPtr
->drEmbedExtent
.blockCount
== 0 &&
1403 theMDBPtr
->drEmbedExtent
.startBlock
== 0 ) {
1407 // number of sectors in our embedded HFS+ volume
1408 myHFSPlusSectors
= (theMDBPtr
->drAlBlkSiz
/ Blk_Size
) * theMDBPtr
->drEmbedExtent
.blockCount
;
1410 // offset of embedded HFS+ volume (in bytes) into HFS wrapper volume
1411 // NOTE - UInt32 is OK since we don't support HFS Wrappers on TB volumes
1412 myVOPtr
->embeddedOffset
=
1413 (theMDBPtr
->drEmbedExtent
.startBlock
* theMDBPtr
->drAlBlkSiz
) +
1414 (theMDBPtr
->drAlBlSt
* Blk_Size
);
1416 // Embedded alternate volume header is always 2nd to last sector
1417 myAlternateBlockNum
=
1418 theMDBPtr
->drAlBlSt
+
1419 ((theMDBPtr
->drAlBlkSiz
/ Blk_Size
) * theMDBPtr
->drEmbedExtent
.startBlock
) +
1420 myHFSPlusSectors
- 2;
1422 // Embedded primary volume header should always be block 2 (relative to 0)
1423 // into the embedded volume
1424 myPrimaryBlockNum
= (theMDBPtr
->drEmbedExtent
.startBlock
* theMDBPtr
->drAlBlkSiz
/ Blk_Size
) +
1425 theMDBPtr
->drAlBlSt
+ 2;
1427 // get the embedded alternate volume header
1428 err
= GetVolumeObjectBlock( myVOPtr
, myAlternateBlockNum
, &myBlockDescriptor
);
1429 if ( err
== noErr
) {
1430 myVHPtr
= (HFSPlusVolumeHeader
*) myBlockDescriptor
.buffer
;
1431 if ( myVHPtr
->signature
== kHFSPlusSigWord
) {
1433 myVOPtr
->alternateVHB
= myAlternateBlockNum
; // save location
1434 myVOPtr
->primaryVHB
= myPrimaryBlockNum
; // save location
1435 err
= ValidVolumeHeader( myVHPtr
);
1436 if ( err
== noErr
) {
1437 myVOPtr
->flags
|= kVO_AltVHBOK
;
1438 myVOPtr
->totalEmbeddedSectors
= myHFSPlusSectors
;
1439 bcopy( myVHPtr
, &myAltVolHeader
, sizeof( *myVHPtr
) );
1442 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
1443 plog( "\tInvalid embedded alternate volume header at block %qd - error %d \n", myAlternateBlockNum
, err
);
1448 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
1449 plog( "\tBlock number %qd is not embedded alternate volume header \n", myAlternateBlockNum
);
1452 (void) ReleaseVolumeBlock( GPtr
->calculatedVCB
, &myBlockDescriptor
, kReleaseBlock
);
1455 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
1456 plog( "\tcould not get embedded alternate volume header at %qd, err %d \n",
1457 myAlternateBlockNum
, err
);
1461 // get the embedded primary volume header
1462 err
= GetVolumeObjectBlock( myVOPtr
, myPrimaryBlockNum
, &myBlockDescriptor
);
1463 if ( err
== noErr
) {
1464 myVHPtr
= (HFSPlusVolumeHeader
*) myBlockDescriptor
.buffer
;
1465 if ( myVHPtr
->signature
== kHFSPlusSigWord
) {
1467 myVOPtr
->primaryVHB
= myPrimaryBlockNum
; // save location
1468 myVOPtr
->alternateVHB
= myAlternateBlockNum
; // save location
1469 err
= ValidVolumeHeader( myVHPtr
);
1470 if ( err
== noErr
) {
1471 myVOPtr
->flags
|= kVO_PriVHBOK
;
1472 myVOPtr
->totalEmbeddedSectors
= myHFSPlusSectors
;
1474 // check to see if the primary and alternates are in sync. 3137809
1475 CompareVolHeaderBTreeSizes( GPtr
, myVOPtr
, myVHPtr
, &myAltVolHeader
);
1478 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
1479 plog( "\tInvalid embedded primary volume header at block %qd - error %d \n", myPrimaryBlockNum
, err
);
1484 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
1485 plog( "\tBlock number %qd is not embedded primary volume header \n", myPrimaryBlockNum
);
1488 (void) ReleaseVolumeBlock( GPtr
->calculatedVCB
, &myBlockDescriptor
, kReleaseBlock
);
1491 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
1492 plog( "\tcould not get embedded primary volume header at %qd, err %d \n",
1493 myPrimaryBlockNum
, err
);
1500 } /* GetEmbeddedVolumeHeaders */
1503 //******************************************************************************
1504 // Routine: CompareVolHeaderBTreeSizes
1506 // Function: checks to see if the primary and alternate volume headers are in
1507 // sync with regards to the catalog and extents btree file size. If
1508 // we find an anomaly we will give preference to the volume header
1509 // with the larger of the btree files since these files never shrink.
1510 // Added for radar #3137809.
1512 // Result: returns nothing.
1513 //******************************************************************************
1514 static void CompareVolHeaderBTreeSizes( SGlobPtr GPtr
,
1515 VolumeObjectPtr theVOPtr
,
1516 HFSPlusVolumeHeader
* thePriVHPtr
,
1517 HFSPlusVolumeHeader
* theAltVHPtr
)
1523 weDisagree
= usePrimary
= useAlternate
= 0;
1525 // we only check if both volume headers appear to be OK
1526 if ( (theVOPtr
->flags
& kVO_PriVHBOK
) == 0 || (theVOPtr
->flags
& kVO_AltVHBOK
) == 0 )
1529 if ( thePriVHPtr
->catalogFile
.totalBlocks
!= theAltVHPtr
->catalogFile
.totalBlocks
) {
1530 // only continue if the B*Tree files both start at the same block number
1531 if ( thePriVHPtr
->catalogFile
.extents
[0].startBlock
== theAltVHPtr
->catalogFile
.extents
[0].startBlock
) {
1533 if ( thePriVHPtr
->catalogFile
.totalBlocks
> theAltVHPtr
->catalogFile
.totalBlocks
)
1537 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
1538 plog( "\tvolume headers disagree on catalog file total blocks - primary %d alternate %d \n",
1539 thePriVHPtr
->catalogFile
.totalBlocks
, theAltVHPtr
->catalogFile
.totalBlocks
);
1544 if ( thePriVHPtr
->extentsFile
.totalBlocks
!= theAltVHPtr
->extentsFile
.totalBlocks
) {
1545 // only continue if the B*Tree files both start at the same block number
1546 if ( thePriVHPtr
->extentsFile
.extents
[0].startBlock
== theAltVHPtr
->extentsFile
.extents
[0].startBlock
) {
1548 if ( thePriVHPtr
->extentsFile
.totalBlocks
> theAltVHPtr
->extentsFile
.totalBlocks
)
1552 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
1553 plog( "\tvolume headers disagree on extents file total blocks - primary %d alternate %d \n",
1554 thePriVHPtr
->extentsFile
.totalBlocks
, theAltVHPtr
->extentsFile
.totalBlocks
);
1559 if ( weDisagree
== 0 )
1562 // we have a disagreement. we resolve the issue by using the larger of the two.
1563 if ( usePrimary
== 1 && useAlternate
== 1 ) {
1564 // this should never happen, but if it does, bail without choosing a preference
1565 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
1566 plog( "\tvolume headers disagree but there is confusion on which to use \n" );
1571 if ( usePrimary
== 1 ) {
1572 // mark alternate as bogus
1573 theVOPtr
->flags
&= ~kVO_AltVHBOK
;
1575 else if ( useAlternate
== 1 ) {
1576 // mark primary as bogus
1577 theVOPtr
->flags
&= ~kVO_PriVHBOK
;
1582 } /* CompareVolHeaderBTreeSizes */
1586 * This code should be removed after debugging is completed.
1592 ({ __typeof(a) _a = (a); __typeof(b) _b = (b); \
1593 (_a < _b) ? _a : _b; })
1597 enum { WIDTH
= 16, };
1600 DumpData(const void *data
, size_t len
)
1602 unsigned char *base
= (unsigned char*)data
;
1603 unsigned char *end
= base
+ len
;
1604 unsigned char *cp
= base
;
1608 unsigned char *tend
= MIN(end
, cp
+ WIDTH
);
1611 size_t gap
= (cp
+ WIDTH
) - tend
;
1613 if (gap
!= 0 || tend
== end
)
1616 for (tmp
= cp
; tmp
< tend
; tmp
++) {
1622 if (allzeroes
== 1) {
1623 fprintf(stderr
, ". . .\n");
1633 fprintf(stderr
, "%04x: ", (int)(cp
- base
));
1634 for (i
= 0, tmp
= cp
; tmp
< tend
; tmp
++) {
1635 fprintf(stderr
, "%02x", *tmp
);
1637 fprintf(stderr
, " ");
1641 for (i
= gap
; i
>= 0; i
--) {
1642 fprintf(stderr
, " ");
1644 fprintf(stderr
, " ");
1646 fprintf(stderr
, " |");
1647 for (tmp
= cp
; tmp
< tend
; tmp
++) {
1648 fprintf(stderr
, "%c", isalnum(*tmp
) ? *tmp
: '.');
1650 for (i
= 0; i
< gap
; i
++) {
1651 fprintf(stderr
, " ");
1653 fprintf(stderr
, "|\n");
1660 //******************************************************************************
1661 // Routine: VolumeObjectIsValid
1663 // Function: determine if the volume represented by our VolumeObject is a
1664 // valid volume type (i.e. not unknown type)
1666 // Result: returns true if volume is known volume type (i.e. HFS, HFS+)
1668 //******************************************************************************
1669 Boolean
VolumeObjectIsValid(void)
1671 VolumeObjectPtr myVOPtr
= GetVolumeObjectPtr();
1672 Boolean retval
= false;
1674 /* Check if the type is unknown type */
1675 if (myVOPtr
->volumeType
== kUnknownVolumeType
) {
1676 pwarn("volumeType is %d\n", kUnknownVolumeType
);
1680 /* Check if it is HFS+ volume */
1681 if (VolumeObjectIsHFSPlus() == true) {
1686 /* Check if it is HFS volume */
1687 if (VolumeObjectIsHFS() == true) {
1694 * This code should be removed after debugging is done.
1696 if (retval
== false) {
1698 VolumeObjectPtr myVOPtr
;
1699 BlockDescriptor theBlockDesc
;
1702 myVOPtr
= GetVolumeObjectPtr();
1703 GetVolumeObjectBlockNum(&myBlockNum
);
1704 err
= GetVolumeBlock(myVOPtr
->vcbPtr
, myBlockNum
, kGetBlock
, &theBlockDesc
);
1706 fprintf(stderr
, "%s: Cannot GetVolumetBlock: %d\n", __FUNCTION__
, err
);
1708 uint8_t *ptr
= (uint8_t*)theBlockDesc
.buffer
;
1709 DumpData(ptr
, theBlockDesc
.blockSize
);
1713 } /* VolumeObjectIsValid */
1715 //******************************************************************************
1716 // Routine: VolumeObjectIsHFSPlus
1718 // Function: determine if the volume represented by our VolumeObject is an
1719 // HFS+ volume (pure or embedded).
1721 // Result: returns true if volume is pure HFS+ or embedded HFS+ else false.
1722 //******************************************************************************
1723 Boolean
VolumeObjectIsHFSPlus( void )
1725 VolumeObjectPtr myVOPtr
;
1727 myVOPtr
= GetVolumeObjectPtr( );
1729 if ( myVOPtr
->volumeType
== kEmbededHFSPlusVolumeType
||
1730 myVOPtr
->volumeType
== kPureHFSPlusVolumeType
) {
1736 } /* VolumeObjectIsHFSPlus */
1739 //******************************************************************************
1740 // Routine: VolumeObjectIsHFSX
1742 // Function: determine if the volume represented by our VolumeObject is an
1743 // HFSX volume (pure or embedded)
1745 // Result: returns true if volume is pure HFSX or embedded HFSX else false.
1746 //******************************************************************************
1748 Boolean
VolumeObjectIsHFSX(SGlobPtr GPtr
)
1752 HFSMasterDirectoryBlock
*mdbp
;
1753 SVCB
*vcb
= GPtr
->calculatedVCB
;
1754 BlockDescriptor block
;
1757 err
= GetVolumeBlock(vcb
, kIDSector
, kGetBlock
, &block
);
1758 if (err
) return (false);
1760 mdbp
= (HFSMasterDirectoryBlock
*)block
.buffer
;
1761 if (mdbp
->drSigWord
== kHFSXSigWord
) {
1763 } else if (mdbp
->drSigWord
== kHFSSigWord
) {
1764 if (mdbp
->drEmbedSigWord
== kHFSXSigWord
) {
1769 (void) ReleaseVolumeBlock(vcb
, &block
, kReleaseBlock
);
1772 } /* VolumeObjectIsHFSX */
1774 //******************************************************************************
1775 // Routine: VolumeObjectIsHFS
1777 // Function: determine if the volume represented by our VolumeObject is an
1778 // HFS (standard) volume.
1780 // Result: returns true if HFS (standard) volume.
1781 //******************************************************************************
1782 Boolean
VolumeObjectIsHFS( void )
1784 VolumeObjectPtr myVOPtr
;
1786 myVOPtr
= GetVolumeObjectPtr( );
1788 if ( myVOPtr
->volumeType
== kHFSVolumeType
)
1793 } /* VolumeObjectIsHFS */
1796 //******************************************************************************
1797 // Routine: VolumeObjectIsEmbeddedHFSPlus
1799 // Function: determine if the volume represented by our VolumeObject is an
1800 // embedded HFS plus volume.
1802 // Result: returns true if embedded HFS plus volume.
1803 //******************************************************************************
1804 Boolean
VolumeObjectIsEmbeddedHFSPlus( void )
1806 VolumeObjectPtr myVOPtr
;
1808 myVOPtr
= GetVolumeObjectPtr( );
1810 if ( myVOPtr
->volumeType
== kEmbededHFSPlusVolumeType
)
1815 } /* VolumeObjectIsEmbeddedHFSPlus */
1818 //******************************************************************************
1819 // Routine: VolumeObjectIsPureHFSPlus
1821 // Function: determine if the volume represented by our VolumeObject is an
1822 // pure HFS plus volume.
1824 // Result: returns true if pure HFS plus volume.
1825 //******************************************************************************
1826 Boolean
VolumeObjectIsPureHFSPlus( void )
1828 VolumeObjectPtr myVOPtr
;
1830 myVOPtr
= GetVolumeObjectPtr( );
1832 if ( myVOPtr
->volumeType
== kPureHFSPlusVolumeType
)
1837 } /* VolumeObjectIsPureHFSPlus */
1840 //******************************************************************************
1841 // Routine: GetVolumeObjectPtr
1843 // Function: Accessor routine to get a pointer to our VolumeObject structure.
1845 // Result: returns pointer to our VolumeObject.
1846 //******************************************************************************
1847 VolumeObjectPtr
GetVolumeObjectPtr( void )
1849 static VolumeObject myVolumeObject
;
1850 static int myInited
= 0;
1852 if ( myInited
== 0 ) {
1854 bzero( &myVolumeObject
, sizeof(myVolumeObject
) );
1857 return( &myVolumeObject
);
1859 } /* GetVolumeObjectPtr */
1862 //******************************************************************************
1863 // Routine: CheckEmbeddedVolInfoInMDBs
1865 // Function: Check the primary and alternate MDB to see if the embedded volume
1866 // information (drEmbedSigWord and drEmbedExtent) match.
1869 //******************************************************************************
1870 void CheckEmbeddedVolInfoInMDBs( SGlobPtr GPtr
)
1873 Boolean primaryIsDamaged
= false;
1874 Boolean alternateIsDamaged
= false;
1875 VolumeObjectPtr myVOPtr
;
1876 HFSMasterDirectoryBlock
* myPriMDBPtr
;
1877 HFSMasterDirectoryBlock
* myAltMDBPtr
;
1880 BlockDescriptor myPrimary
;
1881 BlockDescriptor myAlternate
;
1883 myVOPtr
= GetVolumeObjectPtr( );
1884 myPrimary
.buffer
= NULL
;
1885 myAlternate
.buffer
= NULL
;
1887 // we only check this if primary and alternate are OK at this point. OK means
1888 // that the primary and alternate MDBs have the correct signature and at least
1889 // one of them points to a valid embedded HFS+ volume.
1890 if ( VolumeObjectIsEmbeddedHFSPlus( ) == false ||
1891 (myVOPtr
->flags
& kVO_PriMDBOK
) == 0 || (myVOPtr
->flags
& kVO_AltMDBOK
) == 0 )
1894 err
= GetVolumeObjectPrimaryMDB( &myPrimary
);
1895 if ( err
!= noErr
) {
1896 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
1897 plog( "\tcould not get primary MDB \n" );
1899 goto ExitThisRoutine
;
1901 myPriMDBPtr
= (HFSMasterDirectoryBlock
*) myPrimary
.buffer
;
1902 err
= GetVolumeObjectAlternateMDB( &myAlternate
);
1903 if ( err
!= noErr
) {
1904 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
1905 plog( "\tcould not get alternate MDB \n" );
1907 goto ExitThisRoutine
;
1909 myAltMDBPtr
= (HFSMasterDirectoryBlock
*) myAlternate
.buffer
;
1911 // bail if everything looks good. NOTE - we can bail if drEmbedExtent info
1912 // is the same in the primary and alternate MDB because we know one of them is
1913 // valid (or VolumeObjectIsEmbeddedHFSPlus would be false and we would not be
1915 if ( myPriMDBPtr
->drEmbedSigWord
== kHFSPlusSigWord
&&
1916 myAltMDBPtr
->drEmbedSigWord
== kHFSPlusSigWord
&&
1917 myPriMDBPtr
->drEmbedExtent
.blockCount
== myAltMDBPtr
->drEmbedExtent
.blockCount
&&
1918 myPriMDBPtr
->drEmbedExtent
.startBlock
== myAltMDBPtr
->drEmbedExtent
.startBlock
)
1919 goto ExitThisRoutine
;
1921 // we know that VolumeObject.embeddedOffset and VolumeObject.totalEmbeddedSectors
1922 // are correct so we will verify the info in each MDB calculates to these values.
1923 myOffset
= (myPriMDBPtr
->drEmbedExtent
.startBlock
* myPriMDBPtr
->drAlBlkSiz
) +
1924 (myPriMDBPtr
->drAlBlSt
* Blk_Size
);
1925 mySectors
= (myPriMDBPtr
->drAlBlkSiz
/ Blk_Size
) * myPriMDBPtr
->drEmbedExtent
.blockCount
;
1927 if ( myOffset
!= myVOPtr
->embeddedOffset
|| mySectors
!= myVOPtr
->totalEmbeddedSectors
)
1928 primaryIsDamaged
= true;
1930 myOffset
= (myAltMDBPtr
->drEmbedExtent
.startBlock
* myAltMDBPtr
->drAlBlkSiz
) +
1931 (myAltMDBPtr
->drAlBlSt
* Blk_Size
);
1932 mySectors
= (myAltMDBPtr
->drAlBlkSiz
/ Blk_Size
) * myAltMDBPtr
->drEmbedExtent
.blockCount
;
1934 if ( myOffset
!= myVOPtr
->embeddedOffset
|| mySectors
!= myVOPtr
->totalEmbeddedSectors
)
1935 alternateIsDamaged
= true;
1937 // now check drEmbedSigWord if everything else is OK
1938 if ( primaryIsDamaged
== false && alternateIsDamaged
== false ) {
1939 if ( myPriMDBPtr
->drEmbedSigWord
!= kHFSPlusSigWord
)
1940 primaryIsDamaged
= true;
1941 else if ( myAltMDBPtr
->drEmbedSigWord
!= kHFSPlusSigWord
)
1942 alternateIsDamaged
= true;
1945 if ( primaryIsDamaged
|| alternateIsDamaged
) {
1946 GPtr
->VIStat
|= S_WMDB
;
1947 WriteError( GPtr
, E_MDBDamaged
, 7, 0 );
1948 if ( primaryIsDamaged
) {
1949 myVOPtr
->flags
&= ~kVO_PriMDBOK
; // mark the primary MDB as damaged
1950 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1951 plog("\tinvalid primary wrapper MDB \n");
1954 myVOPtr
->flags
&= ~kVO_AltMDBOK
; // mark the alternate MDB as damaged
1955 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1956 plog("\tinvalid alternate wrapper MDB \n");
1961 if ( myPrimary
.buffer
!= NULL
)
1962 (void) ReleaseVolumeBlock( myVOPtr
->vcbPtr
, &myPrimary
, kReleaseBlock
);
1963 if ( myAlternate
.buffer
!= NULL
)
1964 (void) ReleaseVolumeBlock( myVOPtr
->vcbPtr
, &myAlternate
, kReleaseBlock
);
1968 } /* CheckEmbeddedVolInfoInMDBs */
1971 //******************************************************************************
1972 // Routine: ValidVolumeHeader
1974 // Function: Run some sanity checks to make sure the HFSPlusVolumeHeader is valid
1977 //******************************************************************************
1978 OSErr
ValidVolumeHeader( HFSPlusVolumeHeader
*volumeHeader
)
1982 if ((volumeHeader
->signature
== kHFSPlusSigWord
&& volumeHeader
->version
== kHFSPlusVersion
) ||
1983 (volumeHeader
->signature
== kHFSXSigWord
&& volumeHeader
->version
== kHFSXVersion
))
1985 if ( (volumeHeader
->blockSize
!= 0) && ((volumeHeader
->blockSize
& 0x01FF) == 0) ) // non zero multiple of 512
1988 err
= badMDBErr
; //¥¥ I want badVolumeHeaderErr in Errors.i
1999 //_______________________________________________________________________
2003 // This routine initializes a B-Tree header.
2005 // Note: Since large volumes will have bigger b-trees they need to
2006 // have map nodes setup.
2007 //_______________________________________________________________________
2009 void InitBTreeHeader (UInt32 fileSize
, UInt32 clumpSize
, UInt16 nodeSize
, UInt16 recordCount
, UInt16 keySize
,
2010 UInt32 attributes
, UInt32
*mapNodes
, void *buffer
)
2014 UInt32 nodeBitsInHeader
;
2016 BTNodeDescriptor
*ndp
;
2021 ClearMemory(buffer
, nodeSize
); // start out with clean node
2023 nodeCount
= fileSize
/ nodeSize
;
2024 nodeBitsInHeader
= 8 * (nodeSize
- sizeof(BTNodeDescriptor
) - sizeof(BTHeaderRec
) - kBTreeHeaderUserBytes
- 4*sizeof(SInt16
));
2026 usedNodes
= 1; // header takes up one node
2027 *mapNodes
= 0; // number of map nodes initially (0)
2030 // FILL IN THE NODE DESCRIPTOR:
2031 ndp
= (BTNodeDescriptor
*) buffer
; // point to node descriptor
2033 ndp
->kind
= kBTHeaderNode
; // this node contains the B-tree header
2034 ndp
->numRecords
= 3; // there are 3 records (header, map, and user)
2036 if (nodeCount
> nodeBitsInHeader
) // do we need additional map nodes?
2038 UInt32 nodeBitsInMapNode
;
2040 nodeBitsInMapNode
= 8 * (nodeSize
- sizeof(BTNodeDescriptor
) - 2*sizeof(SInt16
) - 2); //¥¥ why (-2) at end???
2042 if (recordCount
> 0) // catalog B-tree?
2043 ndp
->fLink
= 2; // link points to initial map node
2044 //¥¥ Assumes all records will fit in one node. It would be better
2045 //¥¥ to put the map node(s) first, then the records.
2047 ndp
->fLink
= 1; // link points to initial map node
2049 *mapNodes
= (nodeCount
- nodeBitsInHeader
+ (nodeBitsInMapNode
- 1)) / nodeBitsInMapNode
;
2050 usedNodes
+= *mapNodes
;
2053 // FILL IN THE HEADER RECORD:
2054 bth
= (BTHeaderRec
*) ((char*)buffer
+ sizeof(BTNodeDescriptor
)); // point to header
2056 if (recordCount
> 0)
2058 ++usedNodes
; // one more node will be used
2060 bth
->treeDepth
= 1; // tree depth is one level (leaf)
2061 bth
->rootNode
= 1; // root node is also leaf
2062 bth
->firstLeafNode
= 1; // first leaf node
2063 bth
->lastLeafNode
= 1; // last leaf node
2066 bth
->attributes
= attributes
; // flags for 16-bit key lengths, and variable sized index keys
2067 bth
->leafRecords
= recordCount
; // total number of data records
2068 bth
->nodeSize
= nodeSize
; // size of a node
2069 bth
->maxKeyLength
= keySize
; // maximum length of a key
2070 bth
->totalNodes
= nodeCount
; // total number of nodes
2071 bth
->freeNodes
= nodeCount
- usedNodes
; // number of free nodes
2072 bth
->clumpSize
= clumpSize
; //
2073 // bth->btreeType = 0; // 0 = meta data B-tree
2076 // FILL IN THE MAP RECORD:
2077 bitMapPtr
= (UInt32
*) ((Byte
*) buffer
+ sizeof(BTNodeDescriptor
) + sizeof(BTHeaderRec
) + kBTreeHeaderUserBytes
); // point to bitmap
2079 // MARK NODES THAT ARE IN USE:
2080 // Note - worst case (32MB alloc blk) will have only 18 nodes in use.
2081 *bitMapPtr
= ~((UInt32
) 0xFFFFFFFF >> usedNodes
);
2084 // PLACE RECORD OFFSETS AT THE END OF THE NODE:
2085 offsetPtr
= (SInt16
*) ((Byte
*) buffer
+ nodeSize
- 4*sizeof(SInt16
));
2087 *offsetPtr
++ = sizeof(BTNodeDescriptor
) + sizeof(BTHeaderRec
) + kBTreeHeaderUserBytes
+ nodeBitsInHeader
/8; // offset to free space
2088 *offsetPtr
++ = sizeof(BTNodeDescriptor
) + sizeof(BTHeaderRec
) + kBTreeHeaderUserBytes
; // offset to allocation map
2089 *offsetPtr
++ = sizeof(BTNodeDescriptor
) + sizeof(BTHeaderRec
); // offset to user space
2090 *offsetPtr
= sizeof(BTNodeDescriptor
); // offset to BTH
2093 /*------------------------------------------------------------------------------
2095 Routine: CalculateItemCount
2097 Function: determines number of items for progress feedback
2099 Input: vRefNum: the volume to count items
2101 Output: number of items
2103 ------------------------------------------------------------------------------*/
2105 void CalculateItemCount( SGlob
*GPtr
, UInt64
*itemCount
, UInt64
*onePercent
)
2107 BTreeControlBlock
*btcb
;
2108 VolumeObjectPtr myVOPtr
;
2110 UInt32 realFreeNodes
;
2111 SVCB
*vcb
= GPtr
->calculatedVCB
;
2113 /* each bitmap segment is an item */
2114 myVOPtr
= GetVolumeObjectPtr( );
2115 items
= GPtr
->calculatedVCB
->vcbTotalBlocks
/ 1024;
2118 // Items is the used node count and leaf record count for each btree...
2121 btcb
= (BTreeControlBlock
*) vcb
->vcbCatalogFile
->fcbBtree
;
2122 realFreeNodes
= ((BTreeExtensionsRec
*)btcb
->refCon
)->realFreeNodeCount
;
2123 items
+= (2 * btcb
->leafRecords
) + (btcb
->totalNodes
- realFreeNodes
);
2125 btcb
= (BTreeControlBlock
*) vcb
->vcbExtentsFile
->fcbBtree
;
2126 realFreeNodes
= ((BTreeExtensionsRec
*)btcb
->refCon
)->realFreeNodeCount
;
2127 items
+= btcb
->leafRecords
+ (btcb
->totalNodes
- realFreeNodes
);
2129 if ( vcb
->vcbAttributesFile
!= NULL
)
2131 btcb
= (BTreeControlBlock
*) vcb
->vcbAttributesFile
->fcbBtree
;
2132 realFreeNodes
= ((BTreeExtensionsRec
*)btcb
->refCon
)->realFreeNodeCount
;
2134 items
+= (btcb
->leafRecords
+ (btcb
->totalNodes
- realFreeNodes
));
2137 *onePercent
= items
/ 100;
2140 // [2239291] We're calculating the progress for the wrapper and the embedded volume separately, which
2141 // confuses the caller (since they see the progress jump to a large percentage while checking the wrapper,
2142 // then jump to a small percentage when starting to check the embedded volume). To avoid this behavior,
2143 // we pretend the wrapper has 100 times as many items as it really does. This means the progress will
2144 // never exceed 1% for the wrapper.
2146 /* fsck_hfs doesn't deal wih the wrapper at this time (8.29.2002)
2147 if ( (myVOPtr->volumeType == kEmbededHFSPlusVolumeType) && (GPtr->inputFlags & examineWrapperMask) )
2150 // Add en extra Å 5% to smooth the progress
2151 items
+= *onePercent
* 5;
2157 SFCB
* ResolveFCB(short fileRefNum
)
2159 return( (SFCB
*)((unsigned long)GetFCBSPtr() + (unsigned long)fileRefNum
) );
2163 //******************************************************************************
2164 // Routine: SetupFCB fills in the FCB info
2166 // Returns: The filled up FCB
2167 //******************************************************************************
2168 void SetupFCB( SVCB
*vcb
, SInt16 refNum
, UInt32 fileID
, UInt32 fileClumpSize
)
2172 fcb
= ResolveFCB(refNum
);
2174 fcb
->fcbFileID
= fileID
;
2175 fcb
->fcbVolume
= vcb
;
2176 fcb
->fcbClumpSize
= fileClumpSize
;
2180 //******************************************************************************
2182 // Routine: ResolveFileRefNum
2184 // Purpose: Return a file reference number for a given file control block
2188 // fileCtrlBlockPtr Pointer to the SFCB
2191 // result File reference number,
2192 // or 0 if fileCtrlBlockPtr is invalid
2194 pascal short ResolveFileRefNum(SFCB
* fileCtrlBlockPtr
)
2196 return( (unsigned long)fileCtrlBlockPtr
- (unsigned long)GetFCBSPtr() );
2203 void SetFCBSPtr( Ptr value
)
2208 Ptr
GetFCBSPtr( void )
2214 //_______________________________________________________________________
2216 // Routine: FlushVolumeControlBlock
2217 // Arguments: SVCB *vcb
2218 // Output: OSErr err
2220 // Function: Flush volume information to either the HFSPlusVolumeHeader
2221 // of the Master Directory Block
2222 //_______________________________________________________________________
2224 OSErr
FlushVolumeControlBlock( SVCB
*vcb
)
2227 HFSPlusVolumeHeader
*volumeHeader
;
2229 BlockDescriptor block
;
2231 if ( ! IsVCBDirty( vcb
) ) // if it's not dirty
2234 block
.buffer
= NULL
;
2235 err
= GetVolumeObjectPrimaryBlock( &block
);
2238 // attempt to fix the primary with alternate
2239 if ( block
.buffer
!= NULL
) {
2240 (void) ReleaseVolumeBlock( vcb
, &block
, kReleaseBlock
);
2241 block
.buffer
= NULL
;
2244 err
= VolumeObjectFixPrimaryBlock( );
2245 ReturnIfError( err
);
2247 // should be able to get it now
2248 err
= GetVolumeObjectPrimaryBlock( &block
);
2249 ReturnIfError( err
);
2252 if ( vcb
->vcbSignature
== kHFSPlusSigWord
)
2254 volumeHeader
= (HFSPlusVolumeHeader
*) block
.buffer
;
2256 // 2005507, Keep the MDB creation date and HFSPlusVolumeHeader creation date in sync.
2257 if ( vcb
->vcbEmbeddedOffset
!= 0 ) // It's a wrapped HFS+ volume
2259 HFSMasterDirectoryBlock
*mdb
;
2260 BlockDescriptor mdb_block
;
2262 mdb_block
.buffer
= NULL
;
2263 err
= GetVolumeObjectPrimaryMDB( &mdb_block
);
2266 mdb
= (HFSMasterDirectoryBlock
*) mdb_block
.buffer
;
2267 if ( mdb
->drCrDate
!= vcb
->vcbCreateDate
) // The creation date changed
2269 mdb
->drCrDate
= vcb
->vcbCreateDate
;
2270 (void) ReleaseVolumeBlock(vcb
, &mdb_block
, kForceWriteBlock
);
2271 mdb_block
.buffer
= NULL
;
2274 if ( mdb_block
.buffer
!= NULL
)
2275 (void) ReleaseVolumeBlock(vcb
, &mdb_block
, kReleaseBlock
);
2278 volumeHeader
->attributes
= vcb
->vcbAttributes
;
2279 volumeHeader
->lastMountedVersion
= kFSCKMountVersion
;
2280 volumeHeader
->createDate
= vcb
->vcbCreateDate
; // NOTE: local time, not GMT!
2281 volumeHeader
->modifyDate
= vcb
->vcbModifyDate
;
2282 volumeHeader
->backupDate
= vcb
->vcbBackupDate
;
2283 volumeHeader
->checkedDate
= vcb
->vcbCheckedDate
;
2284 volumeHeader
->fileCount
= vcb
->vcbFileCount
;
2285 volumeHeader
->folderCount
= vcb
->vcbFolderCount
;
2286 volumeHeader
->blockSize
= vcb
->vcbBlockSize
;
2287 volumeHeader
->totalBlocks
= vcb
->vcbTotalBlocks
;
2288 volumeHeader
->freeBlocks
= vcb
->vcbFreeBlocks
;
2289 volumeHeader
->nextAllocation
= vcb
->vcbNextAllocation
;
2290 volumeHeader
->rsrcClumpSize
= vcb
->vcbRsrcClumpSize
;
2291 volumeHeader
->dataClumpSize
= vcb
->vcbDataClumpSize
;
2292 volumeHeader
->nextCatalogID
= vcb
->vcbNextCatalogID
;
2293 volumeHeader
->writeCount
= vcb
->vcbWriteCount
;
2294 volumeHeader
->encodingsBitmap
= vcb
->vcbEncodingsBitmap
;
2296 //¥¥Êshould we use the vcb or fcb clumpSize values ????? -djb
2297 volumeHeader
->allocationFile
.clumpSize
= vcb
->vcbAllocationFile
->fcbClumpSize
;
2298 volumeHeader
->extentsFile
.clumpSize
= vcb
->vcbExtentsFile
->fcbClumpSize
;
2299 volumeHeader
->catalogFile
.clumpSize
= vcb
->vcbCatalogFile
->fcbClumpSize
;
2301 CopyMemory( vcb
->vcbFinderInfo
, volumeHeader
->finderInfo
, sizeof(volumeHeader
->finderInfo
) );
2303 fcb
= vcb
->vcbExtentsFile
;
2304 CopyMemory( fcb
->fcbExtents32
, volumeHeader
->extentsFile
.extents
, sizeof(HFSPlusExtentRecord
) );
2305 volumeHeader
->extentsFile
.logicalSize
= fcb
->fcbLogicalSize
;
2306 volumeHeader
->extentsFile
.totalBlocks
= fcb
->fcbPhysicalSize
/ vcb
->vcbBlockSize
;
2308 fcb
= vcb
->vcbCatalogFile
;
2309 CopyMemory( fcb
->fcbExtents32
, volumeHeader
->catalogFile
.extents
, sizeof(HFSPlusExtentRecord
) );
2310 volumeHeader
->catalogFile
.logicalSize
= fcb
->fcbLogicalSize
;
2311 volumeHeader
->catalogFile
.totalBlocks
= fcb
->fcbPhysicalSize
/ vcb
->vcbBlockSize
;
2313 fcb
= vcb
->vcbAllocationFile
;
2314 CopyMemory( fcb
->fcbExtents32
, volumeHeader
->allocationFile
.extents
, sizeof(HFSPlusExtentRecord
) );
2315 volumeHeader
->allocationFile
.logicalSize
= fcb
->fcbLogicalSize
;
2316 volumeHeader
->allocationFile
.totalBlocks
= fcb
->fcbPhysicalSize
/ vcb
->vcbBlockSize
;
2318 if (vcb
->vcbAttributesFile
!= NULL
) // Only update fields if an attributes file existed and was open
2320 fcb
= vcb
->vcbAttributesFile
;
2321 CopyMemory( fcb
->fcbExtents32
, volumeHeader
->attributesFile
.extents
, sizeof(HFSPlusExtentRecord
) );
2322 volumeHeader
->attributesFile
.logicalSize
= fcb
->fcbLogicalSize
;
2323 volumeHeader
->attributesFile
.clumpSize
= fcb
->fcbClumpSize
;
2324 volumeHeader
->attributesFile
.totalBlocks
= fcb
->fcbPhysicalSize
/ vcb
->vcbBlockSize
;
2329 HFSMasterDirectoryBlock
*mdbP
;
2331 mdbP
= (HFSMasterDirectoryBlock
*) block
.buffer
;
2333 mdbP
->drCrDate
= vcb
->vcbCreateDate
;
2334 mdbP
->drLsMod
= vcb
->vcbModifyDate
;
2335 mdbP
->drAtrb
= (UInt16
)vcb
->vcbAttributes
;
2336 mdbP
->drClpSiz
= vcb
->vcbDataClumpSize
;
2337 mdbP
->drNxtCNID
= vcb
->vcbNextCatalogID
;
2338 mdbP
->drFreeBks
= vcb
->vcbFreeBlocks
;
2339 mdbP
->drXTClpSiz
= vcb
->vcbExtentsFile
->fcbClumpSize
;
2340 mdbP
->drCTClpSiz
= vcb
->vcbCatalogFile
->fcbClumpSize
;
2342 mdbP
->drNmFls
= vcb
->vcbNmFls
;
2343 mdbP
->drNmRtDirs
= vcb
->vcbNmRtDirs
;
2344 mdbP
->drFilCnt
= vcb
->vcbFileCount
;
2345 mdbP
->drDirCnt
= vcb
->vcbFolderCount
;
2347 fcb
= vcb
->vcbExtentsFile
;
2348 CopyMemory( fcb
->fcbExtents16
, mdbP
->drXTExtRec
, sizeof( mdbP
->drXTExtRec
) );
2350 fcb
= vcb
->vcbCatalogFile
;
2351 CopyMemory( fcb
->fcbExtents16
, mdbP
->drCTExtRec
, sizeof( mdbP
->drCTExtRec
) );
2354 //-- Write the VHB/MDB out by releasing the block dirty
2355 if ( block
.buffer
!= NULL
) {
2356 err
= ReleaseVolumeBlock(vcb
, &block
, kForceWriteBlock
);
2357 block
.buffer
= NULL
;
2359 MarkVCBClean( vcb
);
2365 //_______________________________________________________________________
2367 // Routine: FlushAlternateVolumeControlBlock
2368 // Arguments: SVCB *vcb
2369 // Boolean ifHFSPlus
2370 // Output: OSErr err
2372 // Function: Flush volume information to either the Alternate HFSPlusVolumeHeader or the
2373 // Alternate Master Directory Block. Called by the BTree when the catalog
2374 // or extent files grow. Simply BlockMoves the original to the alternate
2376 //_______________________________________________________________________
2378 OSErr
FlushAlternateVolumeControlBlock( SVCB
*vcb
, Boolean isHFSPlus
)
2381 VolumeObjectPtr myVOPtr
;
2383 BlockDescriptor pri_block
, alt_block
;
2385 pri_block
.buffer
= NULL
;
2386 alt_block
.buffer
= NULL
;
2387 myVOPtr
= GetVolumeObjectPtr( );
2389 err
= FlushVolumeControlBlock( vcb
);
2390 err
= GetVolumeObjectPrimaryBlock( &pri_block
);
2392 // invalidate if we have not marked the primary as OK
2393 if ( VolumeObjectIsHFS( ) ) {
2394 if ( (myVOPtr
->flags
& kVO_PriMDBOK
) == 0 )
2397 else if ( (myVOPtr
->flags
& kVO_PriVHBOK
) == 0 ) {
2401 goto ExitThisRoutine
;
2403 GetVolumeObjectAlternateBlockNum( &myBlockNum
);
2404 if ( myBlockNum
!= 0 ) {
2405 // we don't care if this is an invalid MDB / VHB since we will write over it
2406 err
= GetVolumeObjectAlternateBlock( &alt_block
);
2407 if ( err
== noErr
|| err
== badMDBErr
|| err
== noMacDskErr
) {
2408 CopyMemory( pri_block
.buffer
, alt_block
.buffer
, Blk_Size
);
2409 (void) ReleaseVolumeBlock(vcb
, &alt_block
, kForceWriteBlock
);
2410 alt_block
.buffer
= NULL
;
2415 if ( pri_block
.buffer
!= NULL
)
2416 (void) ReleaseVolumeBlock( vcb
, &pri_block
, kReleaseBlock
);
2417 if ( alt_block
.buffer
!= NULL
)
2418 (void) ReleaseVolumeBlock( vcb
, &alt_block
, kReleaseBlock
);
2424 ConvertToHFSPlusExtent( const HFSExtentRecord oldExtents
, HFSPlusExtentRecord newExtents
)
2428 // go backwards so we can convert in place!
2430 for (i
= kHFSPlusExtentDensity
-1; i
> 2; --i
)
2432 newExtents
[i
].blockCount
= 0;
2433 newExtents
[i
].startBlock
= 0;
2436 newExtents
[2].blockCount
= oldExtents
[2].blockCount
;
2437 newExtents
[2].startBlock
= oldExtents
[2].startBlock
;
2438 newExtents
[1].blockCount
= oldExtents
[1].blockCount
;
2439 newExtents
[1].startBlock
= oldExtents
[1].startBlock
;
2440 newExtents
[0].blockCount
= oldExtents
[0].blockCount
;
2441 newExtents
[0].startBlock
= oldExtents
[0].startBlock
;
2446 OSErr
CacheWriteInPlace( SVCB
*vcb
, UInt32 fileRefNum
, HIOParam
*iopb
, UInt64 currentPosition
, UInt32 maximumBytes
, UInt32
*actualBytes
)
2450 UInt32 contiguousBytes
;
2454 buffer
= (char*)iopb
->ioBuffer
+ iopb
->ioActCount
;
2456 err
= MapFileBlockC(vcb
, ResolveFCB(fileRefNum
), maximumBytes
, (currentPosition
>> kSectorShift
),
2457 &diskBlock
, &contiguousBytes
);
2461 err
= DeviceWrite(vcb
->vcbDriverWriteRef
, vcb
->vcbDriveNumber
, buffer
, (diskBlock
<< Log2BlkLo
), contiguousBytes
, actualBytes
);
2467 void PrintName( int theCount
, const UInt8
*theNamePtr
, Boolean isUnicodeString
)
2472 myCount
= (isUnicodeString
) ? (theCount
* 2) : theCount
;
2473 for ( i
= 0; i
< myCount
; i
++ )
2474 plog( "%02X ", *(theNamePtr
+ i
) );
2479 /* Function: add_prime_bucket_uint32
2482 * This function increments the prime number buckets in the prime bucket
2483 * set based on the uint32_t number provided. This function increments
2484 * each prime number bucket by one at an offset of the corresponding
2485 * remainder of the division. This function is based on Chinese Remainder
2486 * Theorem and adds the given number to the set to compare later.
2489 * 1. Corresponding prime bucket to increment.
2490 * 2. uint32_t number to add to the set.
2494 void add_prime_bucket_uint32(PrimeBuckets
*cur
, uint32_t num
)
2496 int r32
, r27
, r25
, r7
, r11
, r13
, r17
, r19
, r23
, r29
, r31
;
2502 /* Perform the necessary divisions here */
2515 /* Update bucket for attribute bit */
2531 /* Function: add_prime_bucket_uint64
2534 * This function increments the prime number buckets in the prime bucket
2535 * set based on the uint64_t number provided. This function increments
2536 * each prime number bucket by one at an offset of the corresponding
2537 * remainder of the division. This function is based on Chinese Remainder
2538 * Theorem and adds the given number to the set to compare later.
2541 * 1. Corresponding prime bucket to increment.
2542 * 2. uint64_t number to add to the set.
2546 void add_prime_bucket_uint64(PrimeBuckets
*cur
, uint64_t num
)
2548 size_t r32
, r27
, r25
, r7
, r11
, r13
, r17
, r19
, r23
, r29
, r31
;
2554 /* Perform the necessary divisions here */
2567 /* Update bucket for attribute bit */
2583 /* Compares the two prime buckets provided.
2585 * zero - If the two buckets are same.
2586 * non-zero - If the two buckets do not match.
2588 int compare_prime_buckets(PrimeBuckets
*bucket1
, PrimeBuckets
*bucket2
)
2593 for (i
=0; i
<32; i
++) {
2594 if (bucket1
->n32
[i
] != bucket2
->n32
[i
]) {
2599 for (i
=0; i
<27; i
++) {
2600 if (bucket1
->n27
[i
] != bucket2
->n27
[i
]) {
2605 for (i
=0; i
<25; i
++) {
2606 if (bucket1
->n25
[i
] != bucket2
->n25
[i
]) {
2611 for (i
=0; i
<7; i
++) {
2612 if (bucket1
->n7
[i
] != bucket2
->n7
[i
]) {
2617 for (i
=0; i
<11; i
++) {
2618 if (bucket1
->n11
[i
] != bucket2
->n11
[i
]) {
2623 for (i
=0; i
<13; i
++) {
2624 if (bucket1
->n13
[i
] != bucket2
->n13
[i
]) {
2629 for (i
=0; i
<17; i
++) {
2630 if (bucket1
->n17
[i
] != bucket2
->n17
[i
]) {
2635 for (i
=0; i
<19; i
++) {
2636 if (bucket1
->n19
[i
] != bucket2
->n19
[i
]) {
2641 for (i
=0; i
<23; i
++) {
2642 if (bucket1
->n23
[i
] != bucket2
->n23
[i
]) {
2647 for (i
=0; i
<29; i
++) {
2648 if (bucket1
->n29
[i
] != bucket2
->n29
[i
]) {
2653 for (i
=0; i
<31; i
++) {
2654 if (bucket1
->n31
[i
] != bucket2
->n31
[i
]) {
2665 /* Prints the prime number bucket for the passed pointer */
2666 void print_prime_buckets(PrimeBuckets
*cur
)
2671 for (i
=0; i
<32; i
++) {
2672 plog ("%d,", cur
->n32
[i
]);
2677 for (i
=0; i
<27; i
++) {
2678 plog ("%d,", cur
->n27
[i
]);
2683 for (i
=0; i
<25; i
++) {
2684 plog ("%d,", cur
->n25
[i
]);
2689 for (i
=0; i
<7; i
++) {
2690 plog ("%d,", cur
->n7
[i
]);
2695 for (i
=0; i
<11; i
++) {
2696 plog ("%d,", cur
->n11
[i
]);
2701 for (i
=0; i
<13; i
++) {
2702 plog ("%d,", cur
->n13
[i
]);
2707 for (i
=0; i
<17; i
++) {
2708 plog ("%d,", cur
->n17
[i
]);
2713 for (i
=0; i
<19; i
++) {
2714 plog ("%d,", cur
->n19
[i
]);
2719 for (i
=0; i
<23; i
++) {
2720 plog ("%d,", cur
->n23
[i
]);
2725 for (i
=0; i
<29; i
++) {
2726 plog ("%d,", cur
->n29
[i
]);
2731 for (i
=0; i
<31; i
++) {
2732 plog ("%d,", cur
->n31
[i
]);