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
, char *label
)
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");
1632 if (label
== NULL
) {
1633 fprintf(stderr
, "%04x: ", (int)(cp
- base
));
1636 fprintf(stderr
, "%s %04x: ", label
, (int)(cp
- base
));
1638 for (i
= 0, tmp
= cp
; tmp
< tend
; tmp
++) {
1639 fprintf(stderr
, "%02x", *tmp
);
1641 fprintf(stderr
, " ");
1645 for (i
= (int)gap
; i
>= 0; i
--) {
1646 fprintf(stderr
, " ");
1648 fprintf(stderr
, " ");
1650 fprintf(stderr
, " |");
1651 for (tmp
= cp
; tmp
< tend
; tmp
++) {
1652 fprintf(stderr
, "%c", isalnum(*tmp
) ? *tmp
: '.');
1654 for (i
= 0; i
< gap
; i
++) {
1655 fprintf(stderr
, " ");
1657 fprintf(stderr
, "|\n");
1664 //******************************************************************************
1665 // Routine: VolumeObjectIsValid
1667 // Function: determine if the volume represented by our VolumeObject is a
1668 // valid volume type (i.e. not unknown type)
1670 // Result: returns true if volume is known volume type (i.e. HFS, HFS+)
1672 //******************************************************************************
1673 Boolean
VolumeObjectIsValid(SGlobPtr gptr
)
1675 VolumeObjectPtr myVOPtr
= GetVolumeObjectPtr();
1676 Boolean retval
= false;
1678 /* Check if the type is unknown type */
1679 if (myVOPtr
->volumeType
== kUnknownVolumeType
) {
1680 pwarn("volumeType is %d\n", kUnknownVolumeType
);
1684 /* Check if it is HFS+ volume */
1685 if (VolumeObjectIsHFSPlus() == true) {
1690 /* Check if it is HFS volume */
1691 if (VolumeObjectIsHFS() == true) {
1698 * This code should be removed after debugging is done.
1700 if (retval
== false) {
1702 VolumeObjectPtr myVOPtr
;
1703 BlockDescriptor theBlockDesc
;
1706 myVOPtr
= GetVolumeObjectPtr();
1707 GetVolumeObjectBlockNum(&myBlockNum
);
1708 err
= GetVolumeBlock(myVOPtr
->vcbPtr
, myBlockNum
, kGetBlock
, &theBlockDesc
);
1710 fprintf(stderr
, "%s: Cannot GetVolumeBlock: %d\n", __FUNCTION__
, err
);
1712 uint8_t *ptr
= (uint8_t*)theBlockDesc
.buffer
;
1713 DumpData(ptr
, theBlockDesc
.blockSize
, gptr
->deviceNode
);
1714 ReleaseVolumeBlock(myVOPtr
->vcbPtr
, &theBlockDesc
, kReleaseBlock
);
1718 } /* VolumeObjectIsValid */
1720 //******************************************************************************
1721 // Routine: VolumeObjectIsHFSPlus
1723 // Function: determine if the volume represented by our VolumeObject is an
1724 // HFS+ volume (pure or embedded).
1726 // Result: returns true if volume is pure HFS+ or embedded HFS+ else false.
1727 //******************************************************************************
1728 Boolean
VolumeObjectIsHFSPlus( void )
1730 VolumeObjectPtr myVOPtr
;
1732 myVOPtr
= GetVolumeObjectPtr( );
1734 if ( myVOPtr
->volumeType
== kEmbededHFSPlusVolumeType
||
1735 myVOPtr
->volumeType
== kPureHFSPlusVolumeType
) {
1741 } /* VolumeObjectIsHFSPlus */
1744 //******************************************************************************
1745 // Routine: VolumeObjectIsHFSX
1747 // Function: determine if the volume represented by our VolumeObject is an
1748 // HFSX volume (pure or embedded)
1750 // Result: returns true if volume is pure HFSX or embedded HFSX else false.
1751 //******************************************************************************
1753 Boolean
VolumeObjectIsHFSX(SGlobPtr GPtr
)
1757 HFSMasterDirectoryBlock
*mdbp
;
1758 SVCB
*vcb
= GPtr
->calculatedVCB
;
1759 BlockDescriptor block
;
1762 err
= GetVolumeBlock(vcb
, kIDSector
, kGetBlock
, &block
);
1763 if (err
) return (false);
1765 mdbp
= (HFSMasterDirectoryBlock
*)block
.buffer
;
1766 if (mdbp
->drSigWord
== kHFSXSigWord
) {
1768 } else if (mdbp
->drSigWord
== kHFSSigWord
) {
1769 if (mdbp
->drEmbedSigWord
== kHFSXSigWord
) {
1774 (void) ReleaseVolumeBlock(vcb
, &block
, kReleaseBlock
);
1777 } /* VolumeObjectIsHFSX */
1779 //******************************************************************************
1780 // Routine: VolumeObjectIsHFS
1782 // Function: determine if the volume represented by our VolumeObject is an
1783 // HFS (standard) volume.
1785 // Result: returns true if HFS (standard) volume.
1786 //******************************************************************************
1787 Boolean
VolumeObjectIsHFS( void )
1789 VolumeObjectPtr myVOPtr
;
1791 myVOPtr
= GetVolumeObjectPtr( );
1793 if ( myVOPtr
->volumeType
== kHFSVolumeType
)
1798 } /* VolumeObjectIsHFS */
1801 //******************************************************************************
1802 // Routine: VolumeObjectIsEmbeddedHFSPlus
1804 // Function: determine if the volume represented by our VolumeObject is an
1805 // embedded HFS plus volume.
1807 // Result: returns true if embedded HFS plus volume.
1808 //******************************************************************************
1809 Boolean
VolumeObjectIsEmbeddedHFSPlus( void )
1811 VolumeObjectPtr myVOPtr
;
1813 myVOPtr
= GetVolumeObjectPtr( );
1815 if ( myVOPtr
->volumeType
== kEmbededHFSPlusVolumeType
)
1820 } /* VolumeObjectIsEmbeddedHFSPlus */
1823 //******************************************************************************
1824 // Routine: VolumeObjectIsPureHFSPlus
1826 // Function: determine if the volume represented by our VolumeObject is an
1827 // pure HFS plus volume.
1829 // Result: returns true if pure HFS plus volume.
1830 //******************************************************************************
1831 Boolean
VolumeObjectIsPureHFSPlus( void )
1833 VolumeObjectPtr myVOPtr
;
1835 myVOPtr
= GetVolumeObjectPtr( );
1837 if ( myVOPtr
->volumeType
== kPureHFSPlusVolumeType
)
1842 } /* VolumeObjectIsPureHFSPlus */
1845 //******************************************************************************
1846 // Routine: GetVolumeObjectPtr
1848 // Function: Accessor routine to get a pointer to our VolumeObject structure.
1850 // Result: returns pointer to our VolumeObject.
1851 //******************************************************************************
1852 VolumeObjectPtr
GetVolumeObjectPtr( void )
1854 static VolumeObject myVolumeObject
;
1855 static int myInited
= 0;
1857 if ( myInited
== 0 ) {
1859 bzero( &myVolumeObject
, sizeof(myVolumeObject
) );
1862 return( &myVolumeObject
);
1864 } /* GetVolumeObjectPtr */
1867 //******************************************************************************
1868 // Routine: CheckEmbeddedVolInfoInMDBs
1870 // Function: Check the primary and alternate MDB to see if the embedded volume
1871 // information (drEmbedSigWord and drEmbedExtent) match.
1874 //******************************************************************************
1875 void CheckEmbeddedVolInfoInMDBs( SGlobPtr GPtr
)
1878 Boolean primaryIsDamaged
= false;
1879 Boolean alternateIsDamaged
= false;
1880 VolumeObjectPtr myVOPtr
;
1881 HFSMasterDirectoryBlock
* myPriMDBPtr
;
1882 HFSMasterDirectoryBlock
* myAltMDBPtr
;
1885 BlockDescriptor myPrimary
;
1886 BlockDescriptor myAlternate
;
1888 myVOPtr
= GetVolumeObjectPtr( );
1889 myPrimary
.buffer
= NULL
;
1890 myAlternate
.buffer
= NULL
;
1892 // we only check this if primary and alternate are OK at this point. OK means
1893 // that the primary and alternate MDBs have the correct signature and at least
1894 // one of them points to a valid embedded HFS+ volume.
1895 if ( VolumeObjectIsEmbeddedHFSPlus( ) == false ||
1896 (myVOPtr
->flags
& kVO_PriMDBOK
) == 0 || (myVOPtr
->flags
& kVO_AltMDBOK
) == 0 )
1899 err
= GetVolumeObjectPrimaryMDB( &myPrimary
);
1900 if ( err
!= noErr
) {
1901 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
1902 plog( "\tcould not get primary MDB \n" );
1904 goto ExitThisRoutine
;
1906 myPriMDBPtr
= (HFSMasterDirectoryBlock
*) myPrimary
.buffer
;
1907 err
= GetVolumeObjectAlternateMDB( &myAlternate
);
1908 if ( err
!= noErr
) {
1909 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
1910 plog( "\tcould not get alternate MDB \n" );
1912 goto ExitThisRoutine
;
1914 myAltMDBPtr
= (HFSMasterDirectoryBlock
*) myAlternate
.buffer
;
1916 // bail if everything looks good. NOTE - we can bail if drEmbedExtent info
1917 // is the same in the primary and alternate MDB because we know one of them is
1918 // valid (or VolumeObjectIsEmbeddedHFSPlus would be false and we would not be
1920 if ( myPriMDBPtr
->drEmbedSigWord
== kHFSPlusSigWord
&&
1921 myAltMDBPtr
->drEmbedSigWord
== kHFSPlusSigWord
&&
1922 myPriMDBPtr
->drEmbedExtent
.blockCount
== myAltMDBPtr
->drEmbedExtent
.blockCount
&&
1923 myPriMDBPtr
->drEmbedExtent
.startBlock
== myAltMDBPtr
->drEmbedExtent
.startBlock
)
1924 goto ExitThisRoutine
;
1926 // we know that VolumeObject.embeddedOffset and VolumeObject.totalEmbeddedSectors
1927 // are correct so we will verify the info in each MDB calculates to these values.
1928 myOffset
= (myPriMDBPtr
->drEmbedExtent
.startBlock
* myPriMDBPtr
->drAlBlkSiz
) +
1929 (myPriMDBPtr
->drAlBlSt
* Blk_Size
);
1930 mySectors
= (myPriMDBPtr
->drAlBlkSiz
/ Blk_Size
) * myPriMDBPtr
->drEmbedExtent
.blockCount
;
1932 if ( myOffset
!= myVOPtr
->embeddedOffset
|| mySectors
!= myVOPtr
->totalEmbeddedSectors
)
1933 primaryIsDamaged
= true;
1935 myOffset
= (myAltMDBPtr
->drEmbedExtent
.startBlock
* myAltMDBPtr
->drAlBlkSiz
) +
1936 (myAltMDBPtr
->drAlBlSt
* Blk_Size
);
1937 mySectors
= (myAltMDBPtr
->drAlBlkSiz
/ Blk_Size
) * myAltMDBPtr
->drEmbedExtent
.blockCount
;
1939 if ( myOffset
!= myVOPtr
->embeddedOffset
|| mySectors
!= myVOPtr
->totalEmbeddedSectors
)
1940 alternateIsDamaged
= true;
1942 // now check drEmbedSigWord if everything else is OK
1943 if ( primaryIsDamaged
== false && alternateIsDamaged
== false ) {
1944 if ( myPriMDBPtr
->drEmbedSigWord
!= kHFSPlusSigWord
)
1945 primaryIsDamaged
= true;
1946 else if ( myAltMDBPtr
->drEmbedSigWord
!= kHFSPlusSigWord
)
1947 alternateIsDamaged
= true;
1950 if ( primaryIsDamaged
|| alternateIsDamaged
) {
1951 GPtr
->VIStat
|= S_WMDB
;
1952 WriteError( GPtr
, E_MDBDamaged
, 7, 0 );
1953 if ( primaryIsDamaged
) {
1954 myVOPtr
->flags
&= ~kVO_PriMDBOK
; // mark the primary MDB as damaged
1955 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1956 plog("\tinvalid primary wrapper MDB \n");
1959 myVOPtr
->flags
&= ~kVO_AltMDBOK
; // mark the alternate MDB as damaged
1960 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
1961 plog("\tinvalid alternate wrapper MDB \n");
1966 if ( myPrimary
.buffer
!= NULL
)
1967 (void) ReleaseVolumeBlock( myVOPtr
->vcbPtr
, &myPrimary
, kReleaseBlock
);
1968 if ( myAlternate
.buffer
!= NULL
)
1969 (void) ReleaseVolumeBlock( myVOPtr
->vcbPtr
, &myAlternate
, kReleaseBlock
);
1973 } /* CheckEmbeddedVolInfoInMDBs */
1976 //******************************************************************************
1977 // Routine: ValidVolumeHeader
1979 // Function: Run some sanity checks to make sure the HFSPlusVolumeHeader is valid
1982 //******************************************************************************
1983 OSErr
ValidVolumeHeader( HFSPlusVolumeHeader
*volumeHeader
)
1987 if ((volumeHeader
->signature
== kHFSPlusSigWord
&& volumeHeader
->version
== kHFSPlusVersion
) ||
1988 (volumeHeader
->signature
== kHFSXSigWord
&& volumeHeader
->version
== kHFSXVersion
))
1990 if ( (volumeHeader
->blockSize
!= 0) && ((volumeHeader
->blockSize
& 0x01FF) == 0) ) // non zero multiple of 512
1993 err
= badMDBErr
; //¥¥ I want badVolumeHeaderErr in Errors.i
2004 //_______________________________________________________________________
2008 // This routine initializes a B-Tree header.
2010 // Note: Since large volumes will have bigger b-trees they need to
2011 // have map nodes setup.
2012 //_______________________________________________________________________
2014 void InitBTreeHeader (UInt32 fileSize
, UInt32 clumpSize
, UInt16 nodeSize
, UInt16 recordCount
, UInt16 keySize
,
2015 UInt32 attributes
, UInt32
*mapNodes
, void *buffer
)
2019 UInt32 nodeBitsInHeader
;
2021 BTNodeDescriptor
*ndp
;
2026 ClearMemory(buffer
, nodeSize
); // start out with clean node
2028 nodeCount
= fileSize
/ nodeSize
;
2029 nodeBitsInHeader
= 8 * (nodeSize
- sizeof(BTNodeDescriptor
) - sizeof(BTHeaderRec
) - kBTreeHeaderUserBytes
- 4*sizeof(SInt16
));
2031 usedNodes
= 1; // header takes up one node
2032 *mapNodes
= 0; // number of map nodes initially (0)
2035 // FILL IN THE NODE DESCRIPTOR:
2036 ndp
= (BTNodeDescriptor
*) buffer
; // point to node descriptor
2038 ndp
->kind
= kBTHeaderNode
; // this node contains the B-tree header
2039 ndp
->numRecords
= 3; // there are 3 records (header, map, and user)
2041 if (nodeCount
> nodeBitsInHeader
) // do we need additional map nodes?
2043 UInt32 nodeBitsInMapNode
;
2045 nodeBitsInMapNode
= 8 * (nodeSize
- sizeof(BTNodeDescriptor
) - 2*sizeof(SInt16
) - 2); //¥¥ why (-2) at end???
2047 if (recordCount
> 0) // catalog B-tree?
2048 ndp
->fLink
= 2; // link points to initial map node
2049 //¥¥ Assumes all records will fit in one node. It would be better
2050 //¥¥ to put the map node(s) first, then the records.
2052 ndp
->fLink
= 1; // link points to initial map node
2054 *mapNodes
= (nodeCount
- nodeBitsInHeader
+ (nodeBitsInMapNode
- 1)) / nodeBitsInMapNode
;
2055 usedNodes
+= *mapNodes
;
2058 // FILL IN THE HEADER RECORD:
2059 bth
= (BTHeaderRec
*) ((char*)buffer
+ sizeof(BTNodeDescriptor
)); // point to header
2061 if (recordCount
> 0)
2063 ++usedNodes
; // one more node will be used
2065 bth
->treeDepth
= 1; // tree depth is one level (leaf)
2066 bth
->rootNode
= 1; // root node is also leaf
2067 bth
->firstLeafNode
= 1; // first leaf node
2068 bth
->lastLeafNode
= 1; // last leaf node
2071 bth
->attributes
= attributes
; // flags for 16-bit key lengths, and variable sized index keys
2072 bth
->leafRecords
= recordCount
; // total number of data records
2073 bth
->nodeSize
= nodeSize
; // size of a node
2074 bth
->maxKeyLength
= keySize
; // maximum length of a key
2075 bth
->totalNodes
= nodeCount
; // total number of nodes
2076 bth
->freeNodes
= nodeCount
- usedNodes
; // number of free nodes
2077 bth
->clumpSize
= clumpSize
; //
2078 // bth->btreeType = 0; // 0 = meta data B-tree
2081 // FILL IN THE MAP RECORD:
2082 bitMapPtr
= (UInt32
*) ((Byte
*) buffer
+ sizeof(BTNodeDescriptor
) + sizeof(BTHeaderRec
) + kBTreeHeaderUserBytes
); // point to bitmap
2084 // MARK NODES THAT ARE IN USE:
2085 // Note - worst case (32MB alloc blk) will have only 18 nodes in use.
2086 *bitMapPtr
= ~((UInt32
) 0xFFFFFFFF >> usedNodes
);
2089 // PLACE RECORD OFFSETS AT THE END OF THE NODE:
2090 offsetPtr
= (SInt16
*) ((Byte
*) buffer
+ nodeSize
- 4*sizeof(SInt16
));
2092 *offsetPtr
++ = sizeof(BTNodeDescriptor
) + sizeof(BTHeaderRec
) + kBTreeHeaderUserBytes
+ nodeBitsInHeader
/8; // offset to free space
2093 *offsetPtr
++ = sizeof(BTNodeDescriptor
) + sizeof(BTHeaderRec
) + kBTreeHeaderUserBytes
; // offset to allocation map
2094 *offsetPtr
++ = sizeof(BTNodeDescriptor
) + sizeof(BTHeaderRec
); // offset to user space
2095 *offsetPtr
= sizeof(BTNodeDescriptor
); // offset to BTH
2098 /*------------------------------------------------------------------------------
2100 Routine: CalculateItemCount
2102 Function: determines number of items for progress feedback
2104 Input: vRefNum: the volume to count items
2106 Output: number of items
2108 ------------------------------------------------------------------------------*/
2110 void CalculateItemCount( SGlob
*GPtr
, UInt64
*itemCount
, UInt64
*onePercent
)
2112 BTreeControlBlock
*btcb
;
2113 VolumeObjectPtr myVOPtr
;
2115 UInt32 realFreeNodes
;
2116 SVCB
*vcb
= GPtr
->calculatedVCB
;
2118 /* each bitmap segment is an item */
2119 myVOPtr
= GetVolumeObjectPtr( );
2120 items
= GPtr
->calculatedVCB
->vcbTotalBlocks
/ 1024;
2123 // Items is the used node count and leaf record count for each btree...
2126 btcb
= (BTreeControlBlock
*) vcb
->vcbCatalogFile
->fcbBtree
;
2127 realFreeNodes
= ((BTreeExtensionsRec
*)btcb
->refCon
)->realFreeNodeCount
;
2128 items
+= (2 * btcb
->leafRecords
) + (btcb
->totalNodes
- realFreeNodes
);
2130 btcb
= (BTreeControlBlock
*) vcb
->vcbExtentsFile
->fcbBtree
;
2131 realFreeNodes
= ((BTreeExtensionsRec
*)btcb
->refCon
)->realFreeNodeCount
;
2132 items
+= btcb
->leafRecords
+ (btcb
->totalNodes
- realFreeNodes
);
2134 if ( vcb
->vcbAttributesFile
!= NULL
)
2136 btcb
= (BTreeControlBlock
*) vcb
->vcbAttributesFile
->fcbBtree
;
2137 realFreeNodes
= ((BTreeExtensionsRec
*)btcb
->refCon
)->realFreeNodeCount
;
2139 items
+= (btcb
->leafRecords
+ (btcb
->totalNodes
- realFreeNodes
));
2142 *onePercent
= items
/ 100;
2145 // [2239291] We're calculating the progress for the wrapper and the embedded volume separately, which
2146 // confuses the caller (since they see the progress jump to a large percentage while checking the wrapper,
2147 // then jump to a small percentage when starting to check the embedded volume). To avoid this behavior,
2148 // we pretend the wrapper has 100 times as many items as it really does. This means the progress will
2149 // never exceed 1% for the wrapper.
2151 /* fsck_hfs doesn't deal wih the wrapper at this time (8.29.2002)
2152 if ( (myVOPtr->volumeType == kEmbededHFSPlusVolumeType) && (GPtr->inputFlags & examineWrapperMask) )
2155 // Add en extra Å 5% to smooth the progress
2156 items
+= *onePercent
* 5;
2162 SFCB
* ResolveFCB(short fileRefNum
)
2164 return( (SFCB
*)((unsigned long)GetFCBSPtr() + (unsigned long)fileRefNum
) );
2168 //******************************************************************************
2169 // Routine: SetupFCB fills in the FCB info
2171 // Returns: The filled up FCB
2172 //******************************************************************************
2173 void SetupFCB( SVCB
*vcb
, SInt16 refNum
, UInt32 fileID
, UInt32 fileClumpSize
)
2177 fcb
= ResolveFCB(refNum
);
2179 fcb
->fcbFileID
= fileID
;
2180 fcb
->fcbVolume
= vcb
;
2181 fcb
->fcbClumpSize
= fileClumpSize
;
2185 //******************************************************************************
2187 // Routine: ResolveFileRefNum
2189 // Purpose: Return a file reference number for a given file control block
2193 // fileCtrlBlockPtr Pointer to the SFCB
2196 // result File reference number,
2197 // or 0 if fileCtrlBlockPtr is invalid
2199 pascal short ResolveFileRefNum(SFCB
* fileCtrlBlockPtr
)
2201 return( (unsigned long)fileCtrlBlockPtr
- (unsigned long)GetFCBSPtr() );
2208 void SetFCBSPtr( Ptr value
)
2213 Ptr
GetFCBSPtr( void )
2219 //_______________________________________________________________________
2221 // Routine: FlushVolumeControlBlock
2222 // Arguments: SVCB *vcb
2223 // Output: OSErr err
2225 // Function: Flush volume information to either the HFSPlusVolumeHeader
2226 // of the Master Directory Block
2227 //_______________________________________________________________________
2229 OSErr
FlushVolumeControlBlock( SVCB
*vcb
)
2232 HFSPlusVolumeHeader
*volumeHeader
;
2234 BlockDescriptor block
;
2236 if ( ! IsVCBDirty( vcb
) ) // if it's not dirty
2239 block
.buffer
= NULL
;
2240 err
= GetVolumeObjectPrimaryBlock( &block
);
2243 // attempt to fix the primary with alternate
2244 if ( block
.buffer
!= NULL
) {
2245 (void) ReleaseVolumeBlock( vcb
, &block
, kReleaseBlock
);
2246 block
.buffer
= NULL
;
2249 err
= VolumeObjectFixPrimaryBlock( );
2250 ReturnIfError( err
);
2252 // should be able to get it now
2253 err
= GetVolumeObjectPrimaryBlock( &block
);
2254 ReturnIfError( err
);
2257 if ( vcb
->vcbSignature
== kHFSPlusSigWord
)
2259 volumeHeader
= (HFSPlusVolumeHeader
*) block
.buffer
;
2261 // 2005507, Keep the MDB creation date and HFSPlusVolumeHeader creation date in sync.
2262 if ( vcb
->vcbEmbeddedOffset
!= 0 ) // It's a wrapped HFS+ volume
2264 HFSMasterDirectoryBlock
*mdb
;
2265 BlockDescriptor mdb_block
;
2267 mdb_block
.buffer
= NULL
;
2268 err
= GetVolumeObjectPrimaryMDB( &mdb_block
);
2271 mdb
= (HFSMasterDirectoryBlock
*) mdb_block
.buffer
;
2272 if ( mdb
->drCrDate
!= vcb
->vcbCreateDate
) // The creation date changed
2274 mdb
->drCrDate
= vcb
->vcbCreateDate
;
2275 (void) ReleaseVolumeBlock(vcb
, &mdb_block
, kForceWriteBlock
);
2276 mdb_block
.buffer
= NULL
;
2279 if ( mdb_block
.buffer
!= NULL
)
2280 (void) ReleaseVolumeBlock(vcb
, &mdb_block
, kReleaseBlock
);
2283 volumeHeader
->attributes
= vcb
->vcbAttributes
;
2284 volumeHeader
->lastMountedVersion
= kFSCKMountVersion
;
2285 volumeHeader
->createDate
= vcb
->vcbCreateDate
; // NOTE: local time, not GMT!
2286 volumeHeader
->modifyDate
= vcb
->vcbModifyDate
;
2287 volumeHeader
->backupDate
= vcb
->vcbBackupDate
;
2288 volumeHeader
->checkedDate
= vcb
->vcbCheckedDate
;
2289 volumeHeader
->fileCount
= vcb
->vcbFileCount
;
2290 volumeHeader
->folderCount
= vcb
->vcbFolderCount
;
2291 volumeHeader
->blockSize
= vcb
->vcbBlockSize
;
2292 volumeHeader
->totalBlocks
= vcb
->vcbTotalBlocks
;
2293 volumeHeader
->freeBlocks
= vcb
->vcbFreeBlocks
;
2294 volumeHeader
->nextAllocation
= vcb
->vcbNextAllocation
;
2295 volumeHeader
->rsrcClumpSize
= vcb
->vcbRsrcClumpSize
;
2296 volumeHeader
->dataClumpSize
= vcb
->vcbDataClumpSize
;
2297 volumeHeader
->nextCatalogID
= vcb
->vcbNextCatalogID
;
2298 volumeHeader
->writeCount
= vcb
->vcbWriteCount
;
2299 volumeHeader
->encodingsBitmap
= vcb
->vcbEncodingsBitmap
;
2301 //¥¥Êshould we use the vcb or fcb clumpSize values ????? -djb
2302 volumeHeader
->allocationFile
.clumpSize
= vcb
->vcbAllocationFile
->fcbClumpSize
;
2303 volumeHeader
->extentsFile
.clumpSize
= vcb
->vcbExtentsFile
->fcbClumpSize
;
2304 volumeHeader
->catalogFile
.clumpSize
= vcb
->vcbCatalogFile
->fcbClumpSize
;
2306 CopyMemory( vcb
->vcbFinderInfo
, volumeHeader
->finderInfo
, sizeof(volumeHeader
->finderInfo
) );
2308 fcb
= vcb
->vcbExtentsFile
;
2309 CopyMemory( fcb
->fcbExtents32
, volumeHeader
->extentsFile
.extents
, sizeof(HFSPlusExtentRecord
) );
2310 volumeHeader
->extentsFile
.logicalSize
= fcb
->fcbLogicalSize
;
2311 volumeHeader
->extentsFile
.totalBlocks
= (u_int32_t
)(fcb
->fcbPhysicalSize
/ vcb
->vcbBlockSize
);
2313 fcb
= vcb
->vcbCatalogFile
;
2314 CopyMemory( fcb
->fcbExtents32
, volumeHeader
->catalogFile
.extents
, sizeof(HFSPlusExtentRecord
) );
2315 volumeHeader
->catalogFile
.logicalSize
= fcb
->fcbLogicalSize
;
2316 volumeHeader
->catalogFile
.totalBlocks
= (u_int32_t
)(fcb
->fcbPhysicalSize
/ vcb
->vcbBlockSize
);
2318 fcb
= vcb
->vcbAllocationFile
;
2319 CopyMemory( fcb
->fcbExtents32
, volumeHeader
->allocationFile
.extents
, sizeof(HFSPlusExtentRecord
) );
2320 volumeHeader
->allocationFile
.logicalSize
= fcb
->fcbLogicalSize
;
2321 volumeHeader
->allocationFile
.totalBlocks
= (u_int32_t
)(fcb
->fcbPhysicalSize
/ vcb
->vcbBlockSize
);
2323 if (vcb
->vcbAttributesFile
!= NULL
) // Only update fields if an attributes file existed and was open
2325 fcb
= vcb
->vcbAttributesFile
;
2326 CopyMemory( fcb
->fcbExtents32
, volumeHeader
->attributesFile
.extents
, sizeof(HFSPlusExtentRecord
) );
2327 volumeHeader
->attributesFile
.logicalSize
= fcb
->fcbLogicalSize
;
2328 volumeHeader
->attributesFile
.clumpSize
= fcb
->fcbClumpSize
;
2329 volumeHeader
->attributesFile
.totalBlocks
= (u_int32_t
)(fcb
->fcbPhysicalSize
/ vcb
->vcbBlockSize
);
2334 HFSMasterDirectoryBlock
*mdbP
;
2336 mdbP
= (HFSMasterDirectoryBlock
*) block
.buffer
;
2338 mdbP
->drCrDate
= vcb
->vcbCreateDate
;
2339 mdbP
->drLsMod
= vcb
->vcbModifyDate
;
2340 mdbP
->drAtrb
= (UInt16
)vcb
->vcbAttributes
;
2341 mdbP
->drClpSiz
= vcb
->vcbDataClumpSize
;
2342 mdbP
->drNxtCNID
= vcb
->vcbNextCatalogID
;
2343 mdbP
->drFreeBks
= vcb
->vcbFreeBlocks
;
2344 mdbP
->drXTClpSiz
= vcb
->vcbExtentsFile
->fcbClumpSize
;
2345 mdbP
->drCTClpSiz
= vcb
->vcbCatalogFile
->fcbClumpSize
;
2347 mdbP
->drNmFls
= vcb
->vcbNmFls
;
2348 mdbP
->drNmRtDirs
= vcb
->vcbNmRtDirs
;
2349 mdbP
->drFilCnt
= vcb
->vcbFileCount
;
2350 mdbP
->drDirCnt
= vcb
->vcbFolderCount
;
2352 fcb
= vcb
->vcbExtentsFile
;
2353 CopyMemory( fcb
->fcbExtents16
, mdbP
->drXTExtRec
, sizeof( mdbP
->drXTExtRec
) );
2355 fcb
= vcb
->vcbCatalogFile
;
2356 CopyMemory( fcb
->fcbExtents16
, mdbP
->drCTExtRec
, sizeof( mdbP
->drCTExtRec
) );
2359 //-- Write the VHB/MDB out by releasing the block dirty
2360 if ( block
.buffer
!= NULL
) {
2361 err
= ReleaseVolumeBlock(vcb
, &block
, kForceWriteBlock
);
2362 block
.buffer
= NULL
;
2364 MarkVCBClean( vcb
);
2370 //_______________________________________________________________________
2372 // Routine: FlushAlternateVolumeControlBlock
2373 // Arguments: SVCB *vcb
2374 // Boolean ifHFSPlus
2375 // Output: OSErr err
2377 // Function: Flush volume information to either the Alternate HFSPlusVolumeHeader or the
2378 // Alternate Master Directory Block. Called by the BTree when the catalog
2379 // or extent files grow. Simply BlockMoves the original to the alternate
2381 //_______________________________________________________________________
2383 OSErr
FlushAlternateVolumeControlBlock( SVCB
*vcb
, Boolean isHFSPlus
)
2386 VolumeObjectPtr myVOPtr
;
2388 BlockDescriptor pri_block
, alt_block
;
2390 pri_block
.buffer
= NULL
;
2391 alt_block
.buffer
= NULL
;
2392 myVOPtr
= GetVolumeObjectPtr( );
2394 err
= FlushVolumeControlBlock( vcb
);
2395 err
= GetVolumeObjectPrimaryBlock( &pri_block
);
2397 // invalidate if we have not marked the primary as OK
2398 if ( VolumeObjectIsHFS( ) ) {
2399 if ( (myVOPtr
->flags
& kVO_PriMDBOK
) == 0 )
2402 else if ( (myVOPtr
->flags
& kVO_PriVHBOK
) == 0 ) {
2406 goto ExitThisRoutine
;
2408 GetVolumeObjectAlternateBlockNum( &myBlockNum
);
2409 if ( myBlockNum
!= 0 ) {
2410 // we don't care if this is an invalid MDB / VHB since we will write over it
2411 err
= GetVolumeObjectAlternateBlock( &alt_block
);
2412 if ( err
== noErr
|| err
== badMDBErr
|| err
== noMacDskErr
) {
2413 CopyMemory( pri_block
.buffer
, alt_block
.buffer
, Blk_Size
);
2414 (void) ReleaseVolumeBlock(vcb
, &alt_block
, kForceWriteBlock
);
2415 alt_block
.buffer
= NULL
;
2420 if ( pri_block
.buffer
!= NULL
)
2421 (void) ReleaseVolumeBlock( vcb
, &pri_block
, kReleaseBlock
);
2422 if ( alt_block
.buffer
!= NULL
)
2423 (void) ReleaseVolumeBlock( vcb
, &alt_block
, kReleaseBlock
);
2429 ConvertToHFSPlusExtent( const HFSExtentRecord oldExtents
, HFSPlusExtentRecord newExtents
)
2433 // go backwards so we can convert in place!
2435 for (i
= kHFSPlusExtentDensity
-1; i
> 2; --i
)
2437 newExtents
[i
].blockCount
= 0;
2438 newExtents
[i
].startBlock
= 0;
2441 newExtents
[2].blockCount
= oldExtents
[2].blockCount
;
2442 newExtents
[2].startBlock
= oldExtents
[2].startBlock
;
2443 newExtents
[1].blockCount
= oldExtents
[1].blockCount
;
2444 newExtents
[1].startBlock
= oldExtents
[1].startBlock
;
2445 newExtents
[0].blockCount
= oldExtents
[0].blockCount
;
2446 newExtents
[0].startBlock
= oldExtents
[0].startBlock
;
2451 OSErr
CacheWriteInPlace( SVCB
*vcb
, UInt32 fileRefNum
, HIOParam
*iopb
, UInt64 currentPosition
, UInt32 maximumBytes
, UInt32
*actualBytes
)
2455 UInt32 contiguousBytes
;
2459 buffer
= (char*)iopb
->ioBuffer
+ iopb
->ioActCount
;
2461 err
= MapFileBlockC(vcb
, ResolveFCB(fileRefNum
), maximumBytes
, (currentPosition
>> kSectorShift
),
2462 &diskBlock
, &contiguousBytes
);
2466 err
= DeviceWrite(vcb
->vcbDriverWriteRef
, vcb
->vcbDriveNumber
, buffer
, (diskBlock
<< Log2BlkLo
), contiguousBytes
, actualBytes
);
2472 void PrintName( int theCount
, const UInt8
*theNamePtr
, Boolean isUnicodeString
)
2477 myCount
= (isUnicodeString
) ? (theCount
* 2) : theCount
;
2478 for ( i
= 0; i
< myCount
; i
++ )
2479 plog( "%02X ", *(theNamePtr
+ i
) );
2484 /* Function: add_prime_bucket_uint32
2487 * This function increments the prime number buckets in the prime bucket
2488 * set based on the uint32_t number provided. This function increments
2489 * each prime number bucket by one at an offset of the corresponding
2490 * remainder of the division. This function is based on Chinese Remainder
2491 * Theorem and adds the given number to the set to compare later.
2494 * 1. Corresponding prime bucket to increment.
2495 * 2. uint32_t number to add to the set.
2499 void add_prime_bucket_uint32(PrimeBuckets
*cur
, uint32_t num
)
2501 int r32
, r27
, r25
, r7
, r11
, r13
, r17
, r19
, r23
, r29
, r31
;
2507 /* Perform the necessary divisions here */
2520 /* Update bucket for attribute bit */
2536 /* Function: add_prime_bucket_uint64
2539 * This function increments the prime number buckets in the prime bucket
2540 * set based on the uint64_t number provided. This function increments
2541 * each prime number bucket by one at an offset of the corresponding
2542 * remainder of the division. This function is based on Chinese Remainder
2543 * Theorem and adds the given number to the set to compare later.
2546 * 1. Corresponding prime bucket to increment.
2547 * 2. uint64_t number to add to the set.
2551 void add_prime_bucket_uint64(PrimeBuckets
*cur
, uint64_t num
)
2553 size_t r32
, r27
, r25
, r7
, r11
, r13
, r17
, r19
, r23
, r29
, r31
;
2559 /* Perform the necessary divisions here */
2572 /* Update bucket for attribute bit */
2588 /* Compares the two prime buckets provided.
2590 * zero - If the two buckets are same.
2591 * non-zero - If the two buckets do not match.
2593 int compare_prime_buckets(PrimeBuckets
*bucket1
, PrimeBuckets
*bucket2
)
2598 for (i
=0; i
<32; i
++) {
2599 if (bucket1
->n32
[i
] != bucket2
->n32
[i
]) {
2604 for (i
=0; i
<27; i
++) {
2605 if (bucket1
->n27
[i
] != bucket2
->n27
[i
]) {
2610 for (i
=0; i
<25; i
++) {
2611 if (bucket1
->n25
[i
] != bucket2
->n25
[i
]) {
2616 for (i
=0; i
<7; i
++) {
2617 if (bucket1
->n7
[i
] != bucket2
->n7
[i
]) {
2622 for (i
=0; i
<11; i
++) {
2623 if (bucket1
->n11
[i
] != bucket2
->n11
[i
]) {
2628 for (i
=0; i
<13; i
++) {
2629 if (bucket1
->n13
[i
] != bucket2
->n13
[i
]) {
2634 for (i
=0; i
<17; i
++) {
2635 if (bucket1
->n17
[i
] != bucket2
->n17
[i
]) {
2640 for (i
=0; i
<19; i
++) {
2641 if (bucket1
->n19
[i
] != bucket2
->n19
[i
]) {
2646 for (i
=0; i
<23; i
++) {
2647 if (bucket1
->n23
[i
] != bucket2
->n23
[i
]) {
2652 for (i
=0; i
<29; i
++) {
2653 if (bucket1
->n29
[i
] != bucket2
->n29
[i
]) {
2658 for (i
=0; i
<31; i
++) {
2659 if (bucket1
->n31
[i
] != bucket2
->n31
[i
]) {
2670 /* Prints the prime number bucket for the passed pointer */
2671 void print_prime_buckets(PrimeBuckets
*cur
)
2676 for (i
=0; i
<32; i
++) {
2677 plog ("%d,", cur
->n32
[i
]);
2682 for (i
=0; i
<27; i
++) {
2683 plog ("%d,", cur
->n27
[i
]);
2688 for (i
=0; i
<25; i
++) {
2689 plog ("%d,", cur
->n25
[i
]);
2694 for (i
=0; i
<7; i
++) {
2695 plog ("%d,", cur
->n7
[i
]);
2700 for (i
=0; i
<11; i
++) {
2701 plog ("%d,", cur
->n11
[i
]);
2706 for (i
=0; i
<13; i
++) {
2707 plog ("%d,", cur
->n13
[i
]);
2712 for (i
=0; i
<17; i
++) {
2713 plog ("%d,", cur
->n17
[i
]);
2718 for (i
=0; i
<19; i
++) {
2719 plog ("%d,", cur
->n19
[i
]);
2724 for (i
=0; i
<23; i
++) {
2725 plog ("%d,", cur
->n23
[i
]);
2730 for (i
=0; i
<29; i
++) {
2731 plog ("%d,", cur
->n29
[i
]);
2736 for (i
=0; i
<31; i
++) {
2737 plog ("%d,", cur
->n31
[i
]);