2 * Copyright (c) 1999-2009 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
26 Contains: xxx put contents here xxx
28 Version: xxx put version here xxx
30 Copyright: © 1997-1999 by Apple Computer, Inc., all rights reserved.
34 #include "Scavenger.h"
43 #include <libkern/OSByteOrder.h>
44 #define SW16(x) OSSwapBigToHostInt16(x)
45 #define SW32(x) OSSwapBigToHostInt32(x)
46 #define SW64(x) OSSwapBigToHostInt64(x)
48 extern int OpenDeviceByUUID(void *uuidp
, char **nameptr
);
50 // internal routine prototypes
52 static int RcdValErr( SGlobPtr GPtr
, OSErr type
, UInt32 correct
, UInt32 incorrect
, HFSCatalogNodeID parid
);
54 static int RcdNameLockedErr( SGlobPtr GPtr
, OSErr type
, UInt32 incorrect
);
56 static OSErr
RcdMDBEmbededVolDescriptionErr( SGlobPtr GPtr
, OSErr type
, HFSMasterDirectoryBlock
*mdb
);
58 static OSErr
CheckNodesFirstOffset( SGlobPtr GPtr
, BTreeControlBlock
*btcb
);
60 static OSErr
ScavengeVolumeType( SGlobPtr GPtr
, HFSMasterDirectoryBlock
*mdb
, UInt32
*volumeType
);
61 static OSErr
SeekVolumeHeader( SGlobPtr GPtr
, UInt64 startSector
, UInt32 numSectors
, UInt64
*vHSector
);
63 /* overlapping extents verification functions prototype */
64 static OSErr
AddExtentToOverlapList( SGlobPtr GPtr
, HFSCatalogNodeID fileNumber
, const char *attrName
, UInt32 extentStartBlock
, UInt32 extentBlockCount
, UInt8 forkType
);
66 static Boolean
ExtentInfoExists( ExtentsTable
**extentsTableH
, ExtentInfo
*extentInfo
);
68 static void CheckHFSPlusExtentRecords(SGlobPtr GPtr
, UInt32 fileID
, const char *attrname
, HFSPlusExtentRecord extent
, UInt8 forkType
);
70 static void CheckHFSExtentRecords(SGlobPtr GPtr
, UInt32 fileID
, HFSExtentRecord extent
, UInt8 forkType
);
72 static Boolean
DoesOverlap(SGlobPtr GPtr
, UInt32 fileID
, const char *attrname
, UInt32 startBlock
, UInt32 blockCount
, UInt8 forkType
);
74 static int CompareExtentFileID(const void *first
, const void *second
);
77 * Check if a volume is journaled.
79 * If journal_bit_only is true, the function only checks
80 * if kHFSVolumeJournaledBit is set or not. If the bit
81 * is set, function returns 1 otherwise 0.
83 * If journal_bit_only is false, in addition to checking
84 * kHFSVolumeJournaledBit, the function also checks if the
85 * last mounted version indicates failed journal replay,
86 * or runtime corruption was detected or simply the volume
87 * is not journaled and it was not unmounted cleanly.
88 * If all of the above conditions are false and the journal
89 * bit is set, function returns 1 to indicate that the
90 * volume is journaled truly otherwise returns 1 to fake
91 * that volume is not journaled.
93 * returns: 0 not journaled or any of the above conditions are true
98 CheckIfJournaled(SGlobPtr GPtr
, Boolean journal_bit_only
)
104 HFSMasterDirectoryBlock
*mdbp
;
105 HFSPlusVolumeHeader
*vhp
;
106 SVCB
*vcb
= GPtr
->calculatedVCB
;
107 ReleaseBlockOptions rbOptions
;
108 BlockDescriptor block
;
110 vhp
= (HFSPlusVolumeHeader
*) NULL
;
111 rbOptions
= kReleaseBlock
;
113 err
= GetVolumeBlock(vcb
, kIDSector
, kGetBlock
, &block
);
116 mdbp
= (HFSMasterDirectoryBlock
*) block
.buffer
;
118 if (mdbp
->drSigWord
== kHFSPlusSigWord
|| mdbp
->drSigWord
== kHFSXSigWord
) {
119 vhp
= (HFSPlusVolumeHeader
*) block
.buffer
;
121 } else if (mdbp
->drSigWord
== kHFSSigWord
) {
123 if (mdbp
->drEmbedSigWord
== kHFSPlusSigWord
) {
127 blkSectors
= mdbp
->drAlBlkSiz
/ 512;
128 vhSector
= mdbp
->drAlBlSt
;
129 vhSector
+= blkSectors
* mdbp
->drEmbedExtent
.startBlock
;
130 vhSector
+= kIDSector
;
132 (void) ReleaseVolumeBlock(vcb
, &block
, kReleaseBlock
);
133 err
= GetVolumeBlock(vcb
, vhSector
, kGetBlock
, &block
);
136 vhp
= (HFSPlusVolumeHeader
*) block
.buffer
;
137 mdbp
= (HFSMasterDirectoryBlock
*) NULL
;
142 if ((vhp
!= NULL
) && (ValidVolumeHeader(vhp
) == noErr
)) {
143 result
= ((vhp
->attributes
& kHFSVolumeJournaledMask
) != 0);
144 if (journal_bit_only
== true) {
148 // even if journaling is enabled for this volume, we'll return
149 // false if it wasn't unmounted cleanly and it was previously
150 // mounted by someone that doesn't know about journaling.
151 // or if lastMountedVersion is kFSKMountVersion
152 if ( vhp
->lastMountedVersion
== kFSKMountVersion
||
153 (vhp
->attributes
& kHFSVolumeInconsistentMask
) ||
154 ((vhp
->lastMountedVersion
!= kHFSJMountVersion
) &&
155 (vhp
->attributes
& kHFSVolumeUnmountedMask
) == 0)) {
163 (void) ReleaseVolumeBlock(vcb
, &block
, rbOptions
);
169 * Get the JournalInfoBlock from a volume.
171 * It borrows code to get the volume header. Note that it
172 * uses the primary volume header, not the alternate one.
173 * It returns 0 on success, or an error value.
174 * If requested, it will also set the block size (as a 32-bit
175 * value), via bsizep -- this is useful because the journal code
176 * needs to know the volume blocksize, but it doesn't necessarily
179 * Note also that it does direct reads, rather than going through
180 * the cache code. This simplifies getting the JIB.
184 GetJournalInfoBlock(SGlobPtr GPtr
, JournalInfoBlock
*jibp
, UInt32
*bsizep
)
191 HFSMasterDirectoryBlock
*mdbp
;
192 HFSPlusVolumeHeader
*vhp
;
193 SVCB
*vcb
= GPtr
->calculatedVCB
;
194 ReleaseBlockOptions rbOptions
;
195 BlockDescriptor block
;
196 size_t blockSize
= 0;
197 off_t embeddedOffset
= 0;
199 vhp
= (HFSPlusVolumeHeader
*) NULL
;
200 rbOptions
= kReleaseBlock
;
205 err
= GetVolumeBlock(vcb
, kIDSector
, kGetBlock
, &block
);
206 if (err
) return (err
);
208 mdbp
= (HFSMasterDirectoryBlock
*) block
.buffer
;
210 if (mdbp
->drSigWord
== kHFSPlusSigWord
|| mdbp
->drSigWord
== kHFSXSigWord
) {
211 vhp
= (HFSPlusVolumeHeader
*) block
.buffer
;
213 } else if (mdbp
->drSigWord
== kHFSSigWord
) {
215 if (mdbp
->drEmbedSigWord
== kHFSPlusSigWord
) {
219 blkSectors
= mdbp
->drAlBlkSiz
/ 512;
220 vhSector
= mdbp
->drAlBlSt
;
221 vhSector
+= blkSectors
* mdbp
->drEmbedExtent
.startBlock
;
222 vhSector
+= kIDSector
;
224 embeddedOffset
= (mdbp
->drEmbedExtent
.startBlock
* mdbp
->drAlBlkSiz
) + (mdbp
->drAlBlSt
* Blk_Size
);
226 plog("Embedded offset is %lld\n", embeddedOffset
);
228 (void) ReleaseVolumeBlock(vcb
, &block
, kReleaseBlock
);
229 err
= GetVolumeBlock(vcb
, vhSector
, kGetBlock
, &block
);
230 if (err
) return (err
);
232 vhp
= (HFSPlusVolumeHeader
*) block
.buffer
;
233 mdbp
= (HFSMasterDirectoryBlock
*) NULL
;
242 if ((err
= ValidVolumeHeader(vhp
)) != noErr
) {
247 // journalInfoBlock is not automatically swapped
248 jiBlk
= SW32(vhp
->journalInfoBlock
);
249 blockSize
= vhp
->blockSize
;
250 (void)ReleaseVolumeBlock(vcb
, &block
, rbOptions
);
253 int jfd
= GPtr
->DrvNum
;
254 uint8_t block
[blockSize
];
257 nread
= pread(jfd
, block
, blockSize
, (off_t
)jiBlk
* blockSize
+ embeddedOffset
);
258 if (nread
== blockSize
) {
260 memcpy(jibp
, block
, sizeof(JournalInfoBlock
));
266 plog("%s: Tried to read JIB, got %zd\n", __FUNCTION__
, nread
);
277 * Journal checksum calculation, taken directly from TN1150.
280 calc_checksum(unsigned char *ptr
, int len
)
284 for(i
=0; i
< len
; i
++, ptr
++) {
285 cksum
= (cksum
<< 8) ^ (cksum
+ *ptr
);
292 * The journal_header structure is not defined in <hfs/hfs_format.h>;
293 * it's described in TN1150. It is on disk in the endian mode that was
294 * used to write it, so we may or may not need to swap the fields.
296 typedef struct journal_header
{
308 #define JOURNAL_HEADER_MAGIC 0x4a4e4c78
309 #define ENDIAN_MAGIC 0x12345678
310 #define JOURNAL_HEADER_CKSUM_SIZE (offsetof(struct journal_header, sequence_num))
313 * Determine if a journal is empty.
314 * This code can use an in-filesystem, or external, journal.
315 * In general, it returns 0 if the journal exists, and appears to
316 * be non-empty (that is, start and end in the journal header are
317 * the same); it will return 1 if it exists and is empty, or if
318 * there was a problem getting the journal. (This behaviour was
319 * chosen because it mimics the existing behaviour of fsck_hfs,
320 * which has traditionally done nothing with the journal. Future
321 * versions may be more demanding.)
323 * <jp> is an OUT parameter: the contents of the structure it points
324 * to are filled in by this routine. (The reasoning for doing this
325 * is because this rountine has to open the journal info block, and read
326 * from the journal device, so putting this in another function was
327 * duplicative and error-prone. By making it a structure instead of
328 * discrete arguments, it can also be extended in the future if necessary.)
331 IsJournalEmpty(SGlobPtr GPtr
, fsckJournalInfo_t
*jp
)
336 JournalInfoBlock jib
;
339 result
= GetJournalInfoBlock(GPtr
, &jib
, &bsize
);
341 /* jib is not byte swapped */
342 /* If the journal needs to be initialized, it's empty. */
343 if ((SW32(jib
.flags
) & kJIJournalNeedInitMask
) == 0) {
344 off_t hdrOffset
= SW64(jib
.offset
);
345 struct journal_header
*jhdr
;
346 uint8_t block
[bsize
];
350 /* If it's an external journal, kJIJournalInSFMask will not be set */
351 if (SW32(jib
.flags
) & kJIJournalInFSMask
) {
352 jfd
= dup(GPtr
->DrvNum
);
353 jp
->name
= strdup(GPtr
->deviceNode
);
355 char **namePtr
= jp
? &jp
->name
: NULL
;
357 plog("External Journal device\n");
358 jfd
= OpenDeviceByUUID(&jib
.ext_jnl_uuid
, namePtr
);
362 plog("Unable to get journal file descriptor, journal flags = %#x\n", SW32(jib
.flags
));
368 jp
->jnlOffset
= SW64(jib
.offset
);
369 jp
->jnlSize
= SW64(jib
.size
);
372 nread
= pread(jfd
, block
, bsize
, hdrOffset
);
375 plog("Could not read journal from descriptor %d: %s", jfd
, strerror(errno
));
378 } else if (nread
!= bsize
) {
380 plog("Only read %zd bytes from journal (expected %zd)", nread
, bsize
);
386 /* We got the journal header, now we need to check it */
391 jhdr
= (struct journal_header
*)block
;
393 if (jhdr
->magic
== JOURNAL_HEADER_MAGIC
||
394 SW32(jhdr
->magic
) == JOURNAL_HEADER_MAGIC
) {
395 if (jhdr
->endian
== ENDIAN_MAGIC
)
397 else if (SW32(jhdr
->endian
) == ENDIAN_MAGIC
)
403 cksum
= swap
? SW32(jhdr
->checksum
) : jhdr
->checksum
;
406 /* Checksum calculation needs the checksum field to be zero. */
407 calc_sum
= calc_checksum((unsigned char*)jhdr
, JOURNAL_HEADER_CKSUM_SIZE
);
408 /* But, for now, this is for debugging purposes only */
409 if (calc_sum
!= cksum
) {
411 plog("Journal checksum doesn't match: orig %x != calc %x\n", cksum
, calc_sum
);
413 /* We have a journal, we got the header, now we check the start and end */
414 if (jhdr
->start
!= jhdr
->end
) {
417 plog("Non-empty journal: start = %lld, end = %lld\n",
418 swap
? SW64(jhdr
->start
) : jhdr
->start
,
419 swap
? SW64(jhdr
->end
) : jhdr
->end
);
431 * The functions checks whether the volume is clean or dirty. It
432 * also marks the volume as clean/dirty depending on the type
433 * of operation specified. It modifies the volume header only
434 * if the old values are not same as the new values. If the volume
435 * header is updated, it also sets the last mounted version for HFS+.
438 * GPtr - Pointer to scavenger global area
439 * operation - Type of operation to perform
440 * kCheckVolume, // check if volume is clean/dirty
441 * kMarkVolumeDirty, // mark the volume dirty
442 * kMarkVolumeClean // mark the volume clean
445 * modified - true if the VH/MDB was modified, otherwise false.
447 * -1 - if the volume is not an HFS/HFS+ volume
448 * 0 - if the volume was dirty or marked dirty
449 * 1 - if the volume was clean or marked clean
450 * If the operation requested was to mark the volume clean/dirty,
451 * the return value is dependent on type of operation (described above).
453 int CheckForClean(SGlobPtr GPtr
, UInt8 operation
, Boolean
*modified
)
455 enum { unknownVolume
= -1, cleanUnmount
= 1, dirtyUnmount
= 0};
456 int result
= unknownVolume
;
457 Boolean update
= false;
458 HFSMasterDirectoryBlock
*mdbp
;
459 HFSPlusVolumeHeader
*vhp
;
460 BlockDescriptor block
;
461 ReleaseBlockOptions rbOptions
;
466 vcb
= GPtr
->calculatedVCB
;
468 rbOptions
= kReleaseBlock
;
470 /* Get the block number for VH/MDB */
471 GetVolumeObjectBlockNum(&blockNum
);
473 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
474 plog( "\t%s - unknown volume type \n", __FUNCTION__
);
475 goto ExitThisRoutine
;
478 /* Get VH or MDB depending on the type of volume */
479 result
= GetVolumeObjectPrimaryBlock(&block
);
481 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
482 plog( "\t%s - could not get VHB/MDB at block %qd \n", __FUNCTION__
, blockNum
);
483 result
= unknownVolume
;
484 goto ExitThisRoutine
;
487 result
= cleanUnmount
;
489 if (VolumeObjectIsHFSPlus()) {
490 vhp
= (HFSPlusVolumeHeader
*) block
.buffer
;
492 /* Check unmount bit and volume inconsistent bit */
493 if (((vhp
->attributes
& kHFSVolumeUnmountedMask
) == 0) ||
494 (vhp
->attributes
& kHFSVolumeInconsistentMask
))
495 result
= dirtyUnmount
;
497 /* Check last mounted version. If kFSKMountVersion, bad
498 * journal was encountered during mount. Force dirty volume.
501 if (vhp
->lastMountedVersion
== kFSKMountVersion
) {
502 GPtr
->JStat
|= S_BadJournal
;
503 RcdError (GPtr
, E_BadJournal
);
504 result
= dirtyUnmount
;
507 if (operation
== kMarkVolumeDirty
) {
508 /* Mark volume was not unmounted cleanly */
509 if (vhp
->attributes
& kHFSVolumeUnmountedMask
) {
510 vhp
->attributes
&= ~kHFSVolumeUnmountedMask
;
513 /* Mark volume inconsistent */
514 if ((vhp
->attributes
& kHFSVolumeInconsistentMask
) == 0) {
515 vhp
->attributes
|= kHFSVolumeInconsistentMask
;
518 } else if (operation
== kMarkVolumeClean
) {
519 /* Mark volume was unmounted cleanly */
520 if ((vhp
->attributes
& kHFSVolumeUnmountedMask
) == 0) {
521 vhp
->attributes
|= kHFSVolumeUnmountedMask
;
524 /* Mark volume consistent */
525 if (vhp
->attributes
& kHFSVolumeInconsistentMask
) {
526 vhp
->attributes
&= ~kHFSVolumeInconsistentMask
;
531 /* If any changes to VH, update the last mounted version */
532 if (update
== true) {
533 vhp
->lastMountedVersion
= kFSCKMountVersion
;
535 } else if (VolumeObjectIsHFS()) {
536 mdbp
= (HFSMasterDirectoryBlock
*) block
.buffer
;
538 /* Check unmount bit and volume inconsistent bit */
539 if (((mdbp
->drAtrb
& kHFSVolumeUnmountedMask
) == 0) ||
540 (mdbp
->drAtrb
& kHFSVolumeInconsistentMask
))
541 result
= dirtyUnmount
;
543 if (operation
== kMarkVolumeDirty
) {
544 /* Mark volume was not unmounted cleanly */
545 if (mdbp
->drAtrb
& kHFSVolumeUnmountedMask
) {
546 mdbp
->drAtrb
&= ~kHFSVolumeUnmountedMask
;
549 /* Mark volume inconsistent */
550 if ((mdbp
->drAtrb
& kHFSVolumeInconsistentMask
) == 0) {
551 mdbp
->drAtrb
|= kHFSVolumeInconsistentMask
;
554 } else if (operation
== kMarkVolumeClean
) {
555 /* Mark volume was unmounted cleanly */
556 if ((mdbp
->drAtrb
& kHFSVolumeUnmountedMask
) == 0) {
557 mdbp
->drAtrb
|= kHFSVolumeUnmountedMask
;
560 /* Mark volume consistent */
561 if (mdbp
->drAtrb
& kHFSVolumeInconsistentMask
) {
562 mdbp
->drAtrb
&= ~kHFSVolumeInconsistentMask
;
569 if (update
== true) {
571 rbOptions
= kForceWriteBlock
;
572 /* Set appropriate return value */
573 if (operation
== kMarkVolumeDirty
) {
574 result
= dirtyUnmount
;
575 } else if (operation
== kMarkVolumeClean
) {
576 result
= cleanUnmount
;
579 if (block
.buffer
!= NULL
)
580 (void) ReleaseVolumeBlock(vcb
, &block
, rbOptions
);
585 /*------------------------------------------------------------------------------
587 Function: IVChk - (Initial Volume Check)
589 Function: Performs an initial check of the volume to be scavenged to confirm
590 that the volume can be accessed and that it is a HFS/HFS+ volume.
592 Input: GPtr - pointer to scavenger global area
594 Output: IVChk - function result:
597 ------------------------------------------------------------------------------*/
598 #define kBitsPerSector 4096
600 OSErr
IVChk( SGlobPtr GPtr
)
603 HFSMasterDirectoryBlock
* myMDBPtr
;
604 HFSPlusVolumeHeader
* myVHBPtr
;
607 UInt32 maxNumberOfAllocationBlocks
;
608 UInt32 realAllocationBlockSize
;
609 UInt32 realTotalBlocks
;
611 BTreeControlBlock
*btcb
;
612 SVCB
*vcb
= GPtr
->calculatedVCB
;
613 VolumeObjectPtr myVOPtr
;
616 BlockDescriptor myBlockDescriptor
;
619 GPtr
->TarID
= AMDB_FNum
; // target = alt MDB
621 maxNumberOfAllocationBlocks
= 0xFFFFFFFF;
622 realAllocationBlockSize
= 0;
625 myBlockDescriptor
.buffer
= NULL
;
626 myVOPtr
= GetVolumeObjectPtr( );
629 if ( myVOPtr
->totalDeviceSectors
< 3 ) {
630 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
631 plog("\tinvalid device information for volume - total sectors = %qd sector size = %d \n",
632 myVOPtr
->totalDeviceSectors
, myVOPtr
->sectorSize
);
636 GetVolumeObjectBlockNum( &blockNum
);
637 if ( blockNum
== 0 || myVOPtr
->volumeType
== kUnknownVolumeType
) {
638 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
639 plog( "\t%s - unknown volume type \n", __FUNCTION__
);
640 err
= R_BadSig
; /* doesn't bear the HFS signature */
644 // get Volume Header (HFS+) or Master Directory (HFS) block
645 err
= GetVolumeObjectVHBorMDB( &myBlockDescriptor
);
646 if ( err
!= noErr
) {
647 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
648 plog( "\t%s - bad volume header - err %d \n", __FUNCTION__
, err
);
651 myMDBPtr
= (HFSMasterDirectoryBlock
*) myBlockDescriptor
.buffer
;
653 // if this is an HFS (kHFSVolumeType) volume and the MDB indicates this
654 // might contain an embedded HFS+ volume then we need to scan
655 // for an embedded HFS+ volume. I'm told there were some old problems
656 // where we could lose track of the embedded volume.
657 if ( VolumeObjectIsHFS( ) &&
658 (myMDBPtr
->drEmbedSigWord
!= 0 ||
659 myMDBPtr
->drEmbedExtent
.blockCount
!= 0 ||
660 myMDBPtr
->drEmbedExtent
.startBlock
!= 0) ) {
662 err
= ScavengeVolumeType( GPtr
, myMDBPtr
, &myVOPtr
->volumeType
);
663 if ( err
== E_InvalidMDBdrAlBlSt
)
664 err
= RcdMDBEmbededVolDescriptionErr( GPtr
, E_InvalidMDBdrAlBlSt
, myMDBPtr
);
666 if ( VolumeObjectIsEmbeddedHFSPlus( ) ) {
667 // we changed volume types so let's get the VHB
668 (void) ReleaseVolumeBlock( vcb
, &myBlockDescriptor
, kReleaseBlock
);
669 myBlockDescriptor
.buffer
= NULL
;
671 err
= GetVolumeObjectVHB( &myBlockDescriptor
);
672 if ( err
!= noErr
) {
673 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
674 plog( "\t%s - bad volume header - err %d \n", __FUNCTION__
, err
);
675 WriteError( GPtr
, E_InvalidVolumeHeader
, 1, 0 );
676 err
= E_InvalidVolumeHeader
;
680 GetVolumeObjectBlockNum( &blockNum
); // get the new Volume header block number
683 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
684 plog( "\t%s - bad volume header - err %d \n", __FUNCTION__
, err
);
685 WriteError( GPtr
, E_InvalidVolumeHeader
, 1, 0 );
686 err
= E_InvalidVolumeHeader
;
691 totalSectors
= ( VolumeObjectIsEmbeddedHFSPlus( ) ) ? myVOPtr
->totalEmbeddedSectors
: myVOPtr
->totalDeviceSectors
;
693 // indicate what type of volume we are dealing with
694 if ( VolumeObjectIsHFSPlus( ) ) {
696 myVHBPtr
= (HFSPlusVolumeHeader
*) myBlockDescriptor
.buffer
;
697 if (myVHBPtr
->attributes
& kHFSVolumeJournaledMask
) {
698 fsckPrint(GPtr
->context
, hfsJournalVolCheck
);
700 fsckPrint(GPtr
->context
, hfsCheckNoJnl
);
702 GPtr
->numExtents
= kHFSPlusExtentDensity
;
703 vcb
->vcbSignature
= kHFSPlusSigWord
;
705 // Further populate the VCB with VolumeHeader info
706 vcb
->vcbAlBlSt
= myVOPtr
->embeddedOffset
/ 512;
707 vcb
->vcbEmbeddedOffset
= myVOPtr
->embeddedOffset
;
708 realAllocationBlockSize
= myVHBPtr
->blockSize
;
709 realTotalBlocks
= myVHBPtr
->totalBlocks
;
710 vcb
->vcbNextCatalogID
= myVHBPtr
->nextCatalogID
;
711 vcb
->vcbCreateDate
= myVHBPtr
->createDate
;
712 vcb
->vcbAttributes
= myVHBPtr
->attributes
& kHFSCatalogNodeIDsReused
;
714 if ( myVHBPtr
->attributesFile
.totalBlocks
== 0 )
715 vcb
->vcbAttributesFile
= NULL
; /* XXX memory leak ? */
717 // Make sure the Extents B-Tree is set to use 16-bit key lengths.
718 // We access it before completely setting up the control block.
719 btcb
= (BTreeControlBlock
*) vcb
->vcbExtentsFile
->fcbBtree
;
720 btcb
->attributes
|= kBTBigKeysMask
;
722 // catch the case where the volume allocation block count is greater than
723 // maximum number of device allocation blocks. - bug 2916021
724 numABlks
= myVOPtr
->totalDeviceSectors
/ ( myVHBPtr
->blockSize
/ Blk_Size
);
725 if ( myVHBPtr
->totalBlocks
> numABlks
) {
726 RcdError( GPtr
, E_NABlks
);
728 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
729 plog( "\t%s - volume header total allocation blocks is greater than device size \n", __FUNCTION__
);
730 plog( "\tvolume allocation block count %d device allocation block count %d \n",
731 myVHBPtr
->totalBlocks
, numABlks
);
736 else if ( VolumeObjectIsHFS( ) ) {
738 // fsckPrint(GPtr->context, fsckCheckingVolume);
739 fsckPrint(GPtr
->context
, hfsCheckHFS
);
741 GPtr
->numExtents
= kHFSExtentDensity
;
742 vcb
->vcbSignature
= myMDBPtr
->drSigWord
;
743 maxNumberOfAllocationBlocks
= 0xFFFF;
744 // set up next file ID, CheckBTreeKey makse sure we are under this value
745 vcb
->vcbNextCatalogID
= myMDBPtr
->drNxtCNID
;
746 vcb
->vcbCreateDate
= myMDBPtr
->drCrDate
;
748 realAllocationBlockSize
= myMDBPtr
->drAlBlkSiz
;
749 realTotalBlocks
= myMDBPtr
->drNmAlBlks
;
752 GPtr
->TarBlock
= blockNum
; // target block
754 // verify volume allocation info
755 // Note: i is the number of sectors per allocation block
756 numABlks
= totalSectors
;
757 minABlkSz
= Blk_Size
; // init minimum ablock size
758 // loop while #ablocks won't fit
759 for( i
= 2; numABlks
> maxNumberOfAllocationBlocks
; i
++ ) {
760 minABlkSz
= i
* Blk_Size
; // jack up minimum
761 numABlks
= totalSectors
/ i
; // recompute #ablocks, assuming this size
764 vcb
->vcbBlockSize
= realAllocationBlockSize
;
765 numABlks
= totalSectors
/ ( realAllocationBlockSize
/ Blk_Size
);
766 if ( VolumeObjectIsHFSPlus( ) ) {
767 // HFS Plus allocation block size must be power of 2
768 if ( (realAllocationBlockSize
< minABlkSz
) ||
769 (realAllocationBlockSize
& (realAllocationBlockSize
- 1)) != 0 )
770 realAllocationBlockSize
= 0;
773 if ( (realAllocationBlockSize
< minABlkSz
) ||
774 (realAllocationBlockSize
> Max_ABSiz
) ||
775 ((realAllocationBlockSize
% Blk_Size
) != 0) )
776 realAllocationBlockSize
= 0;
779 if ( realAllocationBlockSize
== 0 ) {
780 RcdError( GPtr
, E_ABlkSz
);
781 err
= E_ABlkSz
; // bad allocation block size
785 vcb
->vcbTotalBlocks
= realTotalBlocks
;
786 vcb
->vcbFreeBlocks
= 0;
788 // Only do these tests on HFS volumes, since they are either
789 // or, getting the VolumeHeader would have already failed.
790 if ( VolumeObjectIsHFS( ) ) {
791 UInt32 bitMapSizeInSectors
;
793 // Calculate the volume bitmap size
794 bitMapSizeInSectors
= ( numABlks
+ kBitsPerSector
- 1 ) / kBitsPerSector
; // VBM size in blocks
796 //¥¥ Calculate the validaty of HFS Allocation blocks, I think realTotalBlocks == numABlks
797 numABlks
= (totalSectors
- 3 - bitMapSizeInSectors
) / (realAllocationBlockSize
/ Blk_Size
); // actual # of alloc blks
799 if ( realTotalBlocks
> numABlks
) {
800 RcdError( GPtr
, E_NABlks
);
801 err
= E_NABlks
; // invalid number of allocation blocks
805 if ( myMDBPtr
->drVBMSt
<= MDB_BlkN
) {
806 RcdError(GPtr
,E_VBMSt
);
807 err
= E_VBMSt
; // invalid VBM start block
810 vcb
->vcbVBMSt
= myMDBPtr
->drVBMSt
;
812 if (myMDBPtr
->drAlBlSt
< (myMDBPtr
->drVBMSt
+ bitMapSizeInSectors
)) {
813 RcdError(GPtr
,E_ABlkSt
);
814 err
= E_ABlkSt
; // invalid starting alloc block
817 vcb
->vcbAlBlSt
= myMDBPtr
->drAlBlSt
;
821 if (myBlockDescriptor
.buffer
!= NULL
)
822 (void) ReleaseVolumeBlock(vcb
, &myBlockDescriptor
, kReleaseBlock
);
828 static OSErr
ScavengeVolumeType( SGlobPtr GPtr
, HFSMasterDirectoryBlock
*mdb
, UInt32
*volumeType
)
833 UInt64 hfsPlusSectors
= 0;
834 UInt32 sectorsPerBlock
;
835 UInt32 numSectorsToSearch
;
837 HFSPlusVolumeHeader
*volumeHeader
;
838 HFSExtentDescriptor embededExtent
;
839 SVCB
*calculatedVCB
= GPtr
->calculatedVCB
;
840 VolumeObjectPtr myVOPtr
;
841 UInt16 embedSigWord
= mdb
->drEmbedSigWord
;
842 BlockDescriptor block
;
845 * If all of the embedded volume information is zero, then assume
846 * this really is a plain HFS disk like it says. Otherwise, if
847 * you reinitialize a large HFS Plus volume as HFS, the original
848 * embedded volume's volume header and alternate volume header will
849 * still be there, and we'll try to repair the embedded volume.
851 if (embedSigWord
== 0 &&
852 mdb
->drEmbedExtent
.blockCount
== 0 &&
853 mdb
->drEmbedExtent
.startBlock
== 0)
855 *volumeType
= kHFSVolumeType
;
859 myVOPtr
= GetVolumeObjectPtr( );
860 *volumeType
= kEmbededHFSPlusVolumeType
; // Assume HFS+
863 // First see if it is an HFS+ volume and the relevent structures look OK
865 if ( embedSigWord
== kHFSPlusSigWord
)
867 /* look for primary volume header */
868 vHSector
= (UInt64
)mdb
->drAlBlSt
+
869 ((UInt64
)(mdb
->drAlBlkSiz
/ Blk_Size
) * (UInt64
)mdb
->drEmbedExtent
.startBlock
) + 2;
871 err
= GetVolumeBlock(calculatedVCB
, vHSector
, kGetBlock
, &block
);
872 volumeHeader
= (HFSPlusVolumeHeader
*) block
.buffer
;
873 if ( err
!= noErr
) goto AssumeHFS
;
875 myVOPtr
->primaryVHB
= vHSector
;
876 err
= ValidVolumeHeader( volumeHeader
);
877 (void) ReleaseVolumeBlock(calculatedVCB
, &block
, kReleaseBlock
);
878 if ( err
== noErr
) {
879 myVOPtr
->flags
|= kVO_PriVHBOK
;
884 sectorsPerBlock
= mdb
->drAlBlkSiz
/ Blk_Size
;
886 // Search the end of the disk to see if a Volume Header is present at all
887 if ( embedSigWord
!= kHFSPlusSigWord
)
889 numSectorsToSearch
= mdb
->drAlBlkSiz
/ Blk_Size
;
890 startSector
= myVOPtr
->totalDeviceSectors
- 4 - numSectorsToSearch
;
892 err
= SeekVolumeHeader( GPtr
, startSector
, numSectorsToSearch
, &altVHSector
);
893 if ( err
!= noErr
) goto AssumeHFS
;
895 // We found the Alt VH, so this must be a damaged embeded HFS+ volume
896 // Now Scavenge for the Primary VolumeHeader
897 myVOPtr
->alternateVHB
= altVHSector
;
898 myVOPtr
->flags
|= kVO_AltVHBOK
;
899 startSector
= mdb
->drAlBlSt
+ (4 * sectorsPerBlock
); // Start looking at 4th HFS allocation block
900 numSectorsToSearch
= 10 * sectorsPerBlock
; // search for VH in next 10 allocation blocks
902 err
= SeekVolumeHeader( GPtr
, startSector
, numSectorsToSearch
, &vHSector
);
903 if ( err
!= noErr
) goto AssumeHFS
;
905 myVOPtr
->primaryVHB
= vHSector
;
906 myVOPtr
->flags
|= kVO_PriVHBOK
;
907 hfsPlusSectors
= altVHSector
- vHSector
+ 1 + 2 + 1; // numSectors + BB + end
909 // Fix the embeded extent
910 embededExtent
.blockCount
= hfsPlusSectors
/ sectorsPerBlock
;
911 embededExtent
.startBlock
= (vHSector
- 2 - mdb
->drAlBlSt
) / sectorsPerBlock
;
912 embedSigWord
= kHFSPlusSigWord
;
914 myVOPtr
->embeddedOffset
=
915 (embededExtent
.startBlock
* mdb
->drAlBlkSiz
) + (mdb
->drAlBlSt
* Blk_Size
);
919 embedSigWord
= mdb
->drEmbedSigWord
;
920 embededExtent
.blockCount
= mdb
->drEmbedExtent
.blockCount
;
921 embededExtent
.startBlock
= mdb
->drEmbedExtent
.startBlock
;
924 if ( embedSigWord
== kHFSPlusSigWord
)
926 startSector
= 2 + mdb
->drAlBlSt
+
927 ((UInt64
)embededExtent
.startBlock
* (mdb
->drAlBlkSiz
/ Blk_Size
));
929 err
= SeekVolumeHeader( GPtr
, startSector
, mdb
->drAlBlkSiz
/ Blk_Size
, &vHSector
);
930 if ( err
!= noErr
) goto AssumeHFS
;
932 // Now replace the bad fields and mark the error
933 mdb
->drEmbedExtent
.blockCount
= embededExtent
.blockCount
;
934 mdb
->drEmbedExtent
.startBlock
= embededExtent
.startBlock
;
935 mdb
->drEmbedSigWord
= kHFSPlusSigWord
;
936 mdb
->drAlBlSt
+= vHSector
- startSector
; // Fix the bad field
937 myVOPtr
->totalEmbeddedSectors
= (mdb
->drAlBlkSiz
/ Blk_Size
) * mdb
->drEmbedExtent
.blockCount
;
938 myVOPtr
->embeddedOffset
=
939 (mdb
->drEmbedExtent
.startBlock
* mdb
->drAlBlkSiz
) + (mdb
->drAlBlSt
* Blk_Size
);
940 myVOPtr
->primaryVHB
= vHSector
;
941 myVOPtr
->flags
|= kVO_PriVHBOK
;
943 GPtr
->VIStat
= GPtr
->VIStat
| S_MDB
; // write out our MDB
944 return( E_InvalidMDBdrAlBlSt
);
948 *volumeType
= kHFSVolumeType
;
951 } /* ScavengeVolumeType */
954 static OSErr
SeekVolumeHeader( SGlobPtr GPtr
, UInt64 startSector
, UInt32 numSectors
, UInt64
*vHSector
)
957 HFSPlusVolumeHeader
*volumeHeader
;
958 SVCB
*calculatedVCB
= GPtr
->calculatedVCB
;
959 BlockDescriptor block
;
961 for ( *vHSector
= startSector
; *vHSector
< startSector
+ numSectors
; (*vHSector
)++ )
963 err
= GetVolumeBlock(calculatedVCB
, *vHSector
, kGetBlock
, &block
);
964 volumeHeader
= (HFSPlusVolumeHeader
*) block
.buffer
;
965 if ( err
!= noErr
) return( err
);
967 err
= ValidVolumeHeader(volumeHeader
);
969 (void) ReleaseVolumeBlock(calculatedVCB
, &block
, kReleaseBlock
);
978 #if 0 // not used at this time
979 static OSErr
CheckWrapperExtents( SGlobPtr GPtr
, HFSMasterDirectoryBlock
*mdb
)
983 // See if Norton Disk Doctor 2.0 corrupted the catalog's first extent
984 if ( mdb
->drCTExtRec
[0].startBlock
>= mdb
->drEmbedExtent
.startBlock
)
986 // Fix the field in the in-memory copy, and record the error
987 mdb
->drCTExtRec
[0].startBlock
= mdb
->drXTExtRec
[0].startBlock
+ mdb
->drXTExtRec
[0].blockCount
;
988 GPtr
->VIStat
= GPtr
->VIStat
| S_MDB
; // write out our MDB
989 err
= RcdInvalidWrapperExtents( GPtr
, E_InvalidWrapperExtents
);
996 /*------------------------------------------------------------------------------
998 Function: CreateExtentsBTreeControlBlock
1000 Function: Create the calculated ExtentsBTree Control Block
1002 Input: GPtr - pointer to scavenger global area
1004 Output: - 0 = no error
1006 ------------------------------------------------------------------------------*/
1008 OSErr
CreateExtentsBTreeControlBlock( SGlobPtr GPtr
)
1014 BTreeControlBlock
* btcb
;
1016 BlockDescriptor block
;
1020 isHFSPlus
= VolumeObjectIsHFSPlus( );
1021 GPtr
->TarID
= kHFSExtentsFileID
; // target = extent file
1022 GPtr
->TarBlock
= kHeaderNodeNum
; // target block = header node
1023 vcb
= GPtr
->calculatedVCB
;
1024 btcb
= GPtr
->calculatedExtentsBTCB
;
1025 block
.buffer
= NULL
;
1027 // get Volume Header (HFS+) or Master Directory (HFS) block
1028 err
= GetVolumeObjectVHBorMDB( &block
);
1031 // check out allocation info for the Extents File
1035 HFSPlusVolumeHeader
*volumeHeader
;
1037 volumeHeader
= (HFSPlusVolumeHeader
*) block
.buffer
;
1039 CopyMemory(volumeHeader
->extentsFile
.extents
, GPtr
->calculatedExtentsFCB
->fcbExtents32
, sizeof(HFSPlusExtentRecord
) );
1041 err
= CheckFileExtents( GPtr
, kHFSExtentsFileID
, kDataFork
, NULL
, (void *)GPtr
->calculatedExtentsFCB
->fcbExtents32
, &numABlks
); // check out extent info
1045 if ( volumeHeader
->extentsFile
.totalBlocks
!= numABlks
) // check out the PEOF
1047 RcdError( GPtr
, E_ExtPEOF
);
1050 plog("Extents File totalBlocks = %u, numABlks = %u\n", volumeHeader
->extentsFile
.totalBlocks
, numABlks
);
1055 GPtr
->calculatedExtentsFCB
->fcbLogicalSize
= volumeHeader
->extentsFile
.logicalSize
; // Set Extents tree's LEOF
1056 GPtr
->calculatedExtentsFCB
->fcbPhysicalSize
= (UInt64
)volumeHeader
->extentsFile
.totalBlocks
*
1057 (UInt64
)volumeHeader
->blockSize
; // Set Extents tree's PEOF
1061 // Set up the minimal BTreeControlBlock structure
1064 // Read the BTreeHeader from disk & also validate it's node size.
1065 err
= GetBTreeHeader(GPtr
, GPtr
->calculatedExtentsFCB
, &header
);
1068 btcb
->maxKeyLength
= kHFSPlusExtentKeyMaximumLength
; // max key length
1069 btcb
->keyCompareProc
= (void *)CompareExtentKeysPlus
;
1070 btcb
->attributes
|=kBTBigKeysMask
; // HFS+ Extent files have 16-bit key length
1071 btcb
->leafRecords
= header
.leafRecords
;
1072 btcb
->treeDepth
= header
.treeDepth
;
1073 btcb
->rootNode
= header
.rootNode
;
1074 btcb
->firstLeafNode
= header
.firstLeafNode
;
1075 btcb
->lastLeafNode
= header
.lastLeafNode
;
1077 btcb
->nodeSize
= header
.nodeSize
;
1078 btcb
->totalNodes
= ( GPtr
->calculatedExtentsFCB
->fcbPhysicalSize
/ btcb
->nodeSize
);
1079 btcb
->freeNodes
= btcb
->totalNodes
; // start with everything free
1081 // Make sure the header nodes size field is correct by looking at the 1st record offset
1082 err
= CheckNodesFirstOffset( GPtr
, btcb
);
1083 if ( (err
!= noErr
) && (btcb
->nodeSize
!= 1024) ) // default HFS+ Extents node size is 1024
1085 btcb
->nodeSize
= 1024;
1086 btcb
->totalNodes
= ( GPtr
->calculatedExtentsFCB
->fcbPhysicalSize
/ btcb
->nodeSize
);
1087 btcb
->freeNodes
= btcb
->totalNodes
; // start with everything free
1089 err
= CheckNodesFirstOffset( GPtr
, btcb
);
1092 GPtr
->EBTStat
|= S_BTH
; // update the Btree header
1097 HFSMasterDirectoryBlock
*alternateMDB
;
1099 alternateMDB
= (HFSMasterDirectoryBlock
*) block
.buffer
;
1101 CopyMemory(alternateMDB
->drXTExtRec
, GPtr
->calculatedExtentsFCB
->fcbExtents16
, sizeof(HFSExtentRecord
) );
1102 // ExtDataRecToExtents(alternateMDB->drXTExtRec, GPtr->calculatedExtentsFCB->fcbExtents);
1105 err
= CheckFileExtents( GPtr
, kHFSExtentsFileID
, kDataFork
, NULL
, (void *)GPtr
->calculatedExtentsFCB
->fcbExtents16
, &numABlks
); /* check out extent info */
1108 if (alternateMDB
->drXTFlSize
!= ((UInt64
)numABlks
* (UInt64
)GPtr
->calculatedVCB
->vcbBlockSize
))// check out the PEOF
1110 RcdError(GPtr
,E_ExtPEOF
);
1113 plog("Alternate MDB drXTFlSize = %llu, should be %llu\n", (long long)alternateMDB
->drXTFlSize
, (long long)numABlks
* (UInt64
)GPtr
->calculatedVCB
->vcbBlockSize
);
1118 GPtr
->calculatedExtentsFCB
->fcbPhysicalSize
= alternateMDB
->drXTFlSize
; // set up PEOF and EOF in FCB
1119 GPtr
->calculatedExtentsFCB
->fcbLogicalSize
= GPtr
->calculatedExtentsFCB
->fcbPhysicalSize
;
1123 // Set up the minimal BTreeControlBlock structure
1126 // Read the BTreeHeader from disk & also validate it's node size.
1127 err
= GetBTreeHeader(GPtr
, GPtr
->calculatedExtentsFCB
, &header
);
1130 btcb
->maxKeyLength
= kHFSExtentKeyMaximumLength
; // max key length
1131 btcb
->keyCompareProc
= (void *)CompareExtentKeys
;
1132 btcb
->leafRecords
= header
.leafRecords
;
1133 btcb
->treeDepth
= header
.treeDepth
;
1134 btcb
->rootNode
= header
.rootNode
;
1135 btcb
->firstLeafNode
= header
.firstLeafNode
;
1136 btcb
->lastLeafNode
= header
.lastLeafNode
;
1138 btcb
->nodeSize
= header
.nodeSize
;
1139 btcb
->totalNodes
= (GPtr
->calculatedExtentsFCB
->fcbPhysicalSize
/ btcb
->nodeSize
);
1140 btcb
->freeNodes
= btcb
->totalNodes
; // start with everything free
1142 // Make sure the header nodes size field is correct by looking at the 1st record offset
1143 err
= CheckNodesFirstOffset( GPtr
, btcb
);
1147 if ( header
.btreeType
!= kHFSBTreeType
)
1149 GPtr
->EBTStat
|= S_ReservedBTH
; // Repair reserved fields in Btree header
1153 // set up our DFA extended BTCB area. Will we have enough memory on all HFS+ volumes.
1155 btcb
->refCon
= AllocateClearMemory( sizeof(BTreeExtensionsRec
) ); // allocate space for our BTCB extensions
1156 if ( btcb
->refCon
== nil
) {
1160 size
= (btcb
->totalNodes
+ 7) / 8; // size of BTree bit map
1161 ((BTreeExtensionsRec
*)btcb
->refCon
)->BTCBMPtr
= AllocateClearMemory(size
); // get precleared bitmap
1162 if ( ((BTreeExtensionsRec
*)btcb
->refCon
)->BTCBMPtr
== nil
)
1168 ((BTreeExtensionsRec
*)btcb
->refCon
)->BTCBMSize
= size
; // remember how long it is
1169 ((BTreeExtensionsRec
*)btcb
->refCon
)->realFreeNodeCount
= header
.freeNodes
;// keep track of real free nodes for progress
1171 if ( block
.buffer
!= NULL
)
1172 (void) ReleaseVolumeBlock(vcb
, &block
, kReleaseBlock
);
1179 /*------------------------------------------------------------------------------
1181 Function: CheckNodesFirstOffset
1183 Function: Minimal check verifies that the 1st offset is within bounds. If it's not
1184 the nodeSize may be wrong. In the future this routine could be modified
1185 to try different size values until one fits.
1187 ------------------------------------------------------------------------------*/
1188 #define GetRecordOffset(btreePtr,node,index) (*(short *) ((UInt8 *)(node) + (btreePtr)->nodeSize - ((index) << 1) - kOffsetSize))
1189 static OSErr
CheckNodesFirstOffset( SGlobPtr GPtr
, BTreeControlBlock
*btcb
)
1195 (void) SetFileBlockSize(btcb
->fcbPtr
, btcb
->nodeSize
);
1197 err
= GetNode( btcb
, kHeaderNodeNum
, &nodeRec
);
1201 offset
= GetRecordOffset( btcb
, (NodeDescPtr
)nodeRec
.buffer
, 0 );
1202 if ( (offset
< sizeof (BTNodeDescriptor
)) || // offset < minimum
1203 (offset
& 1) || // offset is odd
1204 (offset
>= btcb
->nodeSize
) ) // offset beyond end of node
1206 if (debug
) fprintf(stderr
, "%s(%d): offset is wrong\n", __FUNCTION__
, __LINE__
);
1207 err
= fsBTInvalidNodeErr
;
1212 RcdError( GPtr
, E_InvalidNodeSize
);
1214 (void) ReleaseNode(btcb
, &nodeRec
);
1221 /*------------------------------------------------------------------------------
1223 Function: ExtBTChk - (Extent BTree Check)
1225 Function: Verifies the extent BTree structure.
1227 Input: GPtr - pointer to scavenger global area
1229 Output: ExtBTChk - function result:
1232 ------------------------------------------------------------------------------*/
1234 OSErr
ExtBTChk( SGlobPtr GPtr
)
1239 GPtr
->TarID
= kHFSExtentsFileID
; // target = extent file
1240 GetVolumeObjectBlockNum( &GPtr
->TarBlock
); // target block = VHB/MDB
1243 // check out the BTree structure
1246 err
= BTCheck(GPtr
, kCalculatedExtentRefNum
, NULL
);
1247 ReturnIfError( err
); // invalid extent file BTree
1250 // check out the allocation map structure
1253 err
= BTMapChk( GPtr
, kCalculatedExtentRefNum
);
1254 ReturnIfError( err
); // Invalid extent BTree map
1257 // Make sure unused nodes in the B-tree are zero filled.
1259 err
= BTCheckUnusedNodes(GPtr
, kCalculatedExtentRefNum
, &GPtr
->EBTStat
);
1260 ReturnIfError( err
);
1263 // compare BTree header record on disk with scavenger's BTree header record
1266 err
= CmpBTH( GPtr
, kCalculatedExtentRefNum
);
1267 ReturnIfError( err
);
1270 // compare BTree map on disk with scavenger's BTree map
1273 err
= CmpBTM( GPtr
, kCalculatedExtentRefNum
);
1280 /*------------------------------------------------------------------------------
1282 Function: BadBlockFileExtentCheck - (Check extents of bad block file)
1285 Verifies the extents of bad block file (kHFSBadBlockFileID) that
1286 exist in extents Btree.
1288 Note that the extents for other file IDs < kHFSFirstUserCatalogNodeID
1289 are being taken care in the following functions:
1291 kHFSExtentsFileID - CreateExtentsBTreeControlBlock
1292 kHFSCatalogFileID - CreateCatalogBTreeControlBlock
1293 kHFSAllocationFileID - CreateExtendedAllocationsFCB
1294 kHFSStartupFileID - CreateExtendedAllocationsFCB
1295 kHFSAttributesFileID - CreateAttributesBTreeControlBlock
1297 Input: GPtr - pointer to scavenger global area
1299 Output: BadBlockFileExtentCheck - function result:
1302 ------------------------------------------------------------------------------*/
1304 OSErr
BadBlockFileExtentCheck( SGlobPtr GPtr
)
1311 BlockDescriptor block
;
1313 isHFSPlus
= VolumeObjectIsHFSPlus( );
1314 block
.buffer
= NULL
;
1317 // process the bad block extents (created by the disk init pkg to hide badspots)
1319 vcb
= GPtr
->calculatedVCB
;
1321 result
= GetVolumeObjectVHBorMDB( &block
);
1322 if ( result
!= noErr
) goto ExitThisRoutine
; // error, could't get it
1324 p
= (void *) block
.buffer
;
1325 attributes
= isHFSPlus
== true ? ((HFSPlusVolumeHeader
*)p
)->attributes
: ((HFSMasterDirectoryBlock
*)p
)->drAtrb
;
1327 //¥¥ Does HFS+ honnor the same mask?
1328 if ( attributes
& kHFSVolumeSparedBlocksMask
) // if any badspots
1330 HFSPlusExtentRecord zeroXdr
; // dummy passed to 'CheckFileExtents'
1331 UInt32 numBadBlocks
;
1333 ClearMemory ( zeroXdr
, sizeof( HFSPlusExtentRecord
) );
1334 result
= CheckFileExtents( GPtr
, kHFSBadBlockFileID
, kDataFork
, NULL
, (void *)zeroXdr
, &numBadBlocks
); // check and mark bitmap
1338 if ( block
.buffer
!= NULL
)
1339 (void) ReleaseVolumeBlock(vcb
, &block
, kReleaseBlock
);
1345 /*------------------------------------------------------------------------------
1347 Function: CreateCatalogBTreeControlBlock
1349 Function: Create the calculated CatalogBTree Control Block
1351 Input: GPtr - pointer to scavenger global area
1353 Output: - 0 = no error
1355 ------------------------------------------------------------------------------*/
1356 OSErr
CreateCatalogBTreeControlBlock( SGlobPtr GPtr
)
1362 BTreeControlBlock
* btcb
;
1364 BlockDescriptor block
;
1368 isHFSPlus
= VolumeObjectIsHFSPlus( );
1369 GPtr
->TarID
= kHFSCatalogFileID
;
1370 GPtr
->TarBlock
= kHeaderNodeNum
;
1371 vcb
= GPtr
->calculatedVCB
;
1372 btcb
= GPtr
->calculatedCatalogBTCB
;
1373 block
.buffer
= NULL
;
1375 err
= GetVolumeObjectVHBorMDB( &block
);
1376 if ( err
!= noErr
) goto ExitThisRoutine
; // error, could't get it
1378 // check out allocation info for the Catalog File
1382 HFSPlusVolumeHeader
* volumeHeader
;
1384 volumeHeader
= (HFSPlusVolumeHeader
*) block
.buffer
;
1386 CopyMemory(volumeHeader
->catalogFile
.extents
, GPtr
->calculatedCatalogFCB
->fcbExtents32
, sizeof(HFSPlusExtentRecord
) );
1388 err
= CheckFileExtents( GPtr
, kHFSCatalogFileID
, kDataFork
, NULL
, (void *)GPtr
->calculatedCatalogFCB
->fcbExtents32
, &numABlks
);
1391 if ( volumeHeader
->catalogFile
.totalBlocks
!= numABlks
)
1393 RcdError( GPtr
, E_CatPEOF
);
1399 GPtr
->calculatedCatalogFCB
->fcbLogicalSize
= volumeHeader
->catalogFile
.logicalSize
;
1400 GPtr
->calculatedCatalogFCB
->fcbPhysicalSize
= (UInt64
)volumeHeader
->catalogFile
.totalBlocks
*
1401 (UInt64
)volumeHeader
->blockSize
;
1405 // Set up the minimal BTreeControlBlock structure
1408 // read the BTreeHeader from disk & also validate it's node size.
1409 err
= GetBTreeHeader(GPtr
, GPtr
->calculatedCatalogFCB
, &header
);
1412 btcb
->maxKeyLength
= kHFSPlusCatalogKeyMaximumLength
; // max key length
1415 * Figure out the type of key string compare
1416 * (case-insensitive or case-sensitive)
1418 * To do: should enforce an "HX" volume is require for kHFSBinaryCompare.
1420 if (header
.keyCompareType
== kHFSBinaryCompare
)
1422 btcb
->keyCompareProc
= (void *)CaseSensitiveCatalogKeyCompare
;
1423 fsckPrint(GPtr
->context
, hfsCaseSensitive
);
1427 btcb
->keyCompareProc
= (void *)CompareExtendedCatalogKeys
;
1429 btcb
->keyCompareType
= header
.keyCompareType
;
1430 btcb
->leafRecords
= header
.leafRecords
;
1431 btcb
->nodeSize
= header
.nodeSize
;
1432 btcb
->totalNodes
= ( GPtr
->calculatedCatalogFCB
->fcbPhysicalSize
/ btcb
->nodeSize
);
1433 btcb
->freeNodes
= btcb
->totalNodes
; // start with everything free
1434 btcb
->attributes
|=(kBTBigKeysMask
+ kBTVariableIndexKeysMask
); // HFS+ Catalog files have large, variable-sized keys
1436 btcb
->treeDepth
= header
.treeDepth
;
1437 btcb
->rootNode
= header
.rootNode
;
1438 btcb
->firstLeafNode
= header
.firstLeafNode
;
1439 btcb
->lastLeafNode
= header
.lastLeafNode
;
1442 // Make sure the header nodes size field is correct by looking at the 1st record offset
1443 err
= CheckNodesFirstOffset( GPtr
, btcb
);
1444 if ( (err
!= noErr
) && (btcb
->nodeSize
!= 4096) ) // default HFS+ Catalog node size is 4096
1446 btcb
->nodeSize
= 4096;
1447 btcb
->totalNodes
= ( GPtr
->calculatedCatalogFCB
->fcbPhysicalSize
/ btcb
->nodeSize
);
1448 btcb
->freeNodes
= btcb
->totalNodes
; // start with everything free
1450 err
= CheckNodesFirstOffset( GPtr
, btcb
);
1453 GPtr
->CBTStat
|= S_BTH
; // update the Btree header
1458 HFSMasterDirectoryBlock
*alternateMDB
;
1460 alternateMDB
= (HFSMasterDirectoryBlock
*) block
.buffer
;
1462 CopyMemory( alternateMDB
->drCTExtRec
, GPtr
->calculatedCatalogFCB
->fcbExtents16
, sizeof(HFSExtentRecord
) );
1463 // ExtDataRecToExtents(alternateMDB->drCTExtRec, GPtr->calculatedCatalogFCB->fcbExtents);
1465 err
= CheckFileExtents( GPtr
, kHFSCatalogFileID
, kDataFork
, NULL
, (void *)GPtr
->calculatedCatalogFCB
->fcbExtents16
, &numABlks
); /* check out extent info */
1468 if (alternateMDB
->drCTFlSize
!= ((UInt64
)numABlks
* (UInt64
)vcb
->vcbBlockSize
)) // check out the PEOF
1470 RcdError( GPtr
, E_CatPEOF
);
1476 GPtr
->calculatedCatalogFCB
->fcbPhysicalSize
= alternateMDB
->drCTFlSize
; // set up PEOF and EOF in FCB
1477 GPtr
->calculatedCatalogFCB
->fcbLogicalSize
= GPtr
->calculatedCatalogFCB
->fcbPhysicalSize
;
1481 // Set up the minimal BTreeControlBlock structure
1484 // read the BTreeHeader from disk & also validate it's node size.
1485 err
= GetBTreeHeader(GPtr
, GPtr
->calculatedCatalogFCB
, &header
);
1488 btcb
->maxKeyLength
= kHFSCatalogKeyMaximumLength
; // max key length
1489 btcb
->keyCompareProc
= (void *) CompareCatalogKeys
;
1490 btcb
->leafRecords
= header
.leafRecords
;
1491 btcb
->nodeSize
= header
.nodeSize
;
1492 btcb
->totalNodes
= (GPtr
->calculatedCatalogFCB
->fcbPhysicalSize
/ btcb
->nodeSize
);
1493 btcb
->freeNodes
= btcb
->totalNodes
; // start with everything free
1495 btcb
->treeDepth
= header
.treeDepth
;
1496 btcb
->rootNode
= header
.rootNode
;
1497 btcb
->firstLeafNode
= header
.firstLeafNode
;
1498 btcb
->lastLeafNode
= header
.lastLeafNode
;
1500 // Make sure the header nodes size field is correct by looking at the 1st record offset
1501 err
= CheckNodesFirstOffset( GPtr
, btcb
);
1505 plog(" Catalog B-tree is %qd bytes\n", (UInt64
)btcb
->totalNodes
* (UInt64
) btcb
->nodeSize
);
1508 if ( header
.btreeType
!= kHFSBTreeType
)
1510 GPtr
->CBTStat
|= S_ReservedBTH
; // Repair reserved fields in Btree header
1514 // set up our DFA extended BTCB area. Will we have enough memory on all HFS+ volumes.
1517 btcb
->refCon
= AllocateClearMemory( sizeof(BTreeExtensionsRec
) ); // allocate space for our BTCB extensions
1518 if ( btcb
->refCon
== nil
) {
1522 size
= (btcb
->totalNodes
+ 7) / 8; // size of BTree bit map
1523 ((BTreeExtensionsRec
*)btcb
->refCon
)->BTCBMPtr
= AllocateClearMemory(size
); // get precleared bitmap
1524 if ( ((BTreeExtensionsRec
*)btcb
->refCon
)->BTCBMPtr
== nil
)
1530 ((BTreeExtensionsRec
*)btcb
->refCon
)->BTCBMSize
= size
; // remember how long it is
1531 ((BTreeExtensionsRec
*)btcb
->refCon
)->realFreeNodeCount
= header
.freeNodes
; // keep track of real free nodes for progress
1533 /* it should be OK at this point to get volume name and stuff it into our global */
1538 CatalogRecord record
;
1540 BuildCatalogKey( kHFSRootFolderID
, NULL
, isHFSPlus
, &key
);
1541 result
= SearchBTreeRecord( GPtr
->calculatedCatalogFCB
, &key
, kNoHint
, NULL
, &record
, &recSize
, NULL
);
1542 if ( result
== noErr
) {
1545 HFSPlusCatalogThread
* recPtr
= &record
.hfsPlusThread
;
1546 (void) utf_encodestr( recPtr
->nodeName
.unicode
,
1547 recPtr
->nodeName
.length
* 2,
1548 GPtr
->volumeName
, &len
, sizeof(GPtr
->volumeName
) );
1549 GPtr
->volumeName
[len
] = '\0';
1552 HFSCatalogThread
* recPtr
= &record
.hfsThread
;
1553 bcopy( &recPtr
->nodeName
[1], GPtr
->volumeName
, recPtr
->nodeName
[0] );
1554 GPtr
->volumeName
[ recPtr
->nodeName
[0] ] = '\0';
1556 fsckPrint(GPtr
->context
, fsckVolumeName
, GPtr
->volumeName
);
1562 if ( block
.buffer
!= NULL
)
1563 (void) ReleaseVolumeBlock(vcb
, &block
, kReleaseBlock
);
1569 /*------------------------------------------------------------------------------
1571 Function: CreateExtendedAllocationsFCB
1573 Function: Create the calculated ExtentsBTree Control Block for
1574 kHFSAllocationFileID and kHFSStartupFileID.
1576 Input: GPtr - pointer to scavenger global area
1578 Output: - 0 = no error
1580 ------------------------------------------------------------------------------*/
1581 OSErr
CreateExtendedAllocationsFCB( SGlobPtr GPtr
)
1587 BlockDescriptor block
;
1590 isHFSPlus
= VolumeObjectIsHFSPlus( );
1591 GPtr
->TarID
= kHFSAllocationFileID
;
1592 GetVolumeObjectBlockNum( &GPtr
->TarBlock
); // target block = VHB/MDB
1593 vcb
= GPtr
->calculatedVCB
;
1594 block
.buffer
= NULL
;
1597 // check out allocation info for the allocation File
1603 HFSPlusVolumeHeader
*volumeHeader
;
1605 err
= GetVolumeObjectVHB( &block
);
1608 volumeHeader
= (HFSPlusVolumeHeader
*) block
.buffer
;
1610 fcb
= GPtr
->calculatedAllocationsFCB
;
1611 CopyMemory( volumeHeader
->allocationFile
.extents
, fcb
->fcbExtents32
, sizeof(HFSPlusExtentRecord
) );
1613 err
= CheckFileExtents( GPtr
, kHFSAllocationFileID
, kDataFork
, NULL
, (void *)fcb
->fcbExtents32
, &numABlks
);
1617 // The allocation file will get processed in whole allocation blocks, or
1618 // maximal-sized cache blocks, whichever is smaller. This means the cache
1619 // doesn't need to cope with buffers that are larger than a cache block.
1620 if (vcb
->vcbBlockSize
< fscache
.BlockSize
)
1621 (void) SetFileBlockSize (fcb
, vcb
->vcbBlockSize
);
1623 (void) SetFileBlockSize (fcb
, fscache
.BlockSize
);
1625 if ( volumeHeader
->allocationFile
.totalBlocks
!= numABlks
)
1627 RcdError( GPtr
, E_CatPEOF
);
1633 fcb
->fcbLogicalSize
= volumeHeader
->allocationFile
.logicalSize
;
1634 fcb
->fcbPhysicalSize
= (UInt64
) volumeHeader
->allocationFile
.totalBlocks
*
1635 (UInt64
) volumeHeader
->blockSize
;
1638 /* while we're here, also get startup file extents... */
1639 fcb
= GPtr
->calculatedStartupFCB
;
1640 CopyMemory( volumeHeader
->startupFile
.extents
, fcb
->fcbExtents32
, sizeof(HFSPlusExtentRecord
) );
1642 err
= CheckFileExtents( GPtr
, kHFSStartupFileID
, kDataFork
, NULL
, (void *)fcb
->fcbExtents32
, &numABlks
);
1645 fcb
->fcbLogicalSize
= volumeHeader
->startupFile
.logicalSize
;
1646 fcb
->fcbPhysicalSize
= (UInt64
) volumeHeader
->startupFile
.totalBlocks
*
1647 (UInt64
) volumeHeader
->blockSize
;
1652 (void) ReleaseVolumeBlock(vcb
, &block
, kReleaseBlock
);
1659 /*------------------------------------------------------------------------------
1661 Function: CatHChk - (Catalog Hierarchy Check)
1663 Function: Verifies the catalog hierarchy.
1665 Input: GPtr - pointer to scavenger global area
1667 Output: CatHChk - function result:
1670 ------------------------------------------------------------------------------*/
1672 OSErr
CatHChk( SGlobPtr GPtr
)
1683 SVCB
*calculatedVCB
;
1686 CatalogKey foundKey
;
1687 Boolean validKeyFound
;
1689 CatalogRecord record
;
1690 CatalogRecord record2
;
1691 HFSPlusCatalogFolder
*largeCatalogFolderP
;
1692 HFSPlusCatalogFile
*largeCatalogFileP
;
1693 HFSCatalogFile
*smallCatalogFileP
;
1694 HFSCatalogFolder
*smallCatalogFolderP
;
1695 CatalogName catalogName
;
1697 CatalogRecord threadRecord
;
1698 HFSCatalogNodeID parID
;
1702 isHFSPlus
= VolumeObjectIsHFSPlus( );
1703 calculatedVCB
= GPtr
->calculatedVCB
;
1704 GPtr
->TarID
= kHFSCatalogFileID
; /* target = catalog file */
1705 GPtr
->TarBlock
= 0; /* no target block yet */
1708 // position to the beginning of catalog
1711 //¥¥ Can we ignore this part by just taking advantage of setting the selCode = 0x8001;
1713 BuildCatalogKey( 1, (const CatalogName
*)nil
, isHFSPlus
, &key
);
1714 result
= SearchBTreeRecord( GPtr
->calculatedCatalogFCB
, &key
, kNoHint
, &foundKey
, &threadRecord
, &recSize
, &hint
);
1716 GPtr
->TarBlock
= hint
; /* set target block */
1717 if ( result
!= btNotFound
)
1719 RcdError( GPtr
, E_CatRec
);
1725 dprP
= &(GPtr
->DirPTPtr
)[0];
1726 dprP
->directoryID
= 1;
1728 dirCnt
= filCnt
= rtdirCnt
= rtfilCnt
= 0;
1731 selCode
= 0x8001; /* start with root directory */
1734 // enumerate the entire catalog
1736 while ( (GPtr
->DirLevel
> 0) && (result
== noErr
) )
1738 dprP
= &(GPtr
->DirPTPtr
)[GPtr
->DirLevel
- 1];
1740 validKeyFound
= true;
1741 record
.recordType
= 0;
1743 // get the next record
1744 result
= GetBTreeRecord( GPtr
->calculatedCatalogFCB
, selCode
, &foundKey
, &record
, &recSize
, &hint
);
1746 GPtr
->TarBlock
= hint
; /* set target block */
1747 if ( result
!= noErr
)
1749 if ( result
== btNotFound
)
1752 validKeyFound
= false;
1756 result
= IntError( GPtr
, result
); /* error from BTGetRecord */
1760 selCode
= 1; /* get next rec from now on */
1762 GPtr
->itemsProcessed
++;
1765 // if same ParID ...
1767 parID
= isHFSPlus
== true ? foundKey
.hfsPlus
.parentID
: foundKey
.hfs
.parentID
;
1768 if ( (validKeyFound
== true) && (parID
== dprP
->directoryID
) )
1770 dprP
->offspringIndex
++; /* increment offspring index */
1772 // if new directory ...
1774 if ( record
.recordType
== kHFSPlusFolderRecord
)
1776 result
= CheckForStop( GPtr
); ReturnIfError( result
); // Permit the user to interrupt
1778 largeCatalogFolderP
= (HFSPlusCatalogFolder
*) &record
;
1779 GPtr
->TarID
= largeCatalogFolderP
->folderID
; // target ID = directory ID
1780 GPtr
->CNType
= record
.recordType
; // target CNode type = directory ID
1781 CopyCatalogName( (const CatalogName
*) &foundKey
.hfsPlus
.nodeName
, &GPtr
->CName
, isHFSPlus
);
1783 if ( dprP
->directoryID
> 1 )
1785 GPtr
->DirLevel
++; // we have a new directory level
1788 if ( dprP
->directoryID
== kHFSRootFolderID
) // bump root dir count
1791 if ( GPtr
->DirLevel
> GPtr
->dirPathCount
)
1795 ptr
= realloc(GPtr
->DirPTPtr
, (GPtr
->dirPathCount
+ CMMaxDepth
) * sizeof(SDPR
));
1798 fsckPrint(GPtr
->context
, E_CatDepth
, GPtr
->dirPathCount
);
1799 return noErr
; /* abort this check, but let other checks proceed */
1801 ClearMemory((char *)ptr
+ (GPtr
->dirPathCount
* sizeof(SDPR
)), (CMMaxDepth
* sizeof(SDPR
)));
1802 GPtr
->dirPathCount
+= CMMaxDepth
;
1803 GPtr
->DirPTPtr
= ptr
;
1806 dprP
= &(GPtr
->DirPTPtr
)[GPtr
->DirLevel
- 1];
1807 dprP
->directoryID
= largeCatalogFolderP
->folderID
;
1808 dprP
->offspringIndex
= 1;
1809 dprP
->directoryHint
= hint
;
1810 dprP
->parentDirID
= foundKey
.hfsPlus
.parentID
;
1811 CopyCatalogName( (const CatalogName
*) &foundKey
.hfsPlus
.nodeName
, &dprP
->directoryName
, isHFSPlus
);
1813 for ( i
= 1; i
< GPtr
->DirLevel
; i
++ )
1815 dprP1
= &(GPtr
->DirPTPtr
)[i
- 1];
1816 if (dprP
->directoryID
== dprP1
->directoryID
)
1818 RcdError( GPtr
,E_DirLoop
); // loop in directory hierarchy
1819 return( E_DirLoop
);
1824 * Find thread record
1826 BuildCatalogKey( dprP
->directoryID
, (const CatalogName
*) nil
, isHFSPlus
, &key
);
1827 result
= SearchBTreeRecord( GPtr
->calculatedCatalogFCB
, &key
, kNoHint
, &foundKey
, &threadRecord
, &recSize
, &hint
);
1828 if ( result
!= noErr
) {
1829 struct MissingThread
*mtp
;
1831 /* Report the error */
1832 fsckPrint(GPtr
->context
, E_NoThd
, dprP
->directoryID
);
1834 /* HFS will exit here */
1838 * A directory thread is missing. If we can find this
1839 * ID on the missing-thread list then we know where the
1840 * child entries reside and can resume our enumeration.
1842 for (mtp
= GPtr
->missingThreadList
; mtp
!= NULL
; mtp
= mtp
->link
) {
1843 if (mtp
->threadID
== dprP
->directoryID
) {
1844 mtp
->thread
.recordType
= kHFSPlusFolderThreadRecord
;
1845 mtp
->thread
.parentID
= dprP
->parentDirID
;
1846 CopyCatalogName(&dprP
->directoryName
, (CatalogName
*)&mtp
->thread
.nodeName
, isHFSPlus
);
1848 /* Reposition to the first child of target directory */
1849 result
= SearchBTreeRecord(GPtr
->calculatedCatalogFCB
, &mtp
->nextKey
,
1850 kNoHint
, &foundKey
, &threadRecord
, &recSize
, &hint
);
1854 selCode
= 0; /* use current record instead of next */
1860 * A directory thread is missing but we know this
1861 * directory has no children (since we didn't find
1862 * its ID on the missing-thread list above).
1864 * At this point we can resume the enumeration at
1865 * our previous position in our parent directory.
1867 goto resumeAtParent
;
1870 dprP
->threadHint
= hint
;
1871 GPtr
->TarBlock
= hint
;
1875 else if ( record
.recordType
== kHFSPlusFileRecord
)
1877 largeCatalogFileP
= (HFSPlusCatalogFile
*) &record
;
1878 GPtr
->TarID
= largeCatalogFileP
->fileID
; // target ID = file number
1879 GPtr
->CNType
= record
.recordType
; // target CNode type = thread
1880 CopyCatalogName( (const CatalogName
*) &foundKey
.hfsPlus
.nodeName
, &GPtr
->CName
, isHFSPlus
);
1882 if (dprP
->directoryID
== kHFSRootFolderID
)
1886 else if ( record
.recordType
== kHFSFolderRecord
)
1888 result
= CheckForStop( GPtr
); ReturnIfError( result
); // Permit the user to interrupt
1890 smallCatalogFolderP
= (HFSCatalogFolder
*) &record
;
1891 GPtr
->TarID
= smallCatalogFolderP
->folderID
; /* target ID = directory ID */
1892 GPtr
->CNType
= record
.recordType
; /* target CNode type = directory ID */
1893 CopyCatalogName( (const CatalogName
*) &key
.hfs
.nodeName
, &GPtr
->CName
, isHFSPlus
); /* target CName = directory name */
1895 if (dprP
->directoryID
> 1)
1897 GPtr
->DirLevel
++; /* we have a new directory level */
1900 if (dprP
->directoryID
== kHFSRootFolderID
) /* bump root dir count */
1903 if ( GPtr
->DirLevel
> GPtr
->dirPathCount
)
1907 ptr
= realloc(GPtr
->DirPTPtr
, (GPtr
->dirPathCount
+ CMMaxDepth
) * sizeof(SDPR
));
1910 fsckPrint(GPtr
->context
, E_CatDepth
, GPtr
->dirPathCount
);
1911 return noErr
; /* abort this check, but let other checks proceed */
1913 ClearMemory((char *)ptr
+ (GPtr
->dirPathCount
* sizeof(SDPR
)), (CMMaxDepth
* sizeof(SDPR
)));
1914 GPtr
->dirPathCount
+= CMMaxDepth
;
1915 GPtr
->DirPTPtr
= ptr
;
1918 dprP
= &(GPtr
->DirPTPtr
)[GPtr
->DirLevel
- 1];
1919 dprP
->directoryID
= smallCatalogFolderP
->folderID
;
1920 dprP
->offspringIndex
= 1;
1921 dprP
->directoryHint
= hint
;
1922 dprP
->parentDirID
= foundKey
.hfs
.parentID
;
1924 CopyCatalogName( (const CatalogName
*) &foundKey
.hfs
.nodeName
, &dprP
->directoryName
, isHFSPlus
);
1926 for (i
= 1; i
< GPtr
->DirLevel
; i
++)
1928 dprP1
= &(GPtr
->DirPTPtr
)[i
- 1];
1929 if (dprP
->directoryID
== dprP1
->directoryID
)
1931 RcdError( GPtr
,E_DirLoop
); /* loop in directory hierarchy */
1932 return( E_DirLoop
);
1936 BuildCatalogKey( dprP
->directoryID
, (const CatalogName
*)0, isHFSPlus
, &key
);
1937 result
= SearchBTreeRecord( GPtr
->calculatedCatalogFCB
, &key
, kNoHint
, &foundKey
, &threadRecord
, &recSize
, &hint
);
1938 if (result
!= noErr
)
1940 result
= IntError(GPtr
,result
); /* error from BTSearch */
1943 dprP
->threadHint
= hint
; /* save hint for thread */
1944 GPtr
->TarBlock
= hint
; /* set target block */
1947 // HFSCatalogFile...
1948 else if ( record
.recordType
== kHFSFileRecord
)
1950 smallCatalogFileP
= (HFSCatalogFile
*) &record
;
1951 GPtr
->TarID
= smallCatalogFileP
->fileID
; /* target ID = file number */
1952 GPtr
->CNType
= record
.recordType
; /* target CNode type = thread */
1953 CopyCatalogName( (const CatalogName
*) &foundKey
.hfs
.nodeName
, &GPtr
->CName
, isHFSPlus
); /* target CName = directory name */
1955 if (dprP
->directoryID
== kHFSRootFolderID
)
1959 // Unknown/Bad record type
1962 M_DebugStr("\p Unknown-Bad record type");
1968 // if not same ParID or no record
1970 else if ( (record
.recordType
== kHFSFileThreadRecord
) || (record
.recordType
== kHFSPlusFileThreadRecord
) ) /* it's a file thread, skip past it */
1972 GPtr
->TarID
= parID
; // target ID = file number
1973 GPtr
->CNType
= record
.recordType
; // target CNode type = thread
1974 GPtr
->CName
.ustr
.length
= 0; // no target CName
1980 GPtr
->TarID
= dprP
->directoryID
; /* target ID = current directory ID */
1981 GPtr
->CNType
= record
.recordType
; /* target CNode type = directory */
1982 CopyCatalogName( (const CatalogName
*) &dprP
->directoryName
, &GPtr
->CName
, isHFSPlus
); // copy the string name
1984 // re-locate current directory
1985 CopyCatalogName( (const CatalogName
*) &dprP
->directoryName
, &catalogName
, isHFSPlus
);
1986 BuildCatalogKey( dprP
->parentDirID
, (const CatalogName
*)&catalogName
, isHFSPlus
, &key
);
1987 result
= SearchBTreeRecord( GPtr
->calculatedCatalogFCB
, &key
, dprP
->directoryHint
, &foundKey
, &record2
, &recSize
, &hint
);
1989 if ( result
!= noErr
)
1991 result
= IntError(GPtr
,result
); /* error from BTSearch */
1994 GPtr
->TarBlock
= hint
; /* set target block */
1997 valence
= isHFSPlus
== true ? record2
.hfsPlusFolder
.valence
: (UInt32
)record2
.hfsFolder
.valence
;
1999 if ( valence
!= dprP
->offspringIndex
-1 ) /* check its valence */
2000 if ( ( result
= RcdValErr( GPtr
, E_DirVal
, dprP
->offspringIndex
-1, valence
, dprP
->parentDirID
) ) )
2003 GPtr
->DirLevel
--; /* move up a level */
2005 if(GPtr
->DirLevel
> 0)
2007 dprP
= &(GPtr
->DirPTPtr
)[GPtr
->DirLevel
- 1];
2008 GPtr
->TarID
= dprP
->directoryID
; /* target ID = current directory ID */
2009 GPtr
->CNType
= record
.recordType
; /* target CNode type = directory */
2010 CopyCatalogName( (const CatalogName
*) &dprP
->directoryName
, &GPtr
->CName
, isHFSPlus
);
2016 // verify directory and file counts (all nonfatal, repairable errors)
2018 if (!isHFSPlus
&& (rtdirCnt
!= calculatedVCB
->vcbNmRtDirs
)) /* check count of dirs in root */
2019 if ( ( result
= RcdValErr(GPtr
,E_RtDirCnt
,rtdirCnt
,calculatedVCB
->vcbNmRtDirs
,0) ) )
2022 if (!isHFSPlus
&& (rtfilCnt
!= calculatedVCB
->vcbNmFls
)) /* check count of files in root */
2023 if ( ( result
= RcdValErr(GPtr
,E_RtFilCnt
,rtfilCnt
,calculatedVCB
->vcbNmFls
,0) ) )
2026 if (dirCnt
!= calculatedVCB
->vcbFolderCount
) /* check count of dirs in volume */
2027 if ( ( result
= RcdValErr(GPtr
,E_DirCnt
,dirCnt
,calculatedVCB
->vcbFolderCount
,0) ) )
2030 if (filCnt
!= calculatedVCB
->vcbFileCount
) /* check count of files in volume */
2031 if ( ( result
= RcdValErr(GPtr
,E_FilCnt
,filCnt
,calculatedVCB
->vcbFileCount
,0) ) )
2036 } /* end of CatHChk */
2040 /*------------------------------------------------------------------------------
2042 Function: CreateAttributesBTreeControlBlock
2044 Function: Create the calculated AttributesBTree Control Block
2046 Input: GPtr - pointer to scavenger global area
2048 Output: - 0 = no error
2050 ------------------------------------------------------------------------------*/
2051 OSErr
CreateAttributesBTreeControlBlock( SGlobPtr GPtr
)
2056 BTreeControlBlock
* btcb
;
2060 BlockDescriptor block
;
2063 isHFSPlus
= VolumeObjectIsHFSPlus( );
2064 GPtr
->TarID
= kHFSAttributesFileID
;
2065 GPtr
->TarBlock
= kHeaderNodeNum
;
2066 block
.buffer
= NULL
;
2067 btcb
= GPtr
->calculatedAttributesBTCB
;
2068 vcb
= GPtr
->calculatedVCB
;
2071 // check out allocation info for the Attributes File
2076 HFSPlusVolumeHeader
*volumeHeader
;
2078 err
= GetVolumeObjectVHB( &block
);
2081 volumeHeader
= (HFSPlusVolumeHeader
*) block
.buffer
;
2083 CopyMemory( volumeHeader
->attributesFile
.extents
, GPtr
->calculatedAttributesFCB
->fcbExtents32
, sizeof(HFSPlusExtentRecord
) );
2085 err
= CheckFileExtents( GPtr
, kHFSAttributesFileID
, kDataFork
, NULL
, (void *)GPtr
->calculatedAttributesFCB
->fcbExtents32
, &numABlks
);
2088 if ( volumeHeader
->attributesFile
.totalBlocks
!= numABlks
) // check out the PEOF
2090 RcdError( GPtr
, E_CatPEOF
);
2096 GPtr
->calculatedAttributesFCB
->fcbLogicalSize
= (UInt64
) volumeHeader
->attributesFile
.logicalSize
; // Set Attributes tree's LEOF
2097 GPtr
->calculatedAttributesFCB
->fcbPhysicalSize
= (UInt64
) volumeHeader
->attributesFile
.totalBlocks
*
2098 (UInt64
) volumeHeader
->blockSize
; // Set Attributes tree's PEOF
2102 // See if we actually have an attributes BTree
2106 btcb
->maxKeyLength
= 0;
2107 btcb
->keyCompareProc
= 0;
2108 btcb
->leafRecords
= 0;
2110 btcb
->totalNodes
= 0;
2111 btcb
->freeNodes
= 0;
2112 btcb
->attributes
= 0;
2114 btcb
->treeDepth
= 0;
2116 btcb
->firstLeafNode
= 0;
2117 btcb
->lastLeafNode
= 0;
2119 // GPtr->calculatedVCB->attributesRefNum = 0;
2120 GPtr
->calculatedVCB
->vcbAttributesFile
= NULL
;
2124 // read the BTreeHeader from disk & also validate it's node size.
2125 err
= GetBTreeHeader(GPtr
, GPtr
->calculatedAttributesFCB
, &header
);
2128 btcb
->maxKeyLength
= kAttributeKeyMaximumLength
; // max key length
2129 btcb
->keyCompareProc
= (void *)CompareAttributeKeys
;
2130 btcb
->leafRecords
= header
.leafRecords
;
2131 btcb
->nodeSize
= header
.nodeSize
;
2132 btcb
->totalNodes
= ( GPtr
->calculatedAttributesFCB
->fcbPhysicalSize
/ btcb
->nodeSize
);
2133 btcb
->freeNodes
= btcb
->totalNodes
; // start with everything free
2134 btcb
->attributes
|=(kBTBigKeysMask
+ kBTVariableIndexKeysMask
); // HFS+ Attributes files have large, variable-sized keys
2136 btcb
->treeDepth
= header
.treeDepth
;
2137 btcb
->rootNode
= header
.rootNode
;
2138 btcb
->firstLeafNode
= header
.firstLeafNode
;
2139 btcb
->lastLeafNode
= header
.lastLeafNode
;
2142 // Make sure the header nodes size field is correct by looking at the 1st record offset
2144 err
= CheckNodesFirstOffset( GPtr
, btcb
);
2150 btcb
->maxKeyLength
= 0;
2151 btcb
->keyCompareProc
= 0;
2152 btcb
->leafRecords
= 0;
2154 btcb
->totalNodes
= 0;
2155 btcb
->freeNodes
= 0;
2156 btcb
->attributes
= 0;
2158 btcb
->treeDepth
= 0;
2160 btcb
->firstLeafNode
= 0;
2161 btcb
->lastLeafNode
= 0;
2163 GPtr
->calculatedVCB
->vcbAttributesFile
= NULL
;
2167 // set up our DFA extended BTCB area. Will we have enough memory on all HFS+ volumes.
2169 btcb
->refCon
= AllocateClearMemory( sizeof(BTreeExtensionsRec
) ); // allocate space for our BTCB extensions
2170 if ( btcb
->refCon
== nil
) {
2175 if (btcb
->totalNodes
== 0)
2177 ((BTreeExtensionsRec
*)btcb
->refCon
)->BTCBMPtr
= nil
;
2178 ((BTreeExtensionsRec
*)btcb
->refCon
)->BTCBMSize
= 0;
2179 ((BTreeExtensionsRec
*)btcb
->refCon
)->realFreeNodeCount
= 0;
2183 if ( btcb
->refCon
== nil
) {
2187 size
= (btcb
->totalNodes
+ 7) / 8; // size of BTree bit map
2188 ((BTreeExtensionsRec
*)btcb
->refCon
)->BTCBMPtr
= AllocateClearMemory(size
); // get precleared bitmap
2189 if ( ((BTreeExtensionsRec
*)btcb
->refCon
)->BTCBMPtr
== nil
)
2195 ((BTreeExtensionsRec
*)btcb
->refCon
)->BTCBMSize
= size
; // remember how long it is
2196 ((BTreeExtensionsRec
*)btcb
->refCon
)->realFreeNodeCount
= header
.freeNodes
; // keep track of real free nodes for progress
2201 (void) ReleaseVolumeBlock(vcb
, &block
, kReleaseBlock
);
2207 * Function: RecordLastAttrBits
2210 * Updates the Chinese Remainder Theorem buckets with extended attribute
2211 * information for the previous fileID stored in the global structure.
2214 * GPtr - pointer to scavenger global area
2215 * * GPtr->lastAttrInfo.fileID - fileID of last attribute seen
2219 static void RecordLastAttrBits(SGlobPtr GPtr
)
2221 /* lastAttrInfo structure is initialized to zero and hence ignore
2222 * recording information for fileID = 0. fileIDs < 16 (except for
2223 * fileID = 2) can have extended attributes but do not have
2224 * corresponding entry in catalog Btree. Ignore recording these
2225 * fileIDs for Chinese Remainder Theorem buckets. Currently we only
2226 * set extended attributes for fileID = 1 among these fileIDs
2227 * and this can change in future (see 3984119)
2229 if ((GPtr
->lastAttrInfo
.fileID
== 0) ||
2230 ((GPtr
->lastAttrInfo
.fileID
< kHFSFirstUserCatalogNodeID
) &&
2231 (GPtr
->lastAttrInfo
.fileID
!= kHFSRootFolderID
))) {
2235 if (GPtr
->lastAttrInfo
.hasSecurity
== true) {
2236 /* fileID has both extended attribute and ACL */
2237 RecordXAttrBits(GPtr
, kHFSHasAttributesMask
| kHFSHasSecurityMask
,
2238 GPtr
->lastAttrInfo
.fileID
, kCalculatedAttributesRefNum
);
2239 GPtr
->lastAttrInfo
.hasSecurity
= false;
2241 /* fileID only has extended attribute */
2242 RecordXAttrBits(GPtr
, kHFSHasAttributesMask
,
2243 GPtr
->lastAttrInfo
.fileID
, kCalculatedAttributesRefNum
);
2248 * Function: setLastAttrAllocInfo
2251 * Set the global structure of last extended attribute with
2252 * the allocation block information. Also set the isValid to true
2253 * to indicate that the data is valid and should be used to verify
2254 * allocation blocks.
2257 * GPtr - pointer to scavenger global area
2258 * totalBlocks - total blocks allocated by the attribute
2259 * logicalSize - logical size of the attribute
2260 * calculatedBlocks - blocks accounted by the attribute in current extent
2264 static void setLastAttrAllocInfo(SGlobPtr GPtr
, u_int32_t totalBlocks
,
2265 u_int64_t logicalSize
, u_int32_t calculatedTotalBlocks
)
2267 GPtr
->lastAttrInfo
.totalBlocks
= totalBlocks
;
2268 GPtr
->lastAttrInfo
.logicalSize
= logicalSize
;
2269 GPtr
->lastAttrInfo
.calculatedTotalBlocks
= calculatedTotalBlocks
;
2270 GPtr
->lastAttrInfo
.isValid
= true;
2274 * Function: CheckLastAttrAllocation
2277 * Checks the allocation block information stored for the last
2278 * extended attribute seen during extended attribute BTree traversal.
2279 * Always resets the information stored for last EA allocation.
2281 * Input: GPtr - pointer to scavenger global area
2283 * Output: int - function result:
2287 static int CheckLastAttrAllocation(SGlobPtr GPtr
)
2292 if (GPtr
->lastAttrInfo
.isValid
== true) {
2293 if (GPtr
->lastAttrInfo
.totalBlocks
!=
2294 GPtr
->lastAttrInfo
.calculatedTotalBlocks
) {
2295 result
= RecordBadAllocation(GPtr
->lastAttrInfo
.fileID
,
2296 GPtr
->lastAttrInfo
.attrname
, kEAData
,
2297 GPtr
->lastAttrInfo
.totalBlocks
,
2298 GPtr
->lastAttrInfo
.calculatedTotalBlocks
);
2300 bytes
= (u_int64_t
)GPtr
->lastAttrInfo
.calculatedTotalBlocks
*
2301 (u_int64_t
)GPtr
->calculatedVCB
->vcbBlockSize
;
2302 if (GPtr
->lastAttrInfo
.logicalSize
> bytes
) {
2303 result
= RecordTruncation(GPtr
->lastAttrInfo
.fileID
,
2304 GPtr
->lastAttrInfo
.attrname
, kEAData
,
2305 GPtr
->lastAttrInfo
.logicalSize
, bytes
);
2309 /* Invalidate information in the global structure */
2310 GPtr
->lastAttrInfo
.isValid
= false;
2316 /*------------------------------------------------------------------------------
2317 Function: CheckAttributeRecord
2320 This is call back function called for all leaf records in
2321 Attribute BTree during the verify and repair stage. The basic
2322 functionality of the function is same during verify and repair
2323 stages except that whenever it finds corruption, the verify
2324 stage prints message and the repair stage repairs it. In the verify
2325 stage, this function accounts for allocation blocks used
2326 by extent-based extended attributes and also updates the chinese
2327 remainder theorem buckets corresponding the extended attribute
2330 1. Only in the verify stage, if the fileID or attribute name of current
2331 extended attribute are not same as the previous attribute, check the
2332 allocation block counts for the previous attribute.
2334 2. Only in the verify stage, If the fileID of current attribute is not
2335 same as the previous attribute, record the previous fileID information
2336 for Chinese Remainder Theorem.
2338 3. For attribute type,
2339 kHFSPlusAttrForkData:
2340 ---------------------
2341 Do all of the following during verify stage and nothing in repair
2344 Check the start block for extended attribute from the key. If not
2347 Account for blocks occupied by this extent and store the allocation
2348 information for this extent to check in future. Also update the
2349 last attribute information in the global structure.
2351 kHFSPlusAttrExtents:
2352 --------------------
2353 If the current attribute's fileID is not same as previous fileID, or
2354 if the previous recordType is not a valid forkData or overflow extent
2355 record, report an error in verify stage or mark it for deletion in
2358 Do all of the following during verify stage and nothing in repair
2361 Check the start block for extended attribute from the key. If not
2362 equal to the total blocks seen uptil last attribtue, print error.
2364 Account for blocks occupied by this extent. Update previous
2365 attribute allocation information with blocks seen in current
2366 extent. Also update last attribute block information in the global
2369 kHFSPlusAttrInlineData:
2370 -----------------------
2371 Only in the verify stage, check if the start block in the key is
2372 equal to zero. If not, print error.
2376 In verify stage, report error. In repair stage, mark the record
2379 4. If a record is marked for deletion, delete the record.
2381 5. Before exiting from the function, always do the following -
2382 a. Indicate if the extended attribute was an ACL
2383 b. Update previous fileID and recordType with current information.
2384 c. Update previous attribute name with current attribute name.
2386 Input: GPtr - pointer to scavenger global area
2387 key - key for current attribute
2388 rec - attribute record
2389 reclen - length of the record
2391 Output: int - function result:
2394 ------------------------------------------------------------------------------*/
2396 CheckAttributeRecord(SGlobPtr GPtr
, const HFSPlusAttrKey
*key
, const HFSPlusAttrRecord
*rec
, UInt16 reclen
)
2399 unsigned char attrname
[XATTR_MAXNAMELEN
+1];
2403 struct attributeInfo
*prevAttr
;
2404 Boolean isSameAttr
= true;
2405 Boolean doDelete
= false;
2406 u_int16_t dfaStage
= GetDFAStage();
2408 /* Assert if volume is not HFS Plus */
2409 assert(VolumeObjectIsHFSPlus() == true);
2411 prevAttr
= &(GPtr
->lastAttrInfo
);
2412 fileID
= key
->fileID
;
2413 /* Convert unicode attribute name to UTF-8 string */
2414 (void) utf_encodestr(key
->attrName
, key
->attrNameLen
* 2, attrname
, &attrlen
, sizeof(attrname
));
2415 attrname
[attrlen
] = '\0';
2417 /* Compare the current attribute to last attribute seen */
2418 if ((fileID
!= prevAttr
->fileID
) ||
2419 (strcmp((char *)attrname
, (char *)prevAttr
->attrname
) != 0)) {
2423 /* We check allocation block information and record EA information for
2424 * CRT bucket in verify stage and hence no need to do it again in
2427 if (dfaStage
== kVerifyStage
) {
2428 /* Different attribute - check allocation block information */
2429 if (isSameAttr
== false) {
2430 result
= CheckLastAttrAllocation(GPtr
);
2436 /* Different fileID - record information in CRT bucket */
2437 if (fileID
!= prevAttr
->fileID
) {
2438 RecordLastAttrBits(GPtr
);
2442 switch (rec
->recordType
) {
2443 case kHFSPlusAttrForkData
: {
2444 /* Check start block only in verify stage to avoid printing message
2445 * in repair stage. Note that this corruption is not repairable
2446 * currently. Also check extents only in verify stage to avoid
2447 * false overlap extents error.
2449 if (dfaStage
== kVerifyStage
) {
2450 /* Start block in the key should be zero */
2451 if (key
->startBlock
!= 0) {
2452 RcdError(GPtr
, E_ABlkSt
);
2457 /* Check the extent information and record overlapping extents, if any */
2458 result
= CheckFileExtents (GPtr
, fileID
, kEAData
, attrname
,
2459 rec
->forkData
.theFork
.extents
, &blocks
);
2464 /* Store allocation information to check in future */
2465 (void) setLastAttrAllocInfo(GPtr
, rec
->forkData
.theFork
.totalBlocks
,
2466 rec
->forkData
.theFork
.logicalSize
, blocks
);
2471 case kHFSPlusAttrExtents
: {
2472 /* Different attribute/fileID or incorrect previous record type */
2473 if ((isSameAttr
== false) ||
2474 ((prevAttr
->recordType
!= kHFSPlusAttrExtents
) &&
2475 (prevAttr
->recordType
!= kHFSPlusAttrForkData
))) {
2476 if (dfaStage
== kRepairStage
) {
2477 /* Delete record in repair stage */
2480 /* Report error in verify stage */
2481 RcdError(GPtr
, E_AttrRec
);
2482 GPtr
->ABTStat
|= S_AttrRec
;
2487 /* Check start block only in verify stage to avoid printing message
2488 * in repair stage. Note that this corruption is not repairable
2489 * currently. Also check extents only in verify stage to avoid
2490 * false overlap extents error.
2492 if (dfaStage
== kVerifyStage
) {
2493 /* startBlock in the key should be equal to total blocks
2494 * seen uptil last attribute.
2496 if (key
->startBlock
!= prevAttr
->calculatedTotalBlocks
) {
2497 RcdError(GPtr
, E_ABlkSt
);
2502 /* Check the extent information and record overlapping extents, if any */
2503 result
= CheckFileExtents (GPtr
, fileID
, kEAData
, attrname
,
2504 rec
->overflowExtents
.extents
, &blocks
);
2509 /* Increment the blocks seen uptil now for this attribute */
2510 prevAttr
->calculatedTotalBlocks
+= blocks
;
2515 case kHFSPlusAttrInlineData
: {
2516 /* Check start block only in verify stage to avoid printing message
2519 if (dfaStage
== kVerifyStage
) {
2520 /* Start block in the key should be zero */
2521 if (key
->startBlock
!= 0) {
2522 RcdError(GPtr
, E_ABlkSt
);
2531 /* Unknown attribute record */
2532 if (dfaStage
== kRepairStage
) {
2533 /* Delete record in repair stage */
2536 /* Report error in verify stage */
2537 RcdError(GPtr
, E_AttrRec
);
2538 GPtr
->ABTStat
|= S_AttrRec
;
2545 if (doDelete
== true) {
2546 result
= DeleteBTreeRecord(GPtr
->calculatedAttributesFCB
, key
);
2547 DPRINTF (d_info
|d_xattr
, "%s: Deleting attribute %s for fileID %d, type = %d\n", __FUNCTION__
, attrname
, key
->fileID
, rec
->recordType
);
2549 DPRINTF (d_error
|d_xattr
, "%s: Error in deleting record for %s for fileID %d, type = %d\n", __FUNCTION__
, attrname
, key
->fileID
, rec
->recordType
);
2552 /* Set flags to mark header and map dirty */
2553 GPtr
->ABTStat
|= S_BTH
+ S_BTM
;
2558 /* Note that an ACL exists for this fileID */
2559 if (strcmp((char *)attrname
, KAUTH_FILESEC_XATTR
) == 0) {
2560 prevAttr
->hasSecurity
= true;
2563 /* Always update the last recordType, fileID and attribute name before exiting */
2564 prevAttr
->recordType
= rec
->recordType
;
2565 prevAttr
->fileID
= fileID
;
2566 (void) strlcpy((char *)prevAttr
->attrname
, (char *)attrname
, sizeof(prevAttr
->attrname
));
2571 /* If the current record is invalid/bogus, decide whether to update
2572 * fileID stored in global structure for future comparison based on the
2574 * If the current bogus record's fileID is different from fileID of the
2575 * previous good record, we do not want to account for bogus fileID in
2576 * the Chinese Remainder Theorem when we see next good record.
2577 * Hence reset the fileID in global structure to dummy value. Example,
2578 * if the fileIDs are 10 15 20 and record with ID=15 is bogus, we do not
2579 * want to account for record with ID=15.
2580 * If the current bogus record's fileID is same as the fileID of the
2581 * previous good record, we want to account for this fileID in the
2582 * next good record we see after this bogus record. Hence do not
2583 * reset the fileID to dummy value. Example, if the records have fileID
2584 * 10 10 30 and the second record with ID=10 is bogus, we want to
2585 * account for ID=10 when we see record with ID=30.
2587 if (prevAttr
->fileID
!= fileID
) {
2588 prevAttr
->fileID
= 0;
2595 /* Function: RecordXAttrBits
2598 * This function increments the prime number buckets for the associated
2599 * prime bucket set based on the flags and btreetype to determine
2600 * the discrepancy between the attribute btree and catalog btree for
2601 * extended attribute data consistency. This function is based on
2602 * Chinese Remainder Theorem.
2605 * 1. If none of kHFSHasAttributesMask or kHFSHasSecurity mask is set,
2607 * 2. Based on btreetype and the flags, determine which prime number
2608 * bucket should be updated. Initialize pointers accordingly.
2609 * 3. Divide the fileID with pre-defined prime numbers. Store the
2611 * 4. Increment each prime number bucket at an offset of the
2612 * corresponding remainder with one.
2614 * Input: 1. GPtr - pointer to global scavenger area
2615 * 2. flags - can include kHFSHasAttributesMask and/or kHFSHasSecurityMask
2616 * 3. fileid - fileID for which particular extended attribute is seen
2617 * 4. btreetye - can be kHFSPlusCatalogRecord or kHFSPlusAttributeRecord
2618 * indicates which btree prime number bucket should be incremented
2622 void RecordXAttrBits(SGlobPtr GPtr
, UInt16 flags
, HFSCatalogNodeID fileid
, UInt16 btreetype
)
2624 PrimeBuckets
*cur_attr
= NULL
;
2625 PrimeBuckets
*cur_sec
= NULL
;
2627 if ( ((flags
& kHFSHasAttributesMask
) == 0) &&
2628 ((flags
& kHFSHasSecurityMask
) == 0) ) {
2629 /* No attributes exists for this fileID */
2633 /* Determine which bucket are we updating */
2634 if (btreetype
== kCalculatedCatalogRefNum
) {
2635 /* Catalog BTree buckets */
2636 if (flags
& kHFSHasAttributesMask
) {
2637 cur_attr
= &(GPtr
->CBTAttrBucket
);
2638 GPtr
->cat_ea_count
++;
2640 if (flags
& kHFSHasSecurityMask
) {
2641 cur_sec
= &(GPtr
->CBTSecurityBucket
);
2642 GPtr
->cat_acl_count
++;
2644 } else if (btreetype
== kCalculatedAttributesRefNum
) {
2645 /* Attribute BTree buckets */
2646 if (flags
& kHFSHasAttributesMask
) {
2647 cur_attr
= &(GPtr
->ABTAttrBucket
);
2648 GPtr
->attr_ea_count
++;
2650 if (flags
& kHFSHasSecurityMask
) {
2651 cur_sec
= &(GPtr
->ABTSecurityBucket
);
2652 GPtr
->attr_acl_count
++;
2655 /* Incorrect btreetype found */
2660 add_prime_bucket_uint32(cur_attr
, fileid
);
2664 add_prime_bucket_uint32(cur_sec
, fileid
);
2671 /* Function: CompareXattrPrimeBuckets
2674 * This function compares the prime number buckets for catalog btree
2675 * and attribute btree for the given attribute type (normal attribute
2676 * bit or security bit).
2678 * Input: 1. GPtr - pointer to global scavenger area
2679 * 2. BitMask - indicate which attribute type should be compared.
2680 * can include kHFSHasAttributesMask and/or kHFSHasSecurityMask
2681 * Output: zero - buckets were compared successfully
2682 * non-zero - buckets were not compared
2684 static int CompareXattrPrimeBuckets(SGlobPtr GPtr
, UInt16 BitMask
)
2687 PrimeBuckets
*cat
; /* Catalog BTree */
2688 PrimeBuckets
*attr
; /* Attribute BTree */
2690 /* Find the correct PrimeBuckets to compare */
2691 if (BitMask
& kHFSHasAttributesMask
) {
2692 /* Compare buckets for attribute bit */
2693 cat
= &(GPtr
->CBTAttrBucket
);
2694 attr
= &(GPtr
->ABTAttrBucket
);
2695 } else if (BitMask
& kHFSHasSecurityMask
) {
2696 /* Compare buckets for security bit */
2697 cat
= &(GPtr
->CBTSecurityBucket
);
2698 attr
= &(GPtr
->ABTSecurityBucket
);
2700 plog ("%s: Incorrect BitMask found.\n", __FUNCTION__
);
2704 result
= compare_prime_buckets(cat
, attr
);
2706 char catbtree
[32], attrbtree
[32];
2707 /* Unequal values found, set the error bit in ABTStat */
2708 if (BitMask
& kHFSHasAttributesMask
) {
2709 fsckPrint(GPtr
->context
, E_IncorrectAttrCount
);
2710 sprintf (catbtree
, "%u", GPtr
->cat_ea_count
);
2711 sprintf (attrbtree
, "%u", GPtr
->attr_ea_count
);
2712 fsckPrint(GPtr
->context
, E_BadValue
, attrbtree
, catbtree
);
2713 GPtr
->ABTStat
|= S_AttributeCount
;
2715 fsckPrint(GPtr
->context
, E_IncorrectSecurityCount
);
2716 sprintf (catbtree
, "%u", GPtr
->cat_acl_count
);
2717 sprintf (attrbtree
, "%u", GPtr
->attr_acl_count
);
2718 fsckPrint (GPtr
->context
, E_BadValue
, attrbtree
, catbtree
);
2719 GPtr
->ABTStat
|= S_SecurityCount
;
2729 /*------------------------------------------------------------------------------
2731 Function: AttrBTChk - (Attributes BTree Check)
2733 Function: Verifies the attributes BTree structure.
2735 Input: GPtr - pointer to scavenger global area
2737 Output: ExtBTChk - function result:
2740 ------------------------------------------------------------------------------*/
2742 OSErr
AttrBTChk( SGlobPtr GPtr
)
2747 // If this volume has no attributes BTree, then skip this check
2749 if (GPtr
->calculatedVCB
->vcbAttributesFile
== NULL
)
2752 // Write the status message here to avoid potential confusion to user.
2753 fsckPrint(GPtr
->context
, hfsExtAttrBTCheck
);
2756 GPtr
->TarID
= kHFSAttributesFileID
; // target = attributes file
2757 GetVolumeObjectBlockNum( &GPtr
->TarBlock
); // target block = VHB/MDB
2760 // check out the BTree structure
2763 err
= BTCheck( GPtr
, kCalculatedAttributesRefNum
, (CheckLeafRecordProcPtr
)CheckAttributeRecord
);
2764 ReturnIfError( err
); // invalid attributes file BTree
2766 // check the allocation block information about the last attribute
2767 err
= CheckLastAttrAllocation(GPtr
);
2770 // record the last fileID for Chinese Remainder Theorem comparison
2771 RecordLastAttrBits(GPtr
);
2773 // compare the attributes prime buckets calculated from catalog btree and attribute btree
2774 err
= CompareXattrPrimeBuckets(GPtr
, kHFSHasAttributesMask
);
2775 ReturnIfError( err
);
2777 // compare the security prime buckets calculated from catalog btree and attribute btree
2778 err
= CompareXattrPrimeBuckets(GPtr
, kHFSHasSecurityMask
);
2779 ReturnIfError( err
);
2782 // check out the allocation map structure
2785 err
= BTMapChk( GPtr
, kCalculatedAttributesRefNum
);
2786 ReturnIfError( err
); // Invalid attributes BTree map
2789 // Make sure unused nodes in the B-tree are zero filled.
2791 err
= BTCheckUnusedNodes(GPtr
, kCalculatedAttributesRefNum
, &GPtr
->ABTStat
);
2792 ReturnIfError( err
);
2795 // compare BTree header record on disk with scavenger's BTree header record
2798 err
= CmpBTH( GPtr
, kCalculatedAttributesRefNum
);
2799 ReturnIfError( err
);
2802 // compare BTree map on disk with scavenger's BTree map
2805 err
= CmpBTM( GPtr
, kCalculatedAttributesRefNum
);
2811 /*------------------------------------------------------------------------------
2813 Name: RcdValErr - (Record Valence Error)
2815 Function: Allocates a RepairOrder node and linkg it into the 'GPtr->RepairP'
2816 list, to describe an incorrect valence count for possible repair.
2818 Input: GPtr - ptr to scavenger global data
2819 type - error code (E_xxx), which should be >0
2820 correct - the correct valence, as computed here
2821 incorrect - the incorrect valence as found in volume
2822 parid - the parent id, if S_Valence error
2824 Output: 0 - no error
2825 R_NoMem - not enough mem to allocate record
2826 ------------------------------------------------------------------------------*/
2828 static int RcdValErr( SGlobPtr GPtr
, OSErr type
, UInt32 correct
, UInt32 incorrect
, HFSCatalogNodeID parid
) /* the ParID, if needed */
2830 RepairOrderPtr p
; /* the new node we compile */
2831 SInt16 n
; /* size of node we allocate */
2833 char goodStr
[32], badStr
[32];
2835 isHFSPlus
= VolumeObjectIsHFSPlus( );
2836 fsckPrint(GPtr
->context
, type
);
2837 sprintf(goodStr
, "%u", correct
);
2838 sprintf(badStr
, "%u", incorrect
);
2839 fsckPrint(GPtr
->context
, E_BadValue
, goodStr
, badStr
);
2841 if (type
== E_DirVal
) /* if normal directory valence error */
2842 n
= CatalogNameSize( &GPtr
->CName
, isHFSPlus
);
2844 n
= 0; /* other errors don't need the name */
2846 p
= AllocMinorRepairOrder( GPtr
,n
); /* get the node */
2847 if (p
==NULL
) /* quit if out of room */
2850 p
->type
= type
; /* save error info */
2851 p
->correct
= correct
;
2852 p
->incorrect
= incorrect
;
2855 if ( n
!= 0 ) /* if name needed */
2856 CopyCatalogName( (const CatalogName
*) &GPtr
->CName
, (CatalogName
*)&p
->name
, isHFSPlus
);
2858 GPtr
->CatStat
|= S_Valence
; /* set flag to trigger repair */
2860 return( noErr
); /* successful return */
2863 /*------------------------------------------------------------------------------
2865 Name: RcdHsFldCntErr - (Record HasFolderCount)
2867 Function: Allocates a RepairOrder node and linkg it into the 'GPtr->RepairP'
2868 list, to describe folder flag missing the HasFolderCount bit
2870 Input: GPtr - ptr to scavenger global data
2871 type - error code (E_xxx), which should be >0
2872 correct - the folder mask, as computed here
2873 incorrect - the folder mask, as found in volume
2876 Output: 0 - no error
2877 R_NoMem - not enough mem to allocate record
2878 ------------------------------------------------------------------------------*/
2880 int RcdHsFldCntErr( SGlobPtr GPtr
, OSErr type
, UInt32 correct
, UInt32 incorrect
, HFSCatalogNodeID fid
)
2882 RepairOrderPtr p
; /* the new node we compile */
2883 char goodStr
[32], badStr
[32];
2884 fsckPrint(GPtr
->context
, type
, fid
);
2885 sprintf(goodStr
, "%#x", correct
);
2886 sprintf(badStr
, "%#x", incorrect
);
2887 fsckPrint(GPtr
->context
, E_BadValue
, goodStr
, badStr
);
2889 p
= AllocMinorRepairOrder( GPtr
,0 ); /* get the node */
2890 if (p
==NULL
) /* quit if out of room */
2893 p
->type
= type
; /* save error info */
2894 p
->correct
= correct
;
2895 p
->incorrect
= incorrect
;
2898 return( noErr
); /* successful return */
2900 /*------------------------------------------------------------------------------
2902 Name: RcdFCntErr - (Record Folder Count)
2904 Function: Allocates a RepairOrder node and linkg it into the 'GPtr->RepairP'
2905 list, to describe an incorrect folder count for possible repair.
2907 Input: GPtr - ptr to scavenger global data
2908 type - error code (E_xxx), which should be >0
2909 correct - the correct folder count, as computed here
2910 incorrect - the incorrect folder count as found in volume
2913 Output: 0 - no error
2914 R_NoMem - not enough mem to allocate record
2915 ------------------------------------------------------------------------------*/
2917 int RcdFCntErr( SGlobPtr GPtr
, OSErr type
, UInt32 correct
, UInt32 incorrect
, HFSCatalogNodeID fid
)
2919 RepairOrderPtr p
; /* the new node we compile */
2920 char goodStr
[32], badStr
[32];
2922 fsckPrint(GPtr
->context
, type
, fid
);
2923 sprintf(goodStr
, "%u", correct
);
2924 sprintf(badStr
, "%u", incorrect
);
2925 fsckPrint(GPtr
->context
, E_BadValue
, goodStr
, badStr
);
2927 p
= AllocMinorRepairOrder( GPtr
,0 ); /* get the node */
2928 if (p
==NULL
) /* quit if out of room */
2931 p
->type
= type
; /* save error info */
2932 p
->correct
= correct
;
2933 p
->incorrect
= incorrect
;
2936 return( noErr
); /* successful return */
2939 /*------------------------------------------------------------------------------
2941 Name: RcdMDBAllocationBlockStartErr - (Record Allocation Block Start Error)
2943 Function: Allocates a RepairOrder node and linking it into the 'GPtr->RepairP'
2944 list, to describe the error for possible repair.
2946 Input: GPtr - ptr to scavenger global data
2947 type - error code (E_xxx), which should be >0
2948 correct - the correct valence, as computed here
2949 incorrect - the incorrect valence as found in volume
2951 Output: 0 - no error
2952 R_NoMem - not enough mem to allocate record
2953 ------------------------------------------------------------------------------*/
2955 static OSErr
RcdMDBEmbededVolDescriptionErr( SGlobPtr GPtr
, OSErr type
, HFSMasterDirectoryBlock
*mdb
)
2957 RepairOrderPtr p
; // the new node we compile
2958 EmbededVolDescription
*desc
;
2960 RcdError( GPtr
, type
); // first, record the error
2962 p
= AllocMinorRepairOrder( GPtr
, sizeof(EmbededVolDescription
) ); // get the node
2963 if ( p
== nil
) return( R_NoMem
);
2965 p
->type
= type
; // save error info
2966 desc
= (EmbededVolDescription
*) &(p
->name
);
2967 desc
->drAlBlSt
= mdb
->drAlBlSt
;
2968 desc
->drEmbedSigWord
= mdb
->drEmbedSigWord
;
2969 desc
->drEmbedExtent
.startBlock
= mdb
->drEmbedExtent
.startBlock
;
2970 desc
->drEmbedExtent
.blockCount
= mdb
->drEmbedExtent
.blockCount
;
2972 GPtr
->VIStat
|= S_InvalidWrapperExtents
; // set flag to trigger repair
2974 return( noErr
); // successful return
2978 #if 0 // not used at this time
2979 /*------------------------------------------------------------------------------
2981 Name: RcdInvalidWrapperExtents - (Record Invalid Wrapper Extents)
2983 Function: Allocates a RepairOrder node and linking it into the 'GPtr->RepairP'
2984 list, to describe the error for possible repair.
2986 Input: GPtr - ptr to scavenger global data
2987 type - error code (E_xxx), which should be >0
2988 correct - the correct valence, as computed here
2989 incorrect - the incorrect valence as found in volume
2991 Output: 0 - no error
2992 R_NoMem - not enough mem to allocate record
2993 ------------------------------------------------------------------------------*/
2995 static OSErr
RcdInvalidWrapperExtents( SGlobPtr GPtr
, OSErr type
)
2997 RepairOrderPtr p
; // the new node we compile
2999 RcdError( GPtr
, type
); // first, record the error
3001 p
= AllocMinorRepairOrder( GPtr
, 0 ); // get the node
3002 if ( p
== nil
) return( R_NoMem
);
3004 p
->type
= type
; // save error info
3006 GPtr
->VIStat
|= S_BadMDBdrAlBlSt
; // set flag to trigger repair
3008 return( noErr
); // successful return
3013 #if 0 // We just check and fix them in SRepair.c
3014 /*------------------------------------------------------------------------------
3016 Name: RcdOrphanedExtentErr
3018 Function: Allocates a RepairOrder node and linkg it into the 'GPtr->RepairP'
3019 list, to describe an locked volume name for possible repair.
3021 Input: GPtr - ptr to scavenger global data
3022 type - error code (E_xxx), which should be >0
3023 incorrect - the incorrect file flags as found in file record
3025 Output: 0 - no error
3026 R_NoMem - not enough mem to allocate record
3027 ------------------------------------------------------------------------------*/
3029 static OSErr
RcdOrphanedExtentErr ( SGlobPtr GPtr
, SInt16 type
, void *theKey
)
3031 RepairOrderPtr p
; /* the new node we compile */
3032 SInt16 n
; /* size of node we allocate */
3035 isHFSPlus
= VolumeObjectIsHFSPlus( );
3036 RcdError( GPtr
,type
); /* first, record the error */
3039 n
= sizeof( HFSPlusExtentKey
);
3041 n
= sizeof( HFSExtentKey
);
3043 p
= AllocMinorRepairOrder( GPtr
, n
); /* get the node */
3044 if ( p
== NULL
) /* quit if out of room */
3047 CopyMemory( theKey
, p
->name
, n
); /* copy in the key */
3049 p
->type
= type
; /* save error info */
3051 GPtr
->EBTStat
|= S_OrphanedExtent
; /* set flag to trigger repair */
3053 return( noErr
); /* successful return */
3058 /*------------------------------------------------------------------------------
3060 Function: VInfoChk - (Volume Info Check)
3062 Function: Verifies volume level information.
3064 Input: GPtr - pointer to scavenger global area
3066 Output: VInfoChk - function result:
3069 ------------------------------------------------------------------------------*/
3071 OSErr
VInfoChk( SGlobPtr GPtr
)
3079 VolumeObjectPtr myVOPtr
;
3080 CatalogRecord record
;
3081 CatalogKey foundKey
;
3082 BlockDescriptor altBlock
;
3083 BlockDescriptor priBlock
;
3085 vcb
= GPtr
->calculatedVCB
;
3086 altBlock
.buffer
= priBlock
.buffer
= NULL
;
3087 isHFSPlus
= VolumeObjectIsHFSPlus( );
3088 myVOPtr
= GetVolumeObjectPtr( );
3090 // locate the catalog record for the root directoryÉ
3091 result
= GetBTreeRecord( GPtr
->calculatedCatalogFCB
, 0x8001, &foundKey
, &record
, &recSize
, &hint
);
3092 GPtr
->TarID
= kHFSCatalogFileID
; /* target = catalog */
3093 GPtr
->TarBlock
= hint
; /* target block = returned hint */
3094 if ( result
!= noErr
)
3096 result
= IntError( GPtr
, result
);
3100 GPtr
->TarID
= AMDB_FNum
; // target = alternate MDB or VHB
3101 GetVolumeObjectAlternateBlockNum( &GPtr
->TarBlock
);
3102 result
= GetVolumeObjectAlternateBlock( &altBlock
);
3104 // invalidate if we have not marked the alternate as OK
3106 if ( (myVOPtr
->flags
& kVO_AltVHBOK
) == 0 )
3109 else if ( (myVOPtr
->flags
& kVO_AltMDBOK
) == 0 ) {
3112 if ( result
!= noErr
) {
3113 GPtr
->VIStat
= GPtr
->VIStat
| S_MDB
;
3114 if ( VolumeObjectIsHFS( ) ) {
3115 WriteError( GPtr
, E_MDBDamaged
, 0, 0 );
3116 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
3117 plog("\tinvalid alternate MDB at %qd result %d \n", GPtr
->TarBlock
, result
);
3120 WriteError( GPtr
, E_VolumeHeaderDamaged
, 0, 0 );
3121 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
3122 plog("\tinvalid alternate VHB at %qd result %d \n", GPtr
->TarBlock
, result
);
3128 GPtr
->TarID
= MDB_FNum
; // target = primary MDB or VHB
3129 GetVolumeObjectPrimaryBlockNum( &GPtr
->TarBlock
);
3130 result
= GetVolumeObjectPrimaryBlock( &priBlock
);
3132 // invalidate if we have not marked the primary as OK
3134 if ( (myVOPtr
->flags
& kVO_PriVHBOK
) == 0 )
3137 else if ( (myVOPtr
->flags
& kVO_PriMDBOK
) == 0 ) {
3140 if ( result
!= noErr
) {
3141 GPtr
->VIStat
= GPtr
->VIStat
| S_MDB
;
3142 if ( VolumeObjectIsHFS( ) ) {
3143 WriteError( GPtr
, E_MDBDamaged
, 1, 0 );
3144 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
3145 plog("\tinvalid primary MDB at %qd result %d \n", GPtr
->TarBlock
, result
);
3148 WriteError( GPtr
, E_VolumeHeaderDamaged
, 1, 0 );
3149 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
3150 plog("\tinvalid primary VHB at %qd result %d \n", GPtr
->TarBlock
, result
);
3156 // check to see that embedded HFS plus volumes still have both (alternate and primary) MDBs
3157 if ( VolumeObjectIsEmbeddedHFSPlus( ) &&
3158 ( (myVOPtr
->flags
& kVO_PriMDBOK
) == 0 || (myVOPtr
->flags
& kVO_AltMDBOK
) == 0 ) )
3160 GPtr
->VIStat
|= S_WMDB
;
3161 WriteError( GPtr
, E_MDBDamaged
, 0, 0 );
3162 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
3163 plog("\tinvalid wrapper MDB \n");
3168 HFSPlusVolumeHeader
* volumeHeader
;
3169 HFSPlusVolumeHeader
* alternateVolumeHeader
;
3171 alternateVolumeHeader
= (HFSPlusVolumeHeader
*) altBlock
.buffer
;
3172 volumeHeader
= (HFSPlusVolumeHeader
*) priBlock
.buffer
;
3174 maxClump
= (UInt64
) (vcb
->vcbTotalBlocks
/ 4) * vcb
->vcbBlockSize
; /* max clump = 1/4 volume size */
3176 // check out creation and last mod dates
3177 vcb
->vcbCreateDate
= alternateVolumeHeader
->createDate
; // use creation date in alt MDB
3178 vcb
->vcbModifyDate
= volumeHeader
->modifyDate
; // don't change last mod date
3179 vcb
->vcbCheckedDate
= volumeHeader
->checkedDate
; // don't change checked date
3181 // 3882639: Removed check for volume attributes in HFS Plus
3182 vcb
->vcbAttributes
= volumeHeader
->attributes
;
3184 // verify allocation map ptr
3185 if ( volumeHeader
->nextAllocation
< vcb
->vcbTotalBlocks
)
3186 vcb
->vcbNextAllocation
= volumeHeader
->nextAllocation
;
3188 vcb
->vcbNextAllocation
= 0;
3190 // verify default clump sizes
3191 if ( (volumeHeader
->rsrcClumpSize
> 0) &&
3192 (volumeHeader
->rsrcClumpSize
<= kMaxClumpSize
) &&
3193 ((volumeHeader
->rsrcClumpSize
% vcb
->vcbBlockSize
) == 0) )
3194 vcb
->vcbRsrcClumpSize
= volumeHeader
->rsrcClumpSize
;
3195 else if ( (alternateVolumeHeader
->rsrcClumpSize
> 0) &&
3196 (alternateVolumeHeader
->rsrcClumpSize
<= kMaxClumpSize
) &&
3197 ((alternateVolumeHeader
->rsrcClumpSize
% vcb
->vcbBlockSize
) == 0) )
3198 vcb
->vcbRsrcClumpSize
= alternateVolumeHeader
->rsrcClumpSize
;
3199 else if (4ULL * vcb
->vcbBlockSize
<= kMaxClumpSize
)
3200 vcb
->vcbRsrcClumpSize
= 4 * vcb
->vcbBlockSize
;
3202 vcb
->vcbRsrcClumpSize
= vcb
->vcbBlockSize
; /* for very large volumes, just use 1 allocation block */
3204 if ( vcb
->vcbRsrcClumpSize
> kMaxClumpSize
)
3205 vcb
->vcbRsrcClumpSize
= vcb
->vcbBlockSize
; /* for very large volumes, just use 1 allocation block */
3207 if ( (volumeHeader
->dataClumpSize
> 0) && (volumeHeader
->dataClumpSize
<= kMaxClumpSize
) &&
3208 ((volumeHeader
->dataClumpSize
% vcb
->vcbBlockSize
) == 0) )
3209 vcb
->vcbDataClumpSize
= volumeHeader
->dataClumpSize
;
3210 else if ( (alternateVolumeHeader
->dataClumpSize
> 0) &&
3211 (alternateVolumeHeader
->dataClumpSize
<= kMaxClumpSize
) &&
3212 ((alternateVolumeHeader
->dataClumpSize
% vcb
->vcbBlockSize
) == 0) )
3213 vcb
->vcbDataClumpSize
= alternateVolumeHeader
->dataClumpSize
;
3214 else if (4ULL * vcb
->vcbBlockSize
<= kMaxClumpSize
)
3215 vcb
->vcbDataClumpSize
= 4 * vcb
->vcbBlockSize
;
3217 vcb
->vcbDataClumpSize
= vcb
->vcbBlockSize
; /* for very large volumes, just use 1 allocation block */
3219 if ( vcb
->vcbDataClumpSize
> kMaxClumpSize
)
3220 vcb
->vcbDataClumpSize
= vcb
->vcbBlockSize
; /* for very large volumes, just use 1 allocation block */
3222 /* Verify next CNode ID.
3223 * If volumeHeader->nextCatalogID < vcb->vcbNextCatalogID, probably
3224 * nextCatalogID has wrapped around.
3225 * If volumeHeader->nextCatalogID > vcb->vcbNextCatalogID, probably
3226 * many files were created and deleted, followed by no new file
3229 if ( (volumeHeader
->nextCatalogID
> vcb
->vcbNextCatalogID
) )
3230 vcb
->vcbNextCatalogID
= volumeHeader
->nextCatalogID
;
3232 //¥¥TBD location and unicode? volumename
3233 // verify the volume name
3234 result
= ChkCName( GPtr
, (const CatalogName
*) &foundKey
.hfsPlus
.nodeName
, isHFSPlus
);
3236 // verify last backup date and backup seqence number
3237 vcb
->vcbBackupDate
= volumeHeader
->backupDate
; /* don't change last backup date */
3239 // verify write count
3240 vcb
->vcbWriteCount
= volumeHeader
->writeCount
; /* don't change write count */
3242 // check out extent file clump size
3243 if ( ((volumeHeader
->extentsFile
.clumpSize
% vcb
->vcbBlockSize
) == 0) &&
3244 (volumeHeader
->extentsFile
.clumpSize
<= maxClump
) )
3245 vcb
->vcbExtentsFile
->fcbClumpSize
= volumeHeader
->extentsFile
.clumpSize
;
3246 else if ( ((alternateVolumeHeader
->extentsFile
.clumpSize
% vcb
->vcbBlockSize
) == 0) &&
3247 (alternateVolumeHeader
->extentsFile
.clumpSize
<= maxClump
) )
3248 vcb
->vcbExtentsFile
->fcbClumpSize
= alternateVolumeHeader
->extentsFile
.clumpSize
;
3250 vcb
->vcbExtentsFile
->fcbClumpSize
=
3251 (alternateVolumeHeader
->extentsFile
.extents
[0].blockCount
* vcb
->vcbBlockSize
);
3253 // check out catalog file clump size
3254 if ( ((volumeHeader
->catalogFile
.clumpSize
% vcb
->vcbBlockSize
) == 0) &&
3255 (volumeHeader
->catalogFile
.clumpSize
<= maxClump
) )
3256 vcb
->vcbCatalogFile
->fcbClumpSize
= volumeHeader
->catalogFile
.clumpSize
;
3257 else if ( ((alternateVolumeHeader
->catalogFile
.clumpSize
% vcb
->vcbBlockSize
) == 0) &&
3258 (alternateVolumeHeader
->catalogFile
.clumpSize
<= maxClump
) )
3259 vcb
->vcbCatalogFile
->fcbClumpSize
= alternateVolumeHeader
->catalogFile
.clumpSize
;
3261 vcb
->vcbCatalogFile
->fcbClumpSize
=
3262 (alternateVolumeHeader
->catalogFile
.extents
[0].blockCount
* vcb
->vcbBlockSize
);
3264 // check out allocations file clump size
3265 if ( ((volumeHeader
->allocationFile
.clumpSize
% vcb
->vcbBlockSize
) == 0) &&
3266 (volumeHeader
->allocationFile
.clumpSize
<= maxClump
) )
3267 vcb
->vcbAllocationFile
->fcbClumpSize
= volumeHeader
->allocationFile
.clumpSize
;
3268 else if ( ((alternateVolumeHeader
->allocationFile
.clumpSize
% vcb
->vcbBlockSize
) == 0) &&
3269 (alternateVolumeHeader
->allocationFile
.clumpSize
<= maxClump
) )
3270 vcb
->vcbAllocationFile
->fcbClumpSize
= alternateVolumeHeader
->allocationFile
.clumpSize
;
3272 vcb
->vcbAllocationFile
->fcbClumpSize
=
3273 (alternateVolumeHeader
->allocationFile
.extents
[0].blockCount
* vcb
->vcbBlockSize
);
3275 // check out attribute file clump size
3276 if (vcb
->vcbAttributesFile
) {
3277 if ( ((volumeHeader
->attributesFile
.clumpSize
% vcb
->vcbBlockSize
) == 0) &&
3278 (volumeHeader
->attributesFile
.clumpSize
<= maxClump
) &&
3279 (volumeHeader
->attributesFile
.clumpSize
!= 0))
3280 vcb
->vcbAttributesFile
->fcbClumpSize
= volumeHeader
->attributesFile
.clumpSize
;
3281 else if ( ((alternateVolumeHeader
->attributesFile
.clumpSize
% vcb
->vcbBlockSize
) == 0) &&
3282 (alternateVolumeHeader
->attributesFile
.clumpSize
<= maxClump
) &&
3283 (alternateVolumeHeader
->attributesFile
.clumpSize
!= 0))
3284 vcb
->vcbAttributesFile
->fcbClumpSize
= alternateVolumeHeader
->attributesFile
.clumpSize
;
3285 else if (vcb
->vcbCatalogFile
->fcbClumpSize
!= 0)
3286 // The original attribute clump may be too small, use catalog's
3287 vcb
->vcbAttributesFile
->fcbClumpSize
= vcb
->vcbCatalogFile
->fcbClumpSize
;
3289 vcb
->vcbAttributesFile
->fcbClumpSize
=
3290 alternateVolumeHeader
->attributesFile
.extents
[0].blockCount
* vcb
->vcbBlockSize
;
3293 CopyMemory( volumeHeader
->finderInfo
, vcb
->vcbFinderInfo
, sizeof(vcb
->vcbFinderInfo
) );
3295 // Now compare verified Volume Header info (in the form of a vcb) with Volume Header info on disk
3296 result
= CompareVolumeHeader( GPtr
, volumeHeader
);
3298 // check to see that embedded volume info is correct in both wrapper MDBs
3299 CheckEmbeddedVolInfoInMDBs( GPtr
);
3304 HFSMasterDirectoryBlock
*mdbP
;
3305 HFSMasterDirectoryBlock
*alternateMDB
;
3308 // get volume name from BTree Key
3311 alternateMDB
= (HFSMasterDirectoryBlock
*) altBlock
.buffer
;
3312 mdbP
= (HFSMasterDirectoryBlock
*) priBlock
.buffer
;
3314 maxClump
= (UInt64
) (vcb
->vcbTotalBlocks
/ 4) * vcb
->vcbBlockSize
; /* max clump = 1/4 volume size */
3316 // check out creation and last mod dates
3317 vcb
->vcbCreateDate
= alternateMDB
->drCrDate
; /* use creation date in alt MDB */
3318 vcb
->vcbModifyDate
= mdbP
->drLsMod
; /* don't change last mod date */
3320 // verify volume attribute flags
3321 if ( (mdbP
->drAtrb
& VAtrb_Msk
) == 0 )
3322 vcb
->vcbAttributes
= mdbP
->drAtrb
;
3324 vcb
->vcbAttributes
= VAtrb_DFlt
;
3326 // verify allocation map ptr
3327 if ( mdbP
->drAllocPtr
< vcb
->vcbTotalBlocks
)
3328 vcb
->vcbNextAllocation
= mdbP
->drAllocPtr
;
3330 vcb
->vcbNextAllocation
= 0;
3332 // verify default clump size
3333 if ( (mdbP
->drClpSiz
> 0) &&
3334 (mdbP
->drClpSiz
<= maxClump
) &&
3335 ((mdbP
->drClpSiz
% vcb
->vcbBlockSize
) == 0) )
3336 vcb
->vcbDataClumpSize
= mdbP
->drClpSiz
;
3337 else if ( (alternateMDB
->drClpSiz
> 0) &&
3338 (alternateMDB
->drClpSiz
<= maxClump
) &&
3339 ((alternateMDB
->drClpSiz
% vcb
->vcbBlockSize
) == 0) )
3340 vcb
->vcbDataClumpSize
= alternateMDB
->drClpSiz
;
3342 vcb
->vcbDataClumpSize
= 4 * vcb
->vcbBlockSize
;
3344 if ( vcb
->vcbDataClumpSize
> kMaxClumpSize
)
3345 vcb
->vcbDataClumpSize
= vcb
->vcbBlockSize
; /* for very large volumes, just use 1 allocation block */
3347 // verify next CNode ID
3348 if ( (mdbP
->drNxtCNID
> vcb
->vcbNextCatalogID
) && (mdbP
->drNxtCNID
<= (vcb
->vcbNextCatalogID
+ 4096)) )
3349 vcb
->vcbNextCatalogID
= mdbP
->drNxtCNID
;
3351 // verify the volume name
3352 result
= ChkCName( GPtr
, (const CatalogName
*) &vcb
->vcbVN
, isHFSPlus
);
3353 if ( result
== noErr
)
3354 if ( CmpBlock( mdbP
->drVN
, vcb
->vcbVN
, vcb
->vcbVN
[0] + 1 ) == 0 )
3355 CopyMemory( mdbP
->drVN
, vcb
->vcbVN
, kHFSMaxVolumeNameChars
+ 1 ); /* ...we have a good one */
3357 // verify last backup date and backup seqence number
3358 vcb
->vcbBackupDate
= mdbP
->drVolBkUp
; /* don't change last backup date */
3359 vcb
->vcbVSeqNum
= mdbP
->drVSeqNum
; /* don't change last backup sequence # */
3361 // verify write count
3362 vcb
->vcbWriteCount
= mdbP
->drWrCnt
; /* don't change write count */
3364 // check out extent file and catalog clump sizes
3365 if ( ((mdbP
->drXTClpSiz
% vcb
->vcbBlockSize
) == 0) && (mdbP
->drXTClpSiz
<= maxClump
) )
3366 vcb
->vcbExtentsFile
->fcbClumpSize
= mdbP
->drXTClpSiz
;
3367 else if ( ((alternateMDB
->drXTClpSiz
% vcb
->vcbBlockSize
) == 0) && (alternateMDB
->drXTClpSiz
<= maxClump
) )
3368 vcb
->vcbExtentsFile
->fcbClumpSize
= alternateMDB
->drXTClpSiz
;
3370 vcb
->vcbExtentsFile
->fcbClumpSize
= (alternateMDB
->drXTExtRec
[0].blockCount
* vcb
->vcbBlockSize
);
3372 if ( ((mdbP
->drCTClpSiz
% vcb
->vcbBlockSize
) == 0) && (mdbP
->drCTClpSiz
<= maxClump
) )
3373 vcb
->vcbCatalogFile
->fcbClumpSize
= mdbP
->drCTClpSiz
;
3374 else if ( ((alternateMDB
->drCTClpSiz
% vcb
->vcbBlockSize
) == 0) && (alternateMDB
->drCTClpSiz
<= maxClump
) )
3375 vcb
->vcbCatalogFile
->fcbClumpSize
= alternateMDB
->drCTClpSiz
;
3377 vcb
->vcbCatalogFile
->fcbClumpSize
= (alternateMDB
->drCTExtRec
[0].blockCount
* vcb
->vcbBlockSize
);
3379 // just copy Finder info for now
3380 CopyMemory(mdbP
->drFndrInfo
, vcb
->vcbFinderInfo
, sizeof(mdbP
->drFndrInfo
));
3382 // now compare verified MDB info with MDB info on disk
3383 result
= CmpMDB( GPtr
, mdbP
);
3387 if (priBlock
.buffer
)
3388 (void) ReleaseVolumeBlock(vcb
, &priBlock
, kReleaseBlock
);
3389 if (altBlock
.buffer
)
3390 (void) ReleaseVolumeBlock(vcb
, &altBlock
, kReleaseBlock
);
3394 } /* end of VInfoChk */
3397 /*------------------------------------------------------------------------------
3399 Function: VLockedChk - (Volume Name Locked Check)
3401 Function: Makes sure the volume name isn't locked. If it is locked, generate a repair order.
3403 This function is not called if file sharing is operating.
3405 Input: GPtr - pointer to scavenger global area
3407 Output: VInfoChk - function result:
3410 ------------------------------------------------------------------------------*/
3412 OSErr
VLockedChk( SGlobPtr GPtr
)
3415 CatalogKey foundKey
;
3416 CatalogRecord record
;
3421 SVCB
*calculatedVCB
= GPtr
->calculatedVCB
;
3422 VolumeObjectPtr myVOPtr
;
3424 myVOPtr
= GetVolumeObjectPtr( );
3425 isHFSPlus
= VolumeObjectIsHFSPlus( );
3426 GPtr
->TarID
= kHFSCatalogFileID
; /* target = catalog file */
3427 GPtr
->TarBlock
= 0; /* no target block yet */
3430 // locate the catalog record for the root directory
3432 result
= GetBTreeRecord( GPtr
->calculatedCatalogFCB
, 0x8001, &foundKey
, &record
, &recSize
, &hint
);
3436 RcdError( GPtr
, E_EntryNotFound
);
3437 return( E_EntryNotFound
);
3440 // put the volume name in the VCB
3441 if ( isHFSPlus
== false )
3443 CopyMemory( foundKey
.hfs
.nodeName
, calculatedVCB
->vcbVN
, sizeof(calculatedVCB
->vcbVN
) );
3445 else if ( myVOPtr
->volumeType
!= kPureHFSPlusVolumeType
)
3447 HFSMasterDirectoryBlock
*mdbP
;
3448 BlockDescriptor block
;
3450 block
.buffer
= NULL
;
3451 if ( (myVOPtr
->flags
& kVO_PriMDBOK
) != 0 )
3452 result
= GetVolumeObjectPrimaryMDB( &block
);
3454 result
= GetVolumeObjectAlternateMDB( &block
);
3455 if ( result
== noErr
) {
3456 mdbP
= (HFSMasterDirectoryBlock
*) block
.buffer
;
3457 CopyMemory( mdbP
->drVN
, calculatedVCB
->vcbVN
, sizeof(mdbP
->drVN
) );
3459 if ( block
.buffer
!= NULL
)
3460 (void) ReleaseVolumeBlock(calculatedVCB
, &block
, kReleaseBlock
);
3461 ReturnIfError(result
);
3463 else // Because we don't have the unicode converters, just fill it with a dummy name.
3465 CopyMemory( "\x0dPure HFS Plus", calculatedVCB
->vcbVN
, sizeof(Str27
) );
3468 GPtr
->TarBlock
= hint
;
3470 CopyCatalogName( (const CatalogName
*)&foundKey
.hfsPlus
.nodeName
, &GPtr
->CName
, isHFSPlus
);
3472 CopyCatalogName( (const CatalogName
*)&foundKey
.hfs
.nodeName
, &GPtr
->CName
, isHFSPlus
);
3474 if ( (record
.recordType
== kHFSPlusFolderRecord
) || (record
.recordType
== kHFSFolderRecord
) )
3476 frFlags
= record
.recordType
== kHFSPlusFolderRecord
?
3477 record
.hfsPlusFolder
.userInfo
.frFlags
:
3478 record
.hfsFolder
.userInfo
.frFlags
;
3480 if ( frFlags
& fNameLocked
) // name locked bit set?
3481 RcdNameLockedErr( GPtr
, E_LockedDirName
, frFlags
);
3488 /*------------------------------------------------------------------------------
3490 Name: RcdNameLockedErr
3492 Function: Allocates a RepairOrder node and linkg it into the 'GPtr->RepairP'
3493 list, to describe an locked volume name for possible repair.
3495 Input: GPtr - ptr to scavenger global data
3496 type - error code (E_xxx), which should be >0
3497 incorrect - the incorrect file flags as found in file record
3499 Output: 0 - no error
3500 R_NoMem - not enough mem to allocate record
3501 ------------------------------------------------------------------------------*/
3503 static int RcdNameLockedErr( SGlobPtr GPtr
, SInt16 type
, UInt32 incorrect
) /* for a consistency check */
3505 RepairOrderPtr p
; /* the new node we compile */
3506 int n
; /* size of node we allocate */
3509 isHFSPlus
= VolumeObjectIsHFSPlus( );
3510 RcdError( GPtr
, type
); /* first, record the error */
3512 n
= CatalogNameSize( &GPtr
->CName
, isHFSPlus
);
3514 p
= AllocMinorRepairOrder( GPtr
, n
); /* get the node */
3515 if ( p
==NULL
) /* quit if out of room */
3518 CopyCatalogName( (const CatalogName
*) &GPtr
->CName
, (CatalogName
*)&p
->name
, isHFSPlus
);
3520 p
->type
= type
; /* save error info */
3521 p
->correct
= incorrect
& ~fNameLocked
; /* mask off the name locked bit */
3522 p
->incorrect
= incorrect
;
3523 p
->maskBit
= (UInt16
)fNameLocked
;
3526 GPtr
->CatStat
|= S_LockedDirName
; /* set flag to trigger repair */
3528 return( noErr
); /* successful return */
3531 /*------------------------------------------------------------------------------
3533 Name: RecordBadExtent
3535 Function: Allocates a RepairOrder for repairing bad extent.
3537 Input: GPtr - ptr to scavenger global data
3538 fileID - fileID of the file with bad extent
3539 forkType - bad extent's fork type
3540 startBlock - start block of the bad extent record
3541 badExtentIndex - index of bad extent entry in the extent record
3543 Output: 0 - no error
3544 R_NoMem - not enough mem to allocate record
3545 ------------------------------------------------------------------------------*/
3547 static int RecordBadExtent(SGlobPtr GPtr
, UInt32 fileID
, UInt8 forkType
,
3548 UInt32 startBlock
, UInt32 badExtentIndex
)
3553 isHFSPlus
= VolumeObjectIsHFSPlus();
3555 p
= AllocMinorRepairOrder(GPtr
, 0);
3561 p
->forkType
= forkType
;
3562 p
->correct
= badExtentIndex
;
3563 p
->hint
= startBlock
;
3566 GPtr
->CatStat
|= S_BadExtent
;
3571 * Build a catalog node thread key.
3573 __unused
static void
3574 buildthreadkey(UInt32 parentID
, int std_hfs
, CatalogKey
*key
)
3577 key
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
;
3578 key
->hfs
.reserved
= 0;
3579 key
->hfs
.parentID
= parentID
;
3580 key
->hfs
.nodeName
[0] = 0;
3582 key
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
;
3583 key
->hfsPlus
.parentID
= parentID
;
3584 key
->hfsPlus
.nodeName
.length
= 0;
3590 printpath(SGlobPtr GPtr
, UInt32 fileID
)
3593 char path
[PATH_MAX
* 4];
3594 unsigned int pathlen
= PATH_MAX
* 4;
3596 if (fileID
< kHFSFirstUserCatalogNodeID
) {
3598 case kHFSExtentsFileID
:
3599 printf("$Extents_Overflow_File\n");
3601 case kHFSCatalogFileID
:
3602 printf("$Catalog_File\n");
3604 case kHFSAllocationFileID
:
3605 printf("$Allocation_Bitmap_File\n");
3607 case kHFSAttributesFileID
:
3608 printf("$Attributes_File\n");
3611 printf("$File_ID_%d\n", fileID
);
3616 result
= GetFileNamePathByID(GPtr
, fileID
, path
, &pathlen
, NULL
, NULL
, NULL
);
3618 printf ("error %d getting path for id=%u\n", result
, fileID
);
3621 printf("\"ROOT_OF_VOLUME%s\" (file id=%u)\n", path
, fileID
);
3625 CheckPhysicalMatch(SVCB
*vcb
, UInt32 startblk
, UInt32 blkcount
, UInt32 fileNumber
, UInt8 forkType
)
3628 u_int64_t blk
, blk1
, blk2
;
3631 offset
= (u_int64_t
) startblk
* (u_int64_t
) vcb
->vcbBlockSize
;
3633 if (vcb
->vcbSignature
== kHFSPlusSigWord
)
3634 offset
+= vcb
->vcbEmbeddedOffset
; // offset into the wrapper
3636 offset
+= vcb
->vcbAlBlSt
* 512ULL; // offset to start of volume
3638 blk1
= offset
/ gBlockSize
;
3639 blk2
= blk1
+ ((blkcount
* vcb
->vcbBlockSize
) / gBlockSize
);
3641 for (i
= 0; i
< gBlkListEntries
; ++i
) {
3642 blk
= gBlockList
[i
];
3644 if (blk
>= blk1
&& blk
< blk2
) {
3645 // printf("block %d is in file %d\n", blk, fileNumber);
3646 /* Do we need to grow the found blocks list? */
3647 if (gFoundBlockEntries
% FOUND_BLOCKS_QUANTUM
== 0) {
3648 struct found_blocks
*new_blocks
;
3649 new_blocks
= realloc(gFoundBlocksList
, (gFoundBlockEntries
+ FOUND_BLOCKS_QUANTUM
) * sizeof(struct found_blocks
));
3650 if (new_blocks
== NULL
) {
3651 fprintf(stderr
, "CheckPhysicalMatch: Out of memory!\n");
3654 gFoundBlocksList
= new_blocks
;
3656 gFoundBlocksList
[gFoundBlockEntries
].block
= blk
;
3657 gFoundBlocksList
[gFoundBlockEntries
].fileID
= fileNumber
;
3658 ++gFoundBlockEntries
;
3663 static int compare_found_blocks(const void *x1_arg
, const void *x2_arg
)
3665 const struct found_blocks
*x1
= x1_arg
;
3666 const struct found_blocks
*x2
= x2_arg
;
3668 if (x1
->block
< x2
->block
)
3670 else if (x1
->block
> x2
->block
)
3673 if (x1
->fileID
< x2
->fileID
)
3675 else if (x1
->fileID
> x2
->fileID
)
3683 dumpblocklist(SGlobPtr GPtr
)
3688 /* Sort the found blocks */
3689 qsort(gFoundBlocksList
, gFoundBlockEntries
, sizeof(struct found_blocks
), compare_found_blocks
);
3692 * Print out the blocks with matching files. In the case of overlapped
3693 * extents, the same block number will be printed multiple times, with
3694 * each file containing an overlapping extent. If overlapping extents
3695 * come from the same file, then that path will be printed multiple times.
3697 for (i
= 0; i
< gFoundBlockEntries
; ++i
) {
3698 block
= gFoundBlocksList
[i
].block
;
3700 printf("block %llu:\t", (unsigned long long) block
);
3701 printpath(GPtr
, gFoundBlocksList
[i
].fileID
);
3703 /* Remove block from the gBlockList */
3704 for (j
= 0; j
< gBlkListEntries
; ++j
) {
3705 if (gBlockList
[j
] == block
) {
3706 gBlockList
[j
] = gBlockList
[--gBlkListEntries
];
3712 /* Print out the blocks without matching files */
3713 for (j
= 0; j
< gBlkListEntries
; ++j
) {
3714 printf("block %llu:\t*** NO MATCH ***\n", (unsigned long long) gBlockList
[j
]);
3718 /*------------------------------------------------------------------------------
3720 Function: CheckFileExtents - (Check File Extents)
3723 Verifies the extent info for a file data or extented attribute data. It
3724 checks the correctness of extent data. If the extent information is
3725 correct/valid, it updates in-memory volume bitmap, total number of valid
3726 blocks for given file, and if overlapping extents exist, adds them to
3727 the overlap extents list. If the extent information is not correct, it
3728 considers the file truncated beyond the bad extent entry and reports
3729 only the total number of good blocks seen. Therefore the caller detects
3730 adds the extent information to repair order. It does not include the
3731 invalid extent and any extents after it for checking volume bitmap and
3732 hence overlapping extents. Note that currently the function
3733 returns error if invalid extent is found for system files or for
3734 extended attributes.
3736 For data fork and resource fork of file - This function checks extent
3737 record present in catalog record as well as extent overflow records, if
3738 any, for given fileID.
3740 For extended attribute data - This function only checks the extent record
3741 passed as parameter. If any extended attribute has overflow extents in
3742 the attribute btree, this function does not look them up. It is the left
3743 to the caller to check remaining extents for given file's extended attribute.
3746 GPtr - pointer to scavenger global area
3747 fileNumber - file number for fork/extended attribute
3748 forkType - fork type
3749 00 - kDataFork - data fork
3750 01 - kEAData - extended attribute data extent
3751 ff - kRsrcFork - resource fork
3752 attrname - if fork type is kEAData, attrname contains pointer to the
3753 name of extended attribute whose extent is being checked; else
3754 it should be NULL. Note that the function assumes that this is
3755 NULL-terminated string.
3756 extents - ptr to 1st extent record for the file
3759 CheckFileExtents - function result:
3762 blocksUsed - number of allocation blocks allocated to the file
3763 ------------------------------------------------------------------------------*/
3765 OSErr
CheckFileExtents( SGlobPtr GPtr
, UInt32 fileNumber
, UInt8 forkType
,
3766 const unsigned char *attrname
, const void *extents
,
3769 UInt32 blockCount
= 0;
3770 UInt32 extentBlockCount
;
3771 UInt32 extentStartBlock
;
3773 HFSPlusExtentKey key
;
3774 HFSPlusExtentKey extentKey
;
3775 HFSPlusExtentRecord extentRecord
;
3779 Boolean firstRecord
;
3781 unsigned int lastExtentIndex
;
3782 Boolean foundBadExtent
;
3784 /* For all extended attribute extents, the attrname should not be NULL */
3785 if (forkType
== kEAData
) {
3786 assert(attrname
!= NULL
);
3789 isHFSPlus
= VolumeObjectIsHFSPlus( );
3791 foundBadExtent
= false;
3792 lastExtentIndex
= GPtr
->numExtents
;
3794 while ( (extents
!= nil
) && (err
== noErr
) )
3796 // checkout the extent record first
3797 err
= ChkExtRec( GPtr
, fileNumber
, extents
, &lastExtentIndex
);
3799 DPRINTF (d_info
, "%s: Bad extent for fileID %u in extent %u for startblock %u\n", __FUNCTION__
, fileNumber
, lastExtentIndex
, blockCount
);
3800 if (cur_debug_level
& d_dump_record
)
3803 HexDump(extents
, sizeof(HFSPlusExtentRecord
), FALSE
);
3807 /* Stop verification if bad extent is found for system file or EA */
3808 if ((fileNumber
< kHFSFirstUserCatalogNodeID
) ||
3809 (forkType
== kEAData
)) {
3813 /* store information about bad extent in repair order */
3814 (void) RecordBadExtent(GPtr
, fileNumber
, forkType
, blockCount
, lastExtentIndex
);
3815 foundBadExtent
= true;
3819 /* Check only till the last valid extent entry reported by ChkExtRec */
3820 for ( i
=0 ; i
<lastExtentIndex
; i
++ ) // now checkout the extents
3822 // HFS+/HFS moving extent fields into local variables for evaluation
3823 if ( isHFSPlus
== true )
3825 extentBlockCount
= ((HFSPlusExtentDescriptor
*)extents
)[i
].blockCount
;
3826 extentStartBlock
= ((HFSPlusExtentDescriptor
*)extents
)[i
].startBlock
;
3830 extentBlockCount
= ((HFSExtentDescriptor
*)extents
)[i
].blockCount
;
3831 extentStartBlock
= ((HFSExtentDescriptor
*)extents
)[i
].startBlock
;
3834 if ( extentBlockCount
== 0 )
3837 if (gBlkListEntries
!= 0)
3838 CheckPhysicalMatch(GPtr
->calculatedVCB
, extentStartBlock
, extentBlockCount
, fileNumber
, forkType
);
3840 err
= CaptureBitmapBits(extentStartBlock
, extentBlockCount
);
3841 if (err
== E_OvlExt
) {
3842 err
= AddExtentToOverlapList(GPtr
, fileNumber
, (char *)attrname
, extentStartBlock
, extentBlockCount
, forkType
);
3845 blockCount
+= extentBlockCount
;
3848 if ( fileNumber
== kHFSExtentsFileID
) // Extents file has no overflow extents
3851 /* Found bad extent for this file, do not find any extents after
3852 * current extent. We assume that the file is truncated at the
3855 if (foundBadExtent
== true) {
3859 /* For extended attributes, only check the extent passed as parameter. The
3860 * caller will take care of checking other extents, if any, for given
3861 * extended attribute.
3863 if (forkType
== kEAData
) {
3867 if ( firstRecord
== true )
3869 firstRecord
= false;
3871 // Set up the extent key
3872 BuildExtentKey( isHFSPlus
, forkType
, fileNumber
, blockCount
, (void *)&key
);
3874 err
= SearchBTreeRecord( GPtr
->calculatedExtentsFCB
, &key
, kNoHint
, (void *) &extentKey
, (void *) &extentRecord
, &recSize
, &hint
);
3876 if ( err
== btNotFound
)
3878 err
= noErr
; // no more extent records
3882 else if ( err
!= noErr
)
3884 err
= IntError( GPtr
, err
); // error from SearchBTreeRecord
3890 err
= GetBTreeRecord( GPtr
->calculatedExtentsFCB
, 1, &extentKey
, extentRecord
, &recSize
, &hint
);
3892 if ( err
== btNotFound
)
3894 err
= noErr
; // no more extent records
3898 else if ( err
!= noErr
)
3900 err
= IntError( GPtr
, err
); /* error from BTGetRecord */
3904 // Check same file and fork
3907 if ( (extentKey
.fileID
!= fileNumber
) || (extentKey
.forkType
!= forkType
) )
3912 if ( (((HFSExtentKey
*) &extentKey
)->fileID
!= fileNumber
) || (((HFSExtentKey
*) &extentKey
)->forkType
!= forkType
) )
3917 extents
= (void *) &extentRecord
;
3920 *blocksUsed
= blockCount
;
3926 void BuildExtentKey( Boolean isHFSPlus
, UInt8 forkType
, HFSCatalogNodeID fileNumber
, UInt32 blockNumber
, void * key
)
3930 HFSPlusExtentKey
*hfsPlusKey
= (HFSPlusExtentKey
*) key
;
3932 hfsPlusKey
->keyLength
= kHFSPlusExtentKeyMaximumLength
;
3933 hfsPlusKey
->forkType
= forkType
;
3934 hfsPlusKey
->pad
= 0;
3935 hfsPlusKey
->fileID
= fileNumber
;
3936 hfsPlusKey
->startBlock
= blockNumber
;
3940 HFSExtentKey
*hfsKey
= (HFSExtentKey
*) key
;
3942 hfsKey
->keyLength
= kHFSExtentKeyMaximumLength
;
3943 hfsKey
->forkType
= forkType
;
3944 hfsKey
->fileID
= fileNumber
;
3945 hfsKey
->startBlock
= (UInt16
) blockNumber
;
3952 // Adds this extent to our OverlappedExtentList for later repair.
3954 static OSErr
AddExtentToOverlapList( SGlobPtr GPtr
, HFSCatalogNodeID fileNumber
, const char *attrname
, UInt32 extentStartBlock
, UInt32 extentBlockCount
, UInt8 forkType
)
3956 size_t newHandleSize
;
3957 ExtentInfo extentInfo
;
3958 ExtentsTable
**extentsTableH
;
3961 ClearMemory(&extentInfo
, sizeof(extentInfo
));
3962 extentInfo
.fileID
= fileNumber
;
3963 extentInfo
.startBlock
= extentStartBlock
;
3964 extentInfo
.blockCount
= extentBlockCount
;
3965 extentInfo
.forkType
= forkType
;
3966 /* store the name of extended attribute */
3967 if (forkType
== kEAData
) {
3968 assert(attrname
!= NULL
);
3970 attrlen
= strlen(attrname
) + 1;
3971 extentInfo
.attrname
= malloc(attrlen
);
3972 if (extentInfo
.attrname
== NULL
) {
3975 strlcpy(extentInfo
.attrname
, attrname
, attrlen
);
3978 // If it's uninitialized
3979 if ( GPtr
->overlappedExtents
== nil
)
3981 GPtr
->overlappedExtents
= (ExtentsTable
**) NewHandleClear( sizeof(ExtentsTable
) );
3982 extentsTableH
= GPtr
->overlappedExtents
;
3986 extentsTableH
= GPtr
->overlappedExtents
;
3988 if ( ExtentInfoExists( extentsTableH
, &extentInfo
) == true )
3991 // Grow the Extents table for a new entry.
3992 newHandleSize
= ( sizeof(ExtentInfo
) ) + ( GetHandleSize( (Handle
)extentsTableH
) );
3993 SetHandleSize( (Handle
)extentsTableH
, newHandleSize
);
3996 // Copy the new extents into the end of the table
3997 CopyMemory( &extentInfo
, &((**extentsTableH
).extentInfo
[(**extentsTableH
).count
]), sizeof(ExtentInfo
) );
3999 // Update the overlap extent bit
4000 GPtr
->VIStat
|= S_OverlappingExtents
;
4002 // Update the extent table count
4003 (**extentsTableH
).count
++;
4009 /* Compare if the given extentInfo exsists in the extents table */
4010 static Boolean
ExtentInfoExists( ExtentsTable
**extentsTableH
, ExtentInfo
*extentInfo
)
4013 ExtentInfo
*aryExtentInfo
;
4016 for ( i
= 0 ; i
< (**extentsTableH
).count
; i
++ )
4018 aryExtentInfo
= &((**extentsTableH
).extentInfo
[i
]);
4020 if ( extentInfo
->fileID
== aryExtentInfo
->fileID
)
4022 if ( (extentInfo
->startBlock
== aryExtentInfo
->startBlock
) &&
4023 (extentInfo
->blockCount
== aryExtentInfo
->blockCount
) &&
4024 (extentInfo
->forkType
== aryExtentInfo
->forkType
) )
4026 /* startBlock, blockCount, forkType are same.
4027 * Compare the extended attribute names, if they exist.
4030 /* If no attribute name exists, the two extents are same */
4031 if ((extentInfo
->attrname
== NULL
) &&
4032 (aryExtentInfo
->attrname
== NULL
)) {
4036 /* If only one attribute name exists, the two extents are not same */
4037 if (((extentInfo
->attrname
!= NULL
) && (aryExtentInfo
->attrname
== NULL
)) ||
4038 ((extentInfo
->attrname
== NULL
) && (aryExtentInfo
->attrname
!= NULL
))) {
4042 /* Both attribute name exist. Compare the names */
4043 if (!strcmp(extentInfo
->attrname
, aryExtentInfo
->attrname
)) {
4056 /* Function : DoesOverlap
4059 * This function takes a start block and the count of blocks in a
4060 * given extent and compares it against the list of overlapped
4061 * extents in the global structure.
4062 * This is useful in finding the original files that overlap with
4063 * the files found in catalog btree check. If a file is found
4064 * overlapping, it is added to the overlap list.
4067 * 1. GPtr - global scavenger pointer.
4068 * 2. fileID - file ID being checked.
4069 * 3. attrname - name of extended attribute being checked, should be NULL for regular files
4070 * 4. startBlock - start block in extent.
4071 * 5. blockCount - total number of blocks in extent.
4072 * 6. forkType - type of fork being check (kDataFork, kRsrcFork, kEAData).
4074 * Output: isOverlapped - Boolean value of true or false.
4076 static Boolean
DoesOverlap(SGlobPtr GPtr
, UInt32 fileID
, const char *attrname
, UInt32 startBlock
, UInt32 blockCount
, UInt8 forkType
)
4079 Boolean isOverlapped
= false;
4080 ExtentInfo
*curExtentInfo
;
4081 ExtentsTable
**extentsTableH
= GPtr
->overlappedExtents
;
4083 for (i
= 0; i
< (**extentsTableH
).count
; i
++) {
4084 curExtentInfo
= &((**extentsTableH
).extentInfo
[i
]);
4086 if (curExtentInfo
->startBlock
< startBlock
) {
4087 if ((curExtentInfo
->startBlock
+ curExtentInfo
->blockCount
) > startBlock
) {
4088 isOverlapped
= true;
4091 } else { /* curExtentInfo->startBlock >= startBlock */
4092 if (curExtentInfo
->startBlock
< (startBlock
+ blockCount
)) {
4093 isOverlapped
= true;
4097 } /* for loop Extents Table */
4099 /* Add this extent to overlap list */
4101 AddExtentToOverlapList(GPtr
, fileID
, attrname
, startBlock
, blockCount
, forkType
);
4104 return isOverlapped
;
4107 /* Function : CheckHFSPlusExtentRecords
4110 * For all valid extents, this function calls DoesOverlap to find
4111 * if a given extent is overlapping with another extent existing
4112 * in the overlap list.
4115 * 1. GPtr - global scavenger pointer.
4116 * 2. fileID - file ID being checked.
4117 * 3. attrname - name of extended attribute being checked, should be NULL for regular files
4118 * 4. extent - extent information to check.
4119 * 5. forkType - type of fork being check (kDataFork, kRsrcFork, kEAData).
4123 static void CheckHFSPlusExtentRecords(SGlobPtr GPtr
, UInt32 fileID
, const char *attrname
, HFSPlusExtentRecord extent
, UInt8 forkType
)
4127 /* Check for overlapping extents for all extents in given extent data */
4128 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
4129 if (extent
[i
].startBlock
== 0) {
4132 DoesOverlap(GPtr
, fileID
, attrname
, extent
[i
].startBlock
, extent
[i
].blockCount
, forkType
);
4135 } /* CheckHFSPlusExtentRecords */
4137 /* Function : CheckHFSExtentRecords
4140 * For all valid extents, this function calls DoesOverlap to find
4141 * if a given extent is overlapping with another extent existing
4142 * in the overlap list.
4145 * 1. GPtr - global scavenger pointer.
4146 * 2. fileID - file ID being checked.
4147 * 3. extent - extent information to check.
4148 * 4. forkType - type of fork being check (kDataFork, kRsrcFork).
4152 static void CheckHFSExtentRecords(SGlobPtr GPtr
, UInt32 fileID
, HFSExtentRecord extent
, UInt8 forkType
)
4156 /* Check for overlapping extents for all extents in given extents */
4157 for (i
= 0; i
< kHFSExtentDensity
; i
++) {
4158 if (extent
[i
].startBlock
== 0) {
4161 DoesOverlap(GPtr
, fileID
, NULL
, extent
[i
].startBlock
, extent
[i
].blockCount
, forkType
);
4164 } /* CheckHFSExtentRecords */
4166 /* Function: FindOrigOverlapFiles
4169 * This function is called only if btree check results in
4170 * overlapped extents errors. The btree checks do not find
4171 * out the original files whose extents are overlapping with one
4172 * being reported in its check. This function finds out all the
4173 * original files whose that are being overlapped.
4175 * This function relies on comparison of extents with Overlap list
4176 * created in verify stage. The list is also updated with the
4177 * overlapped extents found in this function.
4179 * 1. Compare extents for all the files located in volume header.
4180 * 2. Traverse catalog btree and compare extents of all files.
4181 * 3. Traverse extents btree and compare extents for all entries.
4183 * Input: GPtr - pointer to global scanvenger area.
4185 * Output: err - function result
4186 * zero means success
4187 * non-zero means failure
4189 int FindOrigOverlapFiles(SGlobPtr GPtr
)
4194 UInt16 selCode
; /* select access pattern for BTree */
4198 CatalogRecord catRecord
;
4201 ExtentRecord extentRecord
;
4202 ExtentKey extentKey
;
4204 HFSPlusAttrRecord attrRecord
;
4205 HFSPlusAttrKey attrKey
;
4206 char attrName
[XATTR_MAXNAMELEN
];
4209 SVCB
*calculatedVCB
= GPtr
->calculatedVCB
;
4211 isHFSPlus
= VolumeObjectIsHFSPlus();
4213 /* Check file extents from volume header */
4215 /* allocation file */
4216 if (calculatedVCB
->vcbAllocationFile
) {
4217 CheckHFSPlusExtentRecords(GPtr
, calculatedVCB
->vcbAllocationFile
->fcbFileID
, NULL
,
4218 calculatedVCB
->vcbAllocationFile
->fcbExtents32
, kDataFork
);
4222 if (calculatedVCB
->vcbExtentsFile
) {
4223 CheckHFSPlusExtentRecords(GPtr
, calculatedVCB
->vcbExtentsFile
->fcbFileID
, NULL
,
4224 calculatedVCB
->vcbExtentsFile
->fcbExtents32
, kDataFork
);
4228 if (calculatedVCB
->vcbCatalogFile
) {
4229 CheckHFSPlusExtentRecords(GPtr
, calculatedVCB
->vcbCatalogFile
->fcbFileID
, NULL
,
4230 calculatedVCB
->vcbCatalogFile
->fcbExtents32
, kDataFork
);
4233 /* attributes file */
4234 if (calculatedVCB
->vcbAttributesFile
) {
4235 CheckHFSPlusExtentRecords(GPtr
, calculatedVCB
->vcbAttributesFile
->fcbFileID
, NULL
,
4236 calculatedVCB
->vcbAttributesFile
->fcbExtents32
, kDataFork
);
4240 if (calculatedVCB
->vcbStartupFile
) {
4241 CheckHFSPlusExtentRecords(GPtr
, calculatedVCB
->vcbStartupFile
->fcbFileID
, NULL
,
4242 calculatedVCB
->vcbStartupFile
->fcbExtents32
, kDataFork
);
4246 if (calculatedVCB
->vcbExtentsFile
) {
4247 CheckHFSExtentRecords(GPtr
, calculatedVCB
->vcbExtentsFile
->fcbFileID
,
4248 calculatedVCB
->vcbExtentsFile
->fcbExtents16
, kDataFork
);
4252 if (calculatedVCB
->vcbCatalogFile
) {
4253 CheckHFSExtentRecords(GPtr
, calculatedVCB
->vcbCatalogFile
->fcbFileID
,
4254 calculatedVCB
->vcbCatalogFile
->fcbExtents16
, kDataFork
);
4258 /* Traverse the catalog btree */
4259 selCode
= 0x8001; /* Get first record from BTree */
4260 err
= GetBTreeRecord(GPtr
->calculatedCatalogFCB
, selCode
, &catKey
, &catRecord
, &recordSize
, &hint
);
4262 goto traverseExtents
;
4264 selCode
= 1; /* Get next record */
4266 if ((catRecord
.recordType
== kHFSPlusFileRecord
) ||
4267 (catRecord
.recordType
== kHFSFileRecord
)) {
4270 /* HFSPlus data fork */
4271 CheckHFSPlusExtentRecords(GPtr
, catRecord
.hfsPlusFile
.fileID
, NULL
,
4272 catRecord
.hfsPlusFile
.dataFork
.extents
, kDataFork
);
4274 /* HFSPlus resource fork */
4275 CheckHFSPlusExtentRecords(GPtr
, catRecord
.hfsPlusFile
.fileID
, NULL
,
4276 catRecord
.hfsPlusFile
.resourceFork
.extents
, kRsrcFork
);
4278 /* HFS data extent */
4279 CheckHFSExtentRecords(GPtr
, catRecord
.hfsFile
.fileID
,
4280 catRecord
.hfsFile
.dataExtents
, kDataFork
);
4282 /* HFS resource extent */
4283 CheckHFSExtentRecords(GPtr
, catRecord
.hfsFile
.fileID
,
4284 catRecord
.hfsFile
.rsrcExtents
, kRsrcFork
);
4288 /* Access the next record */
4289 err
= GetBTreeRecord( GPtr
->calculatedCatalogFCB
, selCode
, &catKey
, &catRecord
, &recordSize
, &hint
);
4290 } while (err
== noErr
);
4293 /* Traverse the extents btree */
4294 selCode
= 0x8001; /* Get first record from BTree */
4295 err
= GetBTreeRecord(GPtr
->calculatedExtentsFCB
, selCode
, &extentKey
, &extentRecord
, &recordSize
, &hint
);
4297 goto traverseAttribute
;
4299 selCode
= 1; /* Get next record */
4302 CheckHFSPlusExtentRecords(GPtr
, extentKey
.hfsPlus
.fileID
, NULL
,
4303 extentRecord
.hfsPlus
, extentKey
.hfsPlus
.forkType
);
4305 CheckHFSExtentRecords(GPtr
, extentKey
.hfs
.fileID
, extentRecord
.hfs
,
4306 extentKey
.hfs
.forkType
);
4309 /* Access the next record */
4310 err
= GetBTreeRecord(GPtr
->calculatedExtentsFCB
, selCode
, &extentKey
, &extentRecord
, &recordSize
, &hint
);
4311 } while (err
== noErr
);
4314 /* Extended attributes are only supported in HFS Plus */
4319 /* Traverse the attribute btree */
4320 selCode
= 0x8001; /* Get first record from BTree */
4321 /* Warning: Attribute record of type kHFSPlusAttrInlineData may be
4322 * truncated on read! (4425232). This function only uses recordType
4323 * field from inline attribute record.
4325 err
= GetBTreeRecord(GPtr
->calculatedAttributesFCB
, selCode
, &attrKey
, &attrRecord
, &recordSize
, &hint
);
4329 selCode
= 1; /* Get next record */
4331 if (attrRecord
.recordType
== kHFSPlusAttrForkData
) {
4332 (void) utf_encodestr(attrKey
.attrName
, attrKey
.attrNameLen
* 2, (unsigned char *)attrName
, &len
, sizeof(attrName
));
4333 attrName
[len
] = '\0';
4335 CheckHFSPlusExtentRecords(GPtr
, attrKey
.fileID
, attrName
, attrRecord
.forkData
.theFork
.extents
, kEAData
);
4336 } else if (attrRecord
.recordType
== kHFSPlusAttrExtents
) {
4337 (void) utf_encodestr(attrKey
.attrName
, attrKey
.attrNameLen
* 2, (unsigned char *)attrName
, &len
, sizeof(attrName
));
4338 attrName
[len
] = '\0';
4340 CheckHFSPlusExtentRecords(GPtr
, attrKey
.fileID
, attrName
, attrRecord
.overflowExtents
.extents
, kEAData
);
4343 /* Access the next record
4344 * Warning: Attribute record of type kHFSPlusAttrInlineData may be
4345 * truncated on read! (4425232). This function only uses recordType
4346 * field from inline attribute record.
4348 err
= GetBTreeRecord(GPtr
->calculatedAttributesFCB
, selCode
, &attrKey
, &attrRecord
, &recordSize
, &hint
);
4349 } while (err
== noErr
);
4352 if (err
== btNotFound
) {
4356 } /* FindOrigOverlapFiles */
4358 /* Function: PrintOverlapFiles
4360 * Description: Print the information about all unique overlapping files.
4361 * 1. Sort the overlap extent in increasing order of fileID
4362 * 2. For every unique fileID, prefix the string with fileID and find the
4363 * filename/path based on fileID.
4364 * If fileID > kHFSFirstUserCatalogNodeID, find path to file
4365 * Else, find name of the system file.
4366 * 3. Print the new string.
4367 * Note that the path is printed only for HFS Plus volumes and not for
4368 * plain HFS volumes. This is done by not allocating buffer for finding
4372 * GPtr - Global scavenger structure pointer.
4377 void PrintOverlapFiles (SGlobPtr GPtr
)
4380 ExtentsTable
**extentsTableH
;
4381 ExtentInfo
*extentInfo
;
4382 unsigned int numOverlapExtents
;
4383 unsigned int buflen
, filepathlen
;
4384 char *filepath
= NULL
;
4390 isHFSPlus
= VolumeObjectIsHFSPlus();
4392 extentsTableH
= GPtr
->overlappedExtents
;
4393 numOverlapExtents
= (**extentsTableH
).count
;
4395 /* Sort the list according to file ID */
4396 qsort((**extentsTableH
).extentInfo
, numOverlapExtents
, sizeof(ExtentInfo
),
4397 CompareExtentFileID
);
4399 buflen
= PATH_MAX
* 4;
4400 /* Allocate buffer to read data */
4402 filepath
= malloc (buflen
);
4405 for (i
= 0; i
< numOverlapExtents
; i
++) {
4406 extentInfo
= &((**extentsTableH
).extentInfo
[i
]);
4408 /* Skip the same fileID */
4409 if (lastID
== extentInfo
->fileID
) {
4413 lastID
= extentInfo
->fileID
;
4417 filepathlen
= buflen
;
4418 if (extentInfo
->fileID
>= kHFSFirstUserCatalogNodeID
) {
4419 /* Lookup the file path */
4420 err
= GetFileNamePathByID (GPtr
, extentInfo
->fileID
, filepath
, &filepathlen
, NULL
, NULL
, NULL
);
4422 /* Get system filename */
4423 err
= GetSystemFileName (extentInfo
->fileID
, filepath
, &filepathlen
);
4427 /* print fileID, filepath */
4428 fsckPrint(GPtr
->context
, E_OvlExt
, extentInfo
->fileID
, filepath
);
4432 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
4433 plog ("\textentType=0x%x, startBlock=0x%x, blockCount=0x%x, attrName=%s\n",
4434 extentInfo
->forkType
, extentInfo
->startBlock
, extentInfo
->blockCount
, extentInfo
->attrname
);
4438 if (printMsg
== false) {
4439 /* print only fileID */
4440 fsckPrint(GPtr
->context
, E_OvlExtID
, extentInfo
->fileID
);
4449 } /* PrintOverlapFiles */
4451 /* Function: CompareExtentFileID
4453 * Description: Compares the fileID from two ExtentInfo and return the
4454 * comparison result. (since we have to arrange in ascending order)
4457 * first and second - void pointers to ExtentInfo structure.
4460 * >0 if first > second
4461 * =0 if first == second
4462 * <0 if first < second
4464 static int CompareExtentFileID(const void *first
, const void *second
)
4466 return (((ExtentInfo
*)first
)->fileID
-
4467 ((ExtentInfo
*)second
)->fileID
);
4468 } /* CompareExtentFileID */
4470 /* Function: journal_replay
4472 * Description: Replay journal on a journaled HFS+ volume. This function
4473 * returns success if the volume is not journaled or the journal was not
4474 * dirty. If there was any error in replaying the journal, a non-zero value
4478 * 0 - success, non-zero - failure.
4480 //int journal_replay(SGlobPtr gptr)
4481 int journal_replay(const char *block_device
)
4488 jfd
= open(block_device
, O_RDWR
);
4492 fplog(stderr
, "Unable to open block device %s: %s", block_device
, strerror(errno
));
4496 retval
= getvfsbyname("hfs", &vfc
);
4503 mib
[1] = vfc
.vfc_typenum
;
4504 mib
[2] = HFS_REPLAY_JOURNAL
;
4506 retval
= sysctl(mib
, 4, NULL
, NULL
, NULL
, 0);