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
));
262 *bsizep
= (UInt32
)blockSize
;
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
;
608 UInt32 maxNumberOfAllocationBlocks
;
609 UInt32 realAllocationBlockSize
;
610 UInt32 realTotalBlocks
;
612 BTreeControlBlock
*btcb
;
613 SVCB
*vcb
= GPtr
->calculatedVCB
;
614 VolumeObjectPtr myVOPtr
;
617 BlockDescriptor myBlockDescriptor
;
620 GPtr
->TarID
= AMDB_FNum
; // target = alt MDB
622 maxNumberOfAllocationBlocks
= 0xFFFFFFFF;
623 realAllocationBlockSize
= 0;
626 myBlockDescriptor
.buffer
= NULL
;
627 myVOPtr
= GetVolumeObjectPtr( );
630 if ( myVOPtr
->totalDeviceSectors
< 3 ) {
631 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
632 plog("\tinvalid device information for volume - total sectors = %qd sector size = %d \n",
633 myVOPtr
->totalDeviceSectors
, myVOPtr
->sectorSize
);
637 GetVolumeObjectBlockNum( &blockNum
);
638 if ( blockNum
== 0 || myVOPtr
->volumeType
== kUnknownVolumeType
) {
639 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
640 plog( "\t%s - unknown volume type \n", __FUNCTION__
);
641 err
= R_BadSig
; /* doesn't bear the HFS signature */
645 // get Volume Header (HFS+) or Master Directory (HFS) block
646 err
= GetVolumeObjectVHBorMDB( &myBlockDescriptor
);
647 if ( err
!= noErr
) {
648 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
649 plog( "\t%s - bad volume header - err %d \n", __FUNCTION__
, err
);
652 myMDBPtr
= (HFSMasterDirectoryBlock
*) myBlockDescriptor
.buffer
;
654 // if this is an HFS (kHFSVolumeType) volume and the MDB indicates this
655 // might contain an embedded HFS+ volume then we need to scan
656 // for an embedded HFS+ volume. I'm told there were some old problems
657 // where we could lose track of the embedded volume.
658 if ( VolumeObjectIsHFS( ) &&
659 (myMDBPtr
->drEmbedSigWord
!= 0 ||
660 myMDBPtr
->drEmbedExtent
.blockCount
!= 0 ||
661 myMDBPtr
->drEmbedExtent
.startBlock
!= 0) ) {
663 err
= ScavengeVolumeType( GPtr
, myMDBPtr
, &myVOPtr
->volumeType
);
664 if ( err
== E_InvalidMDBdrAlBlSt
)
665 err
= RcdMDBEmbededVolDescriptionErr( GPtr
, E_InvalidMDBdrAlBlSt
, myMDBPtr
);
667 if ( VolumeObjectIsEmbeddedHFSPlus( ) ) {
668 // we changed volume types so let's get the VHB
669 (void) ReleaseVolumeBlock( vcb
, &myBlockDescriptor
, kReleaseBlock
);
670 myBlockDescriptor
.buffer
= NULL
;
672 err
= GetVolumeObjectVHB( &myBlockDescriptor
);
673 if ( err
!= noErr
) {
674 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
675 plog( "\t%s - bad volume header - err %d \n", __FUNCTION__
, err
);
676 WriteError( GPtr
, E_InvalidVolumeHeader
, 1, 0 );
677 err
= E_InvalidVolumeHeader
;
681 GetVolumeObjectBlockNum( &blockNum
); // get the new Volume header block number
684 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
685 plog( "\t%s - bad volume header - err %d \n", __FUNCTION__
, err
);
686 WriteError( GPtr
, E_InvalidVolumeHeader
, 1, 0 );
687 err
= E_InvalidVolumeHeader
;
692 totalSectors
= ( VolumeObjectIsEmbeddedHFSPlus( ) ) ? myVOPtr
->totalEmbeddedSectors
: myVOPtr
->totalDeviceSectors
;
694 // indicate what type of volume we are dealing with
695 if ( VolumeObjectIsHFSPlus( ) ) {
697 myVHBPtr
= (HFSPlusVolumeHeader
*) myBlockDescriptor
.buffer
;
698 if (myVHBPtr
->attributes
& kHFSVolumeJournaledMask
) {
699 fsckPrint(GPtr
->context
, hfsJournalVolCheck
);
701 fsckPrint(GPtr
->context
, hfsCheckNoJnl
);
703 GPtr
->numExtents
= kHFSPlusExtentDensity
;
704 vcb
->vcbSignature
= kHFSPlusSigWord
;
706 // Further populate the VCB with VolumeHeader info
707 vcb
->vcbAlBlSt
= myVOPtr
->embeddedOffset
/ 512;
708 vcb
->vcbEmbeddedOffset
= myVOPtr
->embeddedOffset
;
709 realAllocationBlockSize
= myVHBPtr
->blockSize
;
710 realTotalBlocks
= myVHBPtr
->totalBlocks
;
711 vcb
->vcbNextCatalogID
= myVHBPtr
->nextCatalogID
;
712 vcb
->vcbCreateDate
= myVHBPtr
->createDate
;
713 vcb
->vcbAttributes
= myVHBPtr
->attributes
& kHFSCatalogNodeIDsReused
;
715 if ( myVHBPtr
->attributesFile
.totalBlocks
== 0 )
716 vcb
->vcbAttributesFile
= NULL
; /* XXX memory leak ? */
718 // Make sure the Extents B-Tree is set to use 16-bit key lengths.
719 // We access it before completely setting up the control block.
720 btcb
= (BTreeControlBlock
*) vcb
->vcbExtentsFile
->fcbBtree
;
721 btcb
->attributes
|= kBTBigKeysMask
;
723 // catch the case where the volume allocation block count is greater than
724 // maximum number of device allocation blocks. - bug 2916021
725 numABlks
= (UInt32
)(myVOPtr
->totalDeviceSectors
/ ( myVHBPtr
->blockSize
/ Blk_Size
));
726 if ( myVHBPtr
->totalBlocks
> numABlks
) {
727 RcdError( GPtr
, E_NABlks
);
729 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
730 plog( "\t%s - volume header total allocation blocks is greater than device size \n", __FUNCTION__
);
731 plog( "\tvolume allocation block count %d device allocation block count %d \n",
732 myVHBPtr
->totalBlocks
, numABlks
);
737 else if ( VolumeObjectIsHFS( ) ) {
739 // fsckPrint(GPtr->context, fsckCheckingVolume);
740 fsckPrint(GPtr
->context
, hfsCheckHFS
);
742 GPtr
->numExtents
= kHFSExtentDensity
;
743 vcb
->vcbSignature
= myMDBPtr
->drSigWord
;
744 maxNumberOfAllocationBlocks
= 0xFFFF;
745 // set up next file ID, CheckBTreeKey makse sure we are under this value
746 vcb
->vcbNextCatalogID
= myMDBPtr
->drNxtCNID
;
747 vcb
->vcbCreateDate
= myMDBPtr
->drCrDate
;
749 realAllocationBlockSize
= myMDBPtr
->drAlBlkSiz
;
750 realTotalBlocks
= myMDBPtr
->drNmAlBlks
;
753 GPtr
->TarBlock
= blockNum
; // target block
755 // verify volume allocation info
756 // Note: i is the number of sectors per allocation block
757 numBlk
= totalSectors
;
758 minABlkSz
= Blk_Size
; // init minimum ablock size
759 // loop while #ablocks won't fit
760 for( i
= 2; numBlk
> maxNumberOfAllocationBlocks
; i
++ ) {
761 minABlkSz
= i
* Blk_Size
; // jack up minimum
762 numBlk
= totalSectors
/ i
; // recompute #ablocks, assuming this size
765 numABlks
= (UInt32
)numBlk
;
766 vcb
->vcbBlockSize
= realAllocationBlockSize
;
767 numABlks
= (UInt32
)(totalSectors
/ ( realAllocationBlockSize
/ Blk_Size
));
768 if ( VolumeObjectIsHFSPlus( ) ) {
769 // HFS Plus allocation block size must be power of 2
770 if ( (realAllocationBlockSize
< minABlkSz
) ||
771 (realAllocationBlockSize
& (realAllocationBlockSize
- 1)) != 0 )
772 realAllocationBlockSize
= 0;
775 if ( (realAllocationBlockSize
< minABlkSz
) ||
776 (realAllocationBlockSize
> Max_ABSiz
) ||
777 ((realAllocationBlockSize
% Blk_Size
) != 0) )
778 realAllocationBlockSize
= 0;
781 if ( realAllocationBlockSize
== 0 ) {
782 RcdError( GPtr
, E_ABlkSz
);
783 err
= E_ABlkSz
; // bad allocation block size
787 vcb
->vcbTotalBlocks
= realTotalBlocks
;
788 vcb
->vcbFreeBlocks
= 0;
790 // Only do these tests on HFS volumes, since they are either
791 // or, getting the VolumeHeader would have already failed.
792 if ( VolumeObjectIsHFS( ) ) {
793 UInt32 bitMapSizeInSectors
;
795 // Calculate the volume bitmap size
796 bitMapSizeInSectors
= ( numABlks
+ kBitsPerSector
- 1 ) / kBitsPerSector
; // VBM size in blocks
798 //¥¥ Calculate the validaty of HFS Allocation blocks, I think realTotalBlocks == numABlks
799 numABlks
= (UInt32
)((totalSectors
- 3 - bitMapSizeInSectors
) / (realAllocationBlockSize
/ Blk_Size
)); // actual # of alloc blks
801 if ( realTotalBlocks
> numABlks
) {
802 RcdError( GPtr
, E_NABlks
);
803 err
= E_NABlks
; // invalid number of allocation blocks
807 if ( myMDBPtr
->drVBMSt
<= MDB_BlkN
) {
808 RcdError(GPtr
,E_VBMSt
);
809 err
= E_VBMSt
; // invalid VBM start block
812 vcb
->vcbVBMSt
= myMDBPtr
->drVBMSt
;
814 if (myMDBPtr
->drAlBlSt
< (myMDBPtr
->drVBMSt
+ bitMapSizeInSectors
)) {
815 RcdError(GPtr
,E_ABlkSt
);
816 err
= E_ABlkSt
; // invalid starting alloc block
819 vcb
->vcbAlBlSt
= myMDBPtr
->drAlBlSt
;
823 if (myBlockDescriptor
.buffer
!= NULL
)
824 (void) ReleaseVolumeBlock(vcb
, &myBlockDescriptor
, kReleaseBlock
);
830 static OSErr
ScavengeVolumeType( SGlobPtr GPtr
, HFSMasterDirectoryBlock
*mdb
, UInt32
*volumeType
)
835 UInt64 hfsPlusSectors
= 0;
836 UInt32 sectorsPerBlock
;
837 UInt32 numSectorsToSearch
;
839 HFSPlusVolumeHeader
*volumeHeader
;
840 HFSExtentDescriptor embededExtent
;
841 SVCB
*calculatedVCB
= GPtr
->calculatedVCB
;
842 VolumeObjectPtr myVOPtr
;
843 UInt16 embedSigWord
= mdb
->drEmbedSigWord
;
844 BlockDescriptor block
;
847 * If all of the embedded volume information is zero, then assume
848 * this really is a plain HFS disk like it says. Otherwise, if
849 * you reinitialize a large HFS Plus volume as HFS, the original
850 * embedded volume's volume header and alternate volume header will
851 * still be there, and we'll try to repair the embedded volume.
853 if (embedSigWord
== 0 &&
854 mdb
->drEmbedExtent
.blockCount
== 0 &&
855 mdb
->drEmbedExtent
.startBlock
== 0)
857 *volumeType
= kHFSVolumeType
;
861 myVOPtr
= GetVolumeObjectPtr( );
862 *volumeType
= kEmbededHFSPlusVolumeType
; // Assume HFS+
865 // First see if it is an HFS+ volume and the relevent structures look OK
867 if ( embedSigWord
== kHFSPlusSigWord
)
869 /* look for primary volume header */
870 vHSector
= (UInt64
)mdb
->drAlBlSt
+
871 ((UInt64
)(mdb
->drAlBlkSiz
/ Blk_Size
) * (UInt64
)mdb
->drEmbedExtent
.startBlock
) + 2;
873 err
= GetVolumeBlock(calculatedVCB
, vHSector
, kGetBlock
, &block
);
874 volumeHeader
= (HFSPlusVolumeHeader
*) block
.buffer
;
875 if ( err
!= noErr
) goto AssumeHFS
;
877 myVOPtr
->primaryVHB
= vHSector
;
878 err
= ValidVolumeHeader( volumeHeader
);
879 (void) ReleaseVolumeBlock(calculatedVCB
, &block
, kReleaseBlock
);
880 if ( err
== noErr
) {
881 myVOPtr
->flags
|= kVO_PriVHBOK
;
886 sectorsPerBlock
= mdb
->drAlBlkSiz
/ Blk_Size
;
888 // Search the end of the disk to see if a Volume Header is present at all
889 if ( embedSigWord
!= kHFSPlusSigWord
)
891 numSectorsToSearch
= mdb
->drAlBlkSiz
/ Blk_Size
;
892 startSector
= myVOPtr
->totalDeviceSectors
- 4 - numSectorsToSearch
;
894 err
= SeekVolumeHeader( GPtr
, startSector
, numSectorsToSearch
, &altVHSector
);
895 if ( err
!= noErr
) goto AssumeHFS
;
897 // We found the Alt VH, so this must be a damaged embeded HFS+ volume
898 // Now Scavenge for the Primary VolumeHeader
899 myVOPtr
->alternateVHB
= altVHSector
;
900 myVOPtr
->flags
|= kVO_AltVHBOK
;
901 startSector
= mdb
->drAlBlSt
+ (4 * sectorsPerBlock
); // Start looking at 4th HFS allocation block
902 numSectorsToSearch
= 10 * sectorsPerBlock
; // search for VH in next 10 allocation blocks
904 err
= SeekVolumeHeader( GPtr
, startSector
, numSectorsToSearch
, &vHSector
);
905 if ( err
!= noErr
) goto AssumeHFS
;
907 myVOPtr
->primaryVHB
= vHSector
;
908 myVOPtr
->flags
|= kVO_PriVHBOK
;
909 hfsPlusSectors
= altVHSector
- vHSector
+ 1 + 2 + 1; // numSectors + BB + end
911 // Fix the embeded extent
912 embededExtent
.blockCount
= hfsPlusSectors
/ sectorsPerBlock
;
913 embededExtent
.startBlock
= (vHSector
- 2 - mdb
->drAlBlSt
) / sectorsPerBlock
;
914 embedSigWord
= kHFSPlusSigWord
;
916 myVOPtr
->embeddedOffset
=
917 (embededExtent
.startBlock
* mdb
->drAlBlkSiz
) + (mdb
->drAlBlSt
* Blk_Size
);
921 embedSigWord
= mdb
->drEmbedSigWord
;
922 embededExtent
.blockCount
= mdb
->drEmbedExtent
.blockCount
;
923 embededExtent
.startBlock
= mdb
->drEmbedExtent
.startBlock
;
926 if ( embedSigWord
== kHFSPlusSigWord
)
928 startSector
= 2 + mdb
->drAlBlSt
+
929 ((UInt64
)embededExtent
.startBlock
* (mdb
->drAlBlkSiz
/ Blk_Size
));
931 err
= SeekVolumeHeader( GPtr
, startSector
, mdb
->drAlBlkSiz
/ Blk_Size
, &vHSector
);
932 if ( err
!= noErr
) goto AssumeHFS
;
934 // Now replace the bad fields and mark the error
935 mdb
->drEmbedExtent
.blockCount
= embededExtent
.blockCount
;
936 mdb
->drEmbedExtent
.startBlock
= embededExtent
.startBlock
;
937 mdb
->drEmbedSigWord
= kHFSPlusSigWord
;
938 mdb
->drAlBlSt
+= vHSector
- startSector
; // Fix the bad field
939 myVOPtr
->totalEmbeddedSectors
= (mdb
->drAlBlkSiz
/ Blk_Size
) * mdb
->drEmbedExtent
.blockCount
;
940 myVOPtr
->embeddedOffset
=
941 (mdb
->drEmbedExtent
.startBlock
* mdb
->drAlBlkSiz
) + (mdb
->drAlBlSt
* Blk_Size
);
942 myVOPtr
->primaryVHB
= vHSector
;
943 myVOPtr
->flags
|= kVO_PriVHBOK
;
945 GPtr
->VIStat
= GPtr
->VIStat
| S_MDB
; // write out our MDB
946 return( E_InvalidMDBdrAlBlSt
);
950 *volumeType
= kHFSVolumeType
;
953 } /* ScavengeVolumeType */
956 static OSErr
SeekVolumeHeader( SGlobPtr GPtr
, UInt64 startSector
, UInt32 numSectors
, UInt64
*vHSector
)
959 HFSPlusVolumeHeader
*volumeHeader
;
960 SVCB
*calculatedVCB
= GPtr
->calculatedVCB
;
961 BlockDescriptor block
;
963 for ( *vHSector
= startSector
; *vHSector
< startSector
+ numSectors
; (*vHSector
)++ )
965 err
= GetVolumeBlock(calculatedVCB
, *vHSector
, kGetBlock
, &block
);
966 volumeHeader
= (HFSPlusVolumeHeader
*) block
.buffer
;
967 if ( err
!= noErr
) return( err
);
969 err
= ValidVolumeHeader(volumeHeader
);
971 (void) ReleaseVolumeBlock(calculatedVCB
, &block
, kReleaseBlock
);
980 #if 0 // not used at this time
981 static OSErr
CheckWrapperExtents( SGlobPtr GPtr
, HFSMasterDirectoryBlock
*mdb
)
985 // See if Norton Disk Doctor 2.0 corrupted the catalog's first extent
986 if ( mdb
->drCTExtRec
[0].startBlock
>= mdb
->drEmbedExtent
.startBlock
)
988 // Fix the field in the in-memory copy, and record the error
989 mdb
->drCTExtRec
[0].startBlock
= mdb
->drXTExtRec
[0].startBlock
+ mdb
->drXTExtRec
[0].blockCount
;
990 GPtr
->VIStat
= GPtr
->VIStat
| S_MDB
; // write out our MDB
991 err
= RcdInvalidWrapperExtents( GPtr
, E_InvalidWrapperExtents
);
998 /*------------------------------------------------------------------------------
1000 Function: CreateExtentsBTreeControlBlock
1002 Function: Create the calculated ExtentsBTree Control Block
1004 Input: GPtr - pointer to scavenger global area
1006 Output: - 0 = no error
1008 ------------------------------------------------------------------------------*/
1010 OSErr
CreateExtentsBTreeControlBlock( SGlobPtr GPtr
)
1016 BTreeControlBlock
* btcb
;
1018 BlockDescriptor block
;
1022 isHFSPlus
= VolumeObjectIsHFSPlus( );
1023 GPtr
->TarID
= kHFSExtentsFileID
; // target = extent file
1024 GPtr
->TarBlock
= kHeaderNodeNum
; // target block = header node
1025 vcb
= GPtr
->calculatedVCB
;
1026 btcb
= GPtr
->calculatedExtentsBTCB
;
1027 block
.buffer
= NULL
;
1029 // get Volume Header (HFS+) or Master Directory (HFS) block
1030 err
= GetVolumeObjectVHBorMDB( &block
);
1033 // check out allocation info for the Extents File
1037 HFSPlusVolumeHeader
*volumeHeader
;
1039 volumeHeader
= (HFSPlusVolumeHeader
*) block
.buffer
;
1041 CopyMemory(volumeHeader
->extentsFile
.extents
, GPtr
->calculatedExtentsFCB
->fcbExtents32
, sizeof(HFSPlusExtentRecord
) );
1043 err
= CheckFileExtents( GPtr
, kHFSExtentsFileID
, kDataFork
, NULL
, (void *)GPtr
->calculatedExtentsFCB
->fcbExtents32
, &numABlks
); // check out extent info
1047 if ( volumeHeader
->extentsFile
.totalBlocks
!= numABlks
) // check out the PEOF
1049 RcdError( GPtr
, E_ExtPEOF
);
1052 plog("Extents File totalBlocks = %u, numABlks = %u\n", volumeHeader
->extentsFile
.totalBlocks
, numABlks
);
1057 GPtr
->calculatedExtentsFCB
->fcbLogicalSize
= volumeHeader
->extentsFile
.logicalSize
; // Set Extents tree's LEOF
1058 GPtr
->calculatedExtentsFCB
->fcbPhysicalSize
= (UInt64
)volumeHeader
->extentsFile
.totalBlocks
*
1059 (UInt64
)volumeHeader
->blockSize
; // Set Extents tree's PEOF
1063 // Set up the minimal BTreeControlBlock structure
1066 // Read the BTreeHeader from disk & also validate it's node size.
1067 err
= GetBTreeHeader(GPtr
, GPtr
->calculatedExtentsFCB
, &header
);
1070 btcb
->maxKeyLength
= kHFSPlusExtentKeyMaximumLength
; // max key length
1071 btcb
->keyCompareProc
= (void *)CompareExtentKeysPlus
;
1072 btcb
->attributes
|=kBTBigKeysMask
; // HFS+ Extent files have 16-bit key length
1073 btcb
->leafRecords
= header
.leafRecords
;
1074 btcb
->treeDepth
= header
.treeDepth
;
1075 btcb
->rootNode
= header
.rootNode
;
1076 btcb
->firstLeafNode
= header
.firstLeafNode
;
1077 btcb
->lastLeafNode
= header
.lastLeafNode
;
1079 btcb
->nodeSize
= header
.nodeSize
;
1080 btcb
->totalNodes
= (UInt32
)( GPtr
->calculatedExtentsFCB
->fcbPhysicalSize
/ btcb
->nodeSize
);
1081 btcb
->freeNodes
= btcb
->totalNodes
; // start with everything free
1083 // Make sure the header nodes size field is correct by looking at the 1st record offset
1084 err
= CheckNodesFirstOffset( GPtr
, btcb
);
1085 if ( (err
!= noErr
) && (btcb
->nodeSize
!= 1024) ) // default HFS+ Extents node size is 1024
1087 btcb
->nodeSize
= 1024;
1088 btcb
->totalNodes
= (UInt32
)( GPtr
->calculatedExtentsFCB
->fcbPhysicalSize
/ btcb
->nodeSize
);
1089 btcb
->freeNodes
= btcb
->totalNodes
; // start with everything free
1091 err
= CheckNodesFirstOffset( GPtr
, btcb
);
1094 GPtr
->EBTStat
|= S_BTH
; // update the Btree header
1099 HFSMasterDirectoryBlock
*alternateMDB
;
1101 alternateMDB
= (HFSMasterDirectoryBlock
*) block
.buffer
;
1103 CopyMemory(alternateMDB
->drXTExtRec
, GPtr
->calculatedExtentsFCB
->fcbExtents16
, sizeof(HFSExtentRecord
) );
1104 // ExtDataRecToExtents(alternateMDB->drXTExtRec, GPtr->calculatedExtentsFCB->fcbExtents);
1107 err
= CheckFileExtents( GPtr
, kHFSExtentsFileID
, kDataFork
, NULL
, (void *)GPtr
->calculatedExtentsFCB
->fcbExtents16
, &numABlks
); /* check out extent info */
1110 if (alternateMDB
->drXTFlSize
!= ((UInt64
)numABlks
* (UInt64
)GPtr
->calculatedVCB
->vcbBlockSize
))// check out the PEOF
1112 RcdError(GPtr
,E_ExtPEOF
);
1115 plog("Alternate MDB drXTFlSize = %llu, should be %llu\n", (long long)alternateMDB
->drXTFlSize
, (long long)numABlks
* (UInt64
)GPtr
->calculatedVCB
->vcbBlockSize
);
1120 GPtr
->calculatedExtentsFCB
->fcbPhysicalSize
= alternateMDB
->drXTFlSize
; // set up PEOF and EOF in FCB
1121 GPtr
->calculatedExtentsFCB
->fcbLogicalSize
= GPtr
->calculatedExtentsFCB
->fcbPhysicalSize
;
1125 // Set up the minimal BTreeControlBlock structure
1128 // Read the BTreeHeader from disk & also validate it's node size.
1129 err
= GetBTreeHeader(GPtr
, GPtr
->calculatedExtentsFCB
, &header
);
1132 btcb
->maxKeyLength
= kHFSExtentKeyMaximumLength
; // max key length
1133 btcb
->keyCompareProc
= (void *)CompareExtentKeys
;
1134 btcb
->leafRecords
= header
.leafRecords
;
1135 btcb
->treeDepth
= header
.treeDepth
;
1136 btcb
->rootNode
= header
.rootNode
;
1137 btcb
->firstLeafNode
= header
.firstLeafNode
;
1138 btcb
->lastLeafNode
= header
.lastLeafNode
;
1140 btcb
->nodeSize
= header
.nodeSize
;
1141 btcb
->totalNodes
= (UInt32
)(GPtr
->calculatedExtentsFCB
->fcbPhysicalSize
/ btcb
->nodeSize
);
1142 btcb
->freeNodes
= btcb
->totalNodes
; // start with everything free
1144 // Make sure the header nodes size field is correct by looking at the 1st record offset
1145 err
= CheckNodesFirstOffset( GPtr
, btcb
);
1149 if ( header
.btreeType
!= kHFSBTreeType
)
1151 GPtr
->EBTStat
|= S_ReservedBTH
; // Repair reserved fields in Btree header
1155 // set up our DFA extended BTCB area. Will we have enough memory on all HFS+ volumes.
1157 btcb
->refCon
= AllocateClearMemory( sizeof(BTreeExtensionsRec
) ); // allocate space for our BTCB extensions
1158 if ( btcb
->refCon
== nil
) {
1162 size
= (btcb
->totalNodes
+ 7) / 8; // size of BTree bit map
1163 ((BTreeExtensionsRec
*)btcb
->refCon
)->BTCBMPtr
= AllocateClearMemory(size
); // get precleared bitmap
1164 if ( ((BTreeExtensionsRec
*)btcb
->refCon
)->BTCBMPtr
== nil
)
1170 ((BTreeExtensionsRec
*)btcb
->refCon
)->BTCBMSize
= size
; // remember how long it is
1171 ((BTreeExtensionsRec
*)btcb
->refCon
)->realFreeNodeCount
= header
.freeNodes
;// keep track of real free nodes for progress
1173 if ( block
.buffer
!= NULL
)
1174 (void) ReleaseVolumeBlock(vcb
, &block
, kReleaseBlock
);
1181 /*------------------------------------------------------------------------------
1183 Function: CheckNodesFirstOffset
1185 Function: Minimal check verifies that the 1st offset is within bounds. If it's not
1186 the nodeSize may be wrong. In the future this routine could be modified
1187 to try different size values until one fits.
1189 ------------------------------------------------------------------------------*/
1190 #define GetRecordOffset(btreePtr,node,index) (*(short *) ((UInt8 *)(node) + (btreePtr)->nodeSize - ((index) << 1) - kOffsetSize))
1191 static OSErr
CheckNodesFirstOffset( SGlobPtr GPtr
, BTreeControlBlock
*btcb
)
1197 (void) SetFileBlockSize(btcb
->fcbPtr
, btcb
->nodeSize
);
1199 err
= GetNode( btcb
, kHeaderNodeNum
, &nodeRec
);
1203 offset
= GetRecordOffset( btcb
, (NodeDescPtr
)nodeRec
.buffer
, 0 );
1204 if ( (offset
< sizeof (BTNodeDescriptor
)) || // offset < minimum
1205 (offset
& 1) || // offset is odd
1206 (offset
>= btcb
->nodeSize
) ) // offset beyond end of node
1208 if (debug
) fprintf(stderr
, "%s(%d): offset is wrong\n", __FUNCTION__
, __LINE__
);
1209 err
= fsBTInvalidNodeErr
;
1214 RcdError( GPtr
, E_InvalidNodeSize
);
1216 (void) ReleaseNode(btcb
, &nodeRec
);
1223 /*------------------------------------------------------------------------------
1225 Function: ExtBTChk - (Extent BTree Check)
1227 Function: Verifies the extent BTree structure.
1229 Input: GPtr - pointer to scavenger global area
1231 Output: ExtBTChk - function result:
1234 ------------------------------------------------------------------------------*/
1236 OSErr
ExtBTChk( SGlobPtr GPtr
)
1241 GPtr
->TarID
= kHFSExtentsFileID
; // target = extent file
1242 GetVolumeObjectBlockNum( &GPtr
->TarBlock
); // target block = VHB/MDB
1245 // check out the BTree structure
1248 err
= BTCheck(GPtr
, kCalculatedExtentRefNum
, NULL
);
1249 ReturnIfError( err
); // invalid extent file BTree
1252 // check out the allocation map structure
1255 err
= BTMapChk( GPtr
, kCalculatedExtentRefNum
);
1256 ReturnIfError( err
); // Invalid extent BTree map
1259 // Make sure unused nodes in the B-tree are zero filled.
1261 err
= BTCheckUnusedNodes(GPtr
, kCalculatedExtentRefNum
, &GPtr
->EBTStat
);
1262 ReturnIfError( err
);
1265 // compare BTree header record on disk with scavenger's BTree header record
1268 err
= CmpBTH( GPtr
, kCalculatedExtentRefNum
);
1269 ReturnIfError( err
);
1272 // compare BTree map on disk with scavenger's BTree map
1275 err
= CmpBTM( GPtr
, kCalculatedExtentRefNum
);
1282 /*------------------------------------------------------------------------------
1284 Function: BadBlockFileExtentCheck - (Check extents of bad block file)
1287 Verifies the extents of bad block file (kHFSBadBlockFileID) that
1288 exist in extents Btree.
1290 Note that the extents for other file IDs < kHFSFirstUserCatalogNodeID
1291 are being taken care in the following functions:
1293 kHFSExtentsFileID - CreateExtentsBTreeControlBlock
1294 kHFSCatalogFileID - CreateCatalogBTreeControlBlock
1295 kHFSAllocationFileID - CreateExtendedAllocationsFCB
1296 kHFSStartupFileID - CreateExtendedAllocationsFCB
1297 kHFSAttributesFileID - CreateAttributesBTreeControlBlock
1299 Input: GPtr - pointer to scavenger global area
1301 Output: BadBlockFileExtentCheck - function result:
1304 ------------------------------------------------------------------------------*/
1306 OSErr
BadBlockFileExtentCheck( SGlobPtr GPtr
)
1313 BlockDescriptor block
;
1315 isHFSPlus
= VolumeObjectIsHFSPlus( );
1316 block
.buffer
= NULL
;
1319 // process the bad block extents (created by the disk init pkg to hide badspots)
1321 vcb
= GPtr
->calculatedVCB
;
1323 result
= GetVolumeObjectVHBorMDB( &block
);
1324 if ( result
!= noErr
) goto ExitThisRoutine
; // error, could't get it
1326 p
= (void *) block
.buffer
;
1327 attributes
= isHFSPlus
== true ? ((HFSPlusVolumeHeader
*)p
)->attributes
: ((HFSMasterDirectoryBlock
*)p
)->drAtrb
;
1329 //¥¥ Does HFS+ honnor the same mask?
1330 if ( attributes
& kHFSVolumeSparedBlocksMask
) // if any badspots
1332 HFSPlusExtentRecord zeroXdr
; // dummy passed to 'CheckFileExtents'
1333 UInt32 numBadBlocks
;
1335 ClearMemory ( zeroXdr
, sizeof( HFSPlusExtentRecord
) );
1336 result
= CheckFileExtents( GPtr
, kHFSBadBlockFileID
, kDataFork
, NULL
, (void *)zeroXdr
, &numBadBlocks
); // check and mark bitmap
1340 if ( block
.buffer
!= NULL
)
1341 (void) ReleaseVolumeBlock(vcb
, &block
, kReleaseBlock
);
1347 /*------------------------------------------------------------------------------
1349 Function: CreateCatalogBTreeControlBlock
1351 Function: Create the calculated CatalogBTree Control Block
1353 Input: GPtr - pointer to scavenger global area
1355 Output: - 0 = no error
1357 ------------------------------------------------------------------------------*/
1358 OSErr
CreateCatalogBTreeControlBlock( SGlobPtr GPtr
)
1364 BTreeControlBlock
* btcb
;
1366 BlockDescriptor block
;
1370 isHFSPlus
= VolumeObjectIsHFSPlus( );
1371 GPtr
->TarID
= kHFSCatalogFileID
;
1372 GPtr
->TarBlock
= kHeaderNodeNum
;
1373 vcb
= GPtr
->calculatedVCB
;
1374 btcb
= GPtr
->calculatedCatalogBTCB
;
1375 block
.buffer
= NULL
;
1377 err
= GetVolumeObjectVHBorMDB( &block
);
1378 if ( err
!= noErr
) goto ExitThisRoutine
; // error, could't get it
1380 // check out allocation info for the Catalog File
1384 HFSPlusVolumeHeader
* volumeHeader
;
1386 volumeHeader
= (HFSPlusVolumeHeader
*) block
.buffer
;
1388 CopyMemory(volumeHeader
->catalogFile
.extents
, GPtr
->calculatedCatalogFCB
->fcbExtents32
, sizeof(HFSPlusExtentRecord
) );
1390 err
= CheckFileExtents( GPtr
, kHFSCatalogFileID
, kDataFork
, NULL
, (void *)GPtr
->calculatedCatalogFCB
->fcbExtents32
, &numABlks
);
1393 if ( volumeHeader
->catalogFile
.totalBlocks
!= numABlks
)
1395 RcdError( GPtr
, E_CatPEOF
);
1401 GPtr
->calculatedCatalogFCB
->fcbLogicalSize
= volumeHeader
->catalogFile
.logicalSize
;
1402 GPtr
->calculatedCatalogFCB
->fcbPhysicalSize
= (UInt64
)volumeHeader
->catalogFile
.totalBlocks
*
1403 (UInt64
)volumeHeader
->blockSize
;
1407 // Set up the minimal BTreeControlBlock structure
1410 // read the BTreeHeader from disk & also validate it's node size.
1411 err
= GetBTreeHeader(GPtr
, GPtr
->calculatedCatalogFCB
, &header
);
1414 btcb
->maxKeyLength
= kHFSPlusCatalogKeyMaximumLength
; // max key length
1417 * Figure out the type of key string compare
1418 * (case-insensitive or case-sensitive)
1420 * To do: should enforce an "HX" volume is require for kHFSBinaryCompare.
1422 if (header
.keyCompareType
== kHFSBinaryCompare
)
1424 btcb
->keyCompareProc
= (void *)CaseSensitiveCatalogKeyCompare
;
1425 fsckPrint(GPtr
->context
, hfsCaseSensitive
);
1429 btcb
->keyCompareProc
= (void *)CompareExtendedCatalogKeys
;
1431 btcb
->keyCompareType
= header
.keyCompareType
;
1432 btcb
->leafRecords
= header
.leafRecords
;
1433 btcb
->nodeSize
= header
.nodeSize
;
1434 btcb
->totalNodes
= (UInt32
)( GPtr
->calculatedCatalogFCB
->fcbPhysicalSize
/ btcb
->nodeSize
);
1435 btcb
->freeNodes
= btcb
->totalNodes
; // start with everything free
1436 btcb
->attributes
|=(kBTBigKeysMask
+ kBTVariableIndexKeysMask
); // HFS+ Catalog files have large, variable-sized keys
1438 btcb
->treeDepth
= header
.treeDepth
;
1439 btcb
->rootNode
= header
.rootNode
;
1440 btcb
->firstLeafNode
= header
.firstLeafNode
;
1441 btcb
->lastLeafNode
= header
.lastLeafNode
;
1444 // Make sure the header nodes size field is correct by looking at the 1st record offset
1445 err
= CheckNodesFirstOffset( GPtr
, btcb
);
1446 if ( (err
!= noErr
) && (btcb
->nodeSize
!= 4096) ) // default HFS+ Catalog node size is 4096
1448 btcb
->nodeSize
= 4096;
1449 btcb
->totalNodes
= (UInt32
)( GPtr
->calculatedCatalogFCB
->fcbPhysicalSize
/ btcb
->nodeSize
);
1450 btcb
->freeNodes
= btcb
->totalNodes
; // start with everything free
1452 err
= CheckNodesFirstOffset( GPtr
, btcb
);
1455 GPtr
->CBTStat
|= S_BTH
; // update the Btree header
1460 HFSMasterDirectoryBlock
*alternateMDB
;
1462 alternateMDB
= (HFSMasterDirectoryBlock
*) block
.buffer
;
1464 CopyMemory( alternateMDB
->drCTExtRec
, GPtr
->calculatedCatalogFCB
->fcbExtents16
, sizeof(HFSExtentRecord
) );
1465 // ExtDataRecToExtents(alternateMDB->drCTExtRec, GPtr->calculatedCatalogFCB->fcbExtents);
1467 err
= CheckFileExtents( GPtr
, kHFSCatalogFileID
, kDataFork
, NULL
, (void *)GPtr
->calculatedCatalogFCB
->fcbExtents16
, &numABlks
); /* check out extent info */
1470 if (alternateMDB
->drCTFlSize
!= ((UInt64
)numABlks
* (UInt64
)vcb
->vcbBlockSize
)) // check out the PEOF
1472 RcdError( GPtr
, E_CatPEOF
);
1478 GPtr
->calculatedCatalogFCB
->fcbPhysicalSize
= alternateMDB
->drCTFlSize
; // set up PEOF and EOF in FCB
1479 GPtr
->calculatedCatalogFCB
->fcbLogicalSize
= GPtr
->calculatedCatalogFCB
->fcbPhysicalSize
;
1483 // Set up the minimal BTreeControlBlock structure
1486 // read the BTreeHeader from disk & also validate it's node size.
1487 err
= GetBTreeHeader(GPtr
, GPtr
->calculatedCatalogFCB
, &header
);
1490 btcb
->maxKeyLength
= kHFSCatalogKeyMaximumLength
; // max key length
1491 btcb
->keyCompareProc
= (void *) CompareCatalogKeys
;
1492 btcb
->leafRecords
= header
.leafRecords
;
1493 btcb
->nodeSize
= header
.nodeSize
;
1494 btcb
->totalNodes
= (UInt32
)(GPtr
->calculatedCatalogFCB
->fcbPhysicalSize
/ btcb
->nodeSize
);
1495 btcb
->freeNodes
= btcb
->totalNodes
; // start with everything free
1497 btcb
->treeDepth
= header
.treeDepth
;
1498 btcb
->rootNode
= header
.rootNode
;
1499 btcb
->firstLeafNode
= header
.firstLeafNode
;
1500 btcb
->lastLeafNode
= header
.lastLeafNode
;
1502 // Make sure the header nodes size field is correct by looking at the 1st record offset
1503 err
= CheckNodesFirstOffset( GPtr
, btcb
);
1507 plog(" Catalog B-tree is %qd bytes\n", (UInt64
)btcb
->totalNodes
* (UInt64
) btcb
->nodeSize
);
1510 if ( header
.btreeType
!= kHFSBTreeType
)
1512 GPtr
->CBTStat
|= S_ReservedBTH
; // Repair reserved fields in Btree header
1516 // set up our DFA extended BTCB area. Will we have enough memory on all HFS+ volumes.
1519 btcb
->refCon
= AllocateClearMemory( sizeof(BTreeExtensionsRec
) ); // allocate space for our BTCB extensions
1520 if ( btcb
->refCon
== nil
) {
1524 size
= (btcb
->totalNodes
+ 7) / 8; // size of BTree bit map
1525 ((BTreeExtensionsRec
*)btcb
->refCon
)->BTCBMPtr
= AllocateClearMemory(size
); // get precleared bitmap
1526 if ( ((BTreeExtensionsRec
*)btcb
->refCon
)->BTCBMPtr
== nil
)
1532 ((BTreeExtensionsRec
*)btcb
->refCon
)->BTCBMSize
= size
; // remember how long it is
1533 ((BTreeExtensionsRec
*)btcb
->refCon
)->realFreeNodeCount
= header
.freeNodes
; // keep track of real free nodes for progress
1535 /* it should be OK at this point to get volume name and stuff it into our global */
1540 CatalogRecord record
;
1542 BuildCatalogKey( kHFSRootFolderID
, NULL
, isHFSPlus
, &key
);
1543 result
= SearchBTreeRecord( GPtr
->calculatedCatalogFCB
, &key
, kNoHint
, NULL
, &record
, &recSize
, NULL
);
1544 if ( result
== noErr
) {
1547 HFSPlusCatalogThread
* recPtr
= &record
.hfsPlusThread
;
1548 (void) utf_encodestr( recPtr
->nodeName
.unicode
,
1549 recPtr
->nodeName
.length
* 2,
1550 GPtr
->volumeName
, &len
, sizeof(GPtr
->volumeName
) );
1551 GPtr
->volumeName
[len
] = '\0';
1554 HFSCatalogThread
* recPtr
= &record
.hfsThread
;
1555 bcopy( &recPtr
->nodeName
[1], GPtr
->volumeName
, recPtr
->nodeName
[0] );
1556 GPtr
->volumeName
[ recPtr
->nodeName
[0] ] = '\0';
1558 fsckPrint(GPtr
->context
, fsckVolumeName
, GPtr
->volumeName
);
1564 if ( block
.buffer
!= NULL
)
1565 (void) ReleaseVolumeBlock(vcb
, &block
, kReleaseBlock
);
1571 /*------------------------------------------------------------------------------
1573 Function: CreateExtendedAllocationsFCB
1575 Function: Create the calculated ExtentsBTree Control Block for
1576 kHFSAllocationFileID and kHFSStartupFileID.
1578 Input: GPtr - pointer to scavenger global area
1580 Output: - 0 = no error
1582 ------------------------------------------------------------------------------*/
1583 OSErr
CreateExtendedAllocationsFCB( SGlobPtr GPtr
)
1589 BlockDescriptor block
;
1592 isHFSPlus
= VolumeObjectIsHFSPlus( );
1593 GPtr
->TarID
= kHFSAllocationFileID
;
1594 GetVolumeObjectBlockNum( &GPtr
->TarBlock
); // target block = VHB/MDB
1595 vcb
= GPtr
->calculatedVCB
;
1596 block
.buffer
= NULL
;
1599 // check out allocation info for the allocation File
1605 HFSPlusVolumeHeader
*volumeHeader
;
1607 err
= GetVolumeObjectVHB( &block
);
1610 volumeHeader
= (HFSPlusVolumeHeader
*) block
.buffer
;
1612 fcb
= GPtr
->calculatedAllocationsFCB
;
1613 CopyMemory( volumeHeader
->allocationFile
.extents
, fcb
->fcbExtents32
, sizeof(HFSPlusExtentRecord
) );
1615 err
= CheckFileExtents( GPtr
, kHFSAllocationFileID
, kDataFork
, NULL
, (void *)fcb
->fcbExtents32
, &numABlks
);
1619 // The allocation file will get processed in whole allocation blocks, or
1620 // maximal-sized cache blocks, whichever is smaller. This means the cache
1621 // doesn't need to cope with buffers that are larger than a cache block.
1622 if (vcb
->vcbBlockSize
< fscache
.BlockSize
)
1623 (void) SetFileBlockSize (fcb
, vcb
->vcbBlockSize
);
1625 (void) SetFileBlockSize (fcb
, fscache
.BlockSize
);
1627 if ( volumeHeader
->allocationFile
.totalBlocks
!= numABlks
)
1629 RcdError( GPtr
, E_CatPEOF
);
1635 fcb
->fcbLogicalSize
= volumeHeader
->allocationFile
.logicalSize
;
1636 fcb
->fcbPhysicalSize
= (UInt64
) volumeHeader
->allocationFile
.totalBlocks
*
1637 (UInt64
) volumeHeader
->blockSize
;
1640 /* while we're here, also get startup file extents... */
1641 fcb
= GPtr
->calculatedStartupFCB
;
1642 CopyMemory( volumeHeader
->startupFile
.extents
, fcb
->fcbExtents32
, sizeof(HFSPlusExtentRecord
) );
1644 err
= CheckFileExtents( GPtr
, kHFSStartupFileID
, kDataFork
, NULL
, (void *)fcb
->fcbExtents32
, &numABlks
);
1647 fcb
->fcbLogicalSize
= volumeHeader
->startupFile
.logicalSize
;
1648 fcb
->fcbPhysicalSize
= (UInt64
) volumeHeader
->startupFile
.totalBlocks
*
1649 (UInt64
) volumeHeader
->blockSize
;
1654 (void) ReleaseVolumeBlock(vcb
, &block
, kReleaseBlock
);
1661 /*------------------------------------------------------------------------------
1663 Function: CatHChk - (Catalog Hierarchy Check)
1665 Function: Verifies the catalog hierarchy.
1667 Input: GPtr - pointer to scavenger global area
1669 Output: CatHChk - function result:
1672 ------------------------------------------------------------------------------*/
1674 OSErr
CatHChk( SGlobPtr GPtr
)
1685 SVCB
*calculatedVCB
;
1688 CatalogKey foundKey
;
1689 Boolean validKeyFound
;
1691 CatalogRecord record
;
1692 CatalogRecord record2
;
1693 HFSPlusCatalogFolder
*largeCatalogFolderP
;
1694 HFSPlusCatalogFile
*largeCatalogFileP
;
1695 HFSCatalogFile
*smallCatalogFileP
;
1696 HFSCatalogFolder
*smallCatalogFolderP
;
1697 CatalogName catalogName
;
1699 CatalogRecord threadRecord
;
1700 HFSCatalogNodeID parID
;
1704 isHFSPlus
= VolumeObjectIsHFSPlus( );
1705 calculatedVCB
= GPtr
->calculatedVCB
;
1706 GPtr
->TarID
= kHFSCatalogFileID
; /* target = catalog file */
1707 GPtr
->TarBlock
= 0; /* no target block yet */
1710 // position to the beginning of catalog
1713 //¥¥ Can we ignore this part by just taking advantage of setting the selCode = 0x8001;
1715 BuildCatalogKey( 1, (const CatalogName
*)nil
, isHFSPlus
, &key
);
1716 result
= SearchBTreeRecord( GPtr
->calculatedCatalogFCB
, &key
, kNoHint
, &foundKey
, &threadRecord
, &recSize
, &hint
);
1718 GPtr
->TarBlock
= hint
; /* set target block */
1719 if ( result
!= btNotFound
)
1721 RcdError( GPtr
, E_CatRec
);
1727 dprP
= &(GPtr
->DirPTPtr
)[0];
1728 dprP
->directoryID
= 1;
1730 dirCnt
= filCnt
= rtdirCnt
= rtfilCnt
= 0;
1733 selCode
= 0x8001; /* start with root directory */
1736 // enumerate the entire catalog
1738 while ( (GPtr
->DirLevel
> 0) && (result
== noErr
) )
1740 dprP
= &(GPtr
->DirPTPtr
)[GPtr
->DirLevel
- 1];
1742 validKeyFound
= true;
1743 record
.recordType
= 0;
1745 // get the next record
1746 result
= GetBTreeRecord( GPtr
->calculatedCatalogFCB
, selCode
, &foundKey
, &record
, &recSize
, &hint
);
1748 GPtr
->TarBlock
= hint
; /* set target block */
1749 if ( result
!= noErr
)
1751 if ( result
== btNotFound
)
1754 validKeyFound
= false;
1758 result
= IntError( GPtr
, result
); /* error from BTGetRecord */
1762 selCode
= 1; /* get next rec from now on */
1764 GPtr
->itemsProcessed
++;
1767 // if same ParID ...
1769 parID
= isHFSPlus
== true ? foundKey
.hfsPlus
.parentID
: foundKey
.hfs
.parentID
;
1770 if ( (validKeyFound
== true) && (parID
== dprP
->directoryID
) )
1772 dprP
->offspringIndex
++; /* increment offspring index */
1774 // if new directory ...
1776 if ( record
.recordType
== kHFSPlusFolderRecord
)
1778 result
= CheckForStop( GPtr
); ReturnIfError( result
); // Permit the user to interrupt
1780 largeCatalogFolderP
= (HFSPlusCatalogFolder
*) &record
;
1781 GPtr
->TarID
= largeCatalogFolderP
->folderID
; // target ID = directory ID
1782 GPtr
->CNType
= record
.recordType
; // target CNode type = directory ID
1783 CopyCatalogName( (const CatalogName
*) &foundKey
.hfsPlus
.nodeName
, &GPtr
->CName
, isHFSPlus
);
1785 if ( dprP
->directoryID
> 1 )
1787 GPtr
->DirLevel
++; // we have a new directory level
1790 if ( dprP
->directoryID
== kHFSRootFolderID
) // bump root dir count
1793 if ( GPtr
->DirLevel
> GPtr
->dirPathCount
)
1797 ptr
= realloc(GPtr
->DirPTPtr
, (GPtr
->dirPathCount
+ CMMaxDepth
) * sizeof(SDPR
));
1800 fsckPrint(GPtr
->context
, E_CatDepth
, GPtr
->dirPathCount
);
1801 return noErr
; /* abort this check, but let other checks proceed */
1803 ClearMemory((char *)ptr
+ (GPtr
->dirPathCount
* sizeof(SDPR
)), (CMMaxDepth
* sizeof(SDPR
)));
1804 GPtr
->dirPathCount
+= CMMaxDepth
;
1805 GPtr
->DirPTPtr
= ptr
;
1808 dprP
= &(GPtr
->DirPTPtr
)[GPtr
->DirLevel
- 1];
1809 dprP
->directoryID
= largeCatalogFolderP
->folderID
;
1810 dprP
->offspringIndex
= 1;
1811 dprP
->directoryHint
= hint
;
1812 dprP
->parentDirID
= foundKey
.hfsPlus
.parentID
;
1813 CopyCatalogName( (const CatalogName
*) &foundKey
.hfsPlus
.nodeName
, &dprP
->directoryName
, isHFSPlus
);
1815 for ( i
= 1; i
< GPtr
->DirLevel
; i
++ )
1817 dprP1
= &(GPtr
->DirPTPtr
)[i
- 1];
1818 if (dprP
->directoryID
== dprP1
->directoryID
)
1820 RcdError( GPtr
,E_DirLoop
); // loop in directory hierarchy
1821 return( E_DirLoop
);
1826 * Find thread record
1828 BuildCatalogKey( dprP
->directoryID
, (const CatalogName
*) nil
, isHFSPlus
, &key
);
1829 result
= SearchBTreeRecord( GPtr
->calculatedCatalogFCB
, &key
, kNoHint
, &foundKey
, &threadRecord
, &recSize
, &hint
);
1830 if ( result
!= noErr
) {
1831 struct MissingThread
*mtp
;
1833 /* Report the error */
1834 fsckPrint(GPtr
->context
, E_NoThd
, dprP
->directoryID
);
1836 /* HFS will exit here */
1840 * A directory thread is missing. If we can find this
1841 * ID on the missing-thread list then we know where the
1842 * child entries reside and can resume our enumeration.
1844 for (mtp
= GPtr
->missingThreadList
; mtp
!= NULL
; mtp
= mtp
->link
) {
1845 if (mtp
->threadID
== dprP
->directoryID
) {
1846 mtp
->thread
.recordType
= kHFSPlusFolderThreadRecord
;
1847 mtp
->thread
.parentID
= dprP
->parentDirID
;
1848 CopyCatalogName(&dprP
->directoryName
, (CatalogName
*)&mtp
->thread
.nodeName
, isHFSPlus
);
1850 /* Reposition to the first child of target directory */
1851 result
= SearchBTreeRecord(GPtr
->calculatedCatalogFCB
, &mtp
->nextKey
,
1852 kNoHint
, &foundKey
, &threadRecord
, &recSize
, &hint
);
1856 selCode
= 0; /* use current record instead of next */
1862 * A directory thread is missing but we know this
1863 * directory has no children (since we didn't find
1864 * its ID on the missing-thread list above).
1866 * At this point we can resume the enumeration at
1867 * our previous position in our parent directory.
1869 goto resumeAtParent
;
1872 dprP
->threadHint
= hint
;
1873 GPtr
->TarBlock
= hint
;
1877 else if ( record
.recordType
== kHFSPlusFileRecord
)
1879 largeCatalogFileP
= (HFSPlusCatalogFile
*) &record
;
1880 GPtr
->TarID
= largeCatalogFileP
->fileID
; // target ID = file number
1881 GPtr
->CNType
= record
.recordType
; // target CNode type = thread
1882 CopyCatalogName( (const CatalogName
*) &foundKey
.hfsPlus
.nodeName
, &GPtr
->CName
, isHFSPlus
);
1884 if (dprP
->directoryID
== kHFSRootFolderID
)
1888 else if ( record
.recordType
== kHFSFolderRecord
)
1890 result
= CheckForStop( GPtr
); ReturnIfError( result
); // Permit the user to interrupt
1892 smallCatalogFolderP
= (HFSCatalogFolder
*) &record
;
1893 GPtr
->TarID
= smallCatalogFolderP
->folderID
; /* target ID = directory ID */
1894 GPtr
->CNType
= record
.recordType
; /* target CNode type = directory ID */
1895 CopyCatalogName( (const CatalogName
*) &key
.hfs
.nodeName
, &GPtr
->CName
, isHFSPlus
); /* target CName = directory name */
1897 if (dprP
->directoryID
> 1)
1899 GPtr
->DirLevel
++; /* we have a new directory level */
1902 if (dprP
->directoryID
== kHFSRootFolderID
) /* bump root dir count */
1905 if ( GPtr
->DirLevel
> GPtr
->dirPathCount
)
1909 ptr
= realloc(GPtr
->DirPTPtr
, (GPtr
->dirPathCount
+ CMMaxDepth
) * sizeof(SDPR
));
1912 fsckPrint(GPtr
->context
, E_CatDepth
, GPtr
->dirPathCount
);
1913 return noErr
; /* abort this check, but let other checks proceed */
1915 ClearMemory((char *)ptr
+ (GPtr
->dirPathCount
* sizeof(SDPR
)), (CMMaxDepth
* sizeof(SDPR
)));
1916 GPtr
->dirPathCount
+= CMMaxDepth
;
1917 GPtr
->DirPTPtr
= ptr
;
1920 dprP
= &(GPtr
->DirPTPtr
)[GPtr
->DirLevel
- 1];
1921 dprP
->directoryID
= smallCatalogFolderP
->folderID
;
1922 dprP
->offspringIndex
= 1;
1923 dprP
->directoryHint
= hint
;
1924 dprP
->parentDirID
= foundKey
.hfs
.parentID
;
1926 CopyCatalogName( (const CatalogName
*) &foundKey
.hfs
.nodeName
, &dprP
->directoryName
, isHFSPlus
);
1928 for (i
= 1; i
< GPtr
->DirLevel
; i
++)
1930 dprP1
= &(GPtr
->DirPTPtr
)[i
- 1];
1931 if (dprP
->directoryID
== dprP1
->directoryID
)
1933 RcdError( GPtr
,E_DirLoop
); /* loop in directory hierarchy */
1934 return( E_DirLoop
);
1938 BuildCatalogKey( dprP
->directoryID
, (const CatalogName
*)0, isHFSPlus
, &key
);
1939 result
= SearchBTreeRecord( GPtr
->calculatedCatalogFCB
, &key
, kNoHint
, &foundKey
, &threadRecord
, &recSize
, &hint
);
1940 if (result
!= noErr
)
1942 result
= IntError(GPtr
,result
); /* error from BTSearch */
1945 dprP
->threadHint
= hint
; /* save hint for thread */
1946 GPtr
->TarBlock
= hint
; /* set target block */
1949 // HFSCatalogFile...
1950 else if ( record
.recordType
== kHFSFileRecord
)
1952 smallCatalogFileP
= (HFSCatalogFile
*) &record
;
1953 GPtr
->TarID
= smallCatalogFileP
->fileID
; /* target ID = file number */
1954 GPtr
->CNType
= record
.recordType
; /* target CNode type = thread */
1955 CopyCatalogName( (const CatalogName
*) &foundKey
.hfs
.nodeName
, &GPtr
->CName
, isHFSPlus
); /* target CName = directory name */
1957 if (dprP
->directoryID
== kHFSRootFolderID
)
1961 // Unknown/Bad record type
1964 M_DebugStr("\p Unknown-Bad record type");
1970 // if not same ParID or no record
1972 else if ( (record
.recordType
== kHFSFileThreadRecord
) || (record
.recordType
== kHFSPlusFileThreadRecord
) ) /* it's a file thread, skip past it */
1974 GPtr
->TarID
= parID
; // target ID = file number
1975 GPtr
->CNType
= record
.recordType
; // target CNode type = thread
1976 GPtr
->CName
.ustr
.length
= 0; // no target CName
1982 GPtr
->TarID
= dprP
->directoryID
; /* target ID = current directory ID */
1983 GPtr
->CNType
= record
.recordType
; /* target CNode type = directory */
1984 CopyCatalogName( (const CatalogName
*) &dprP
->directoryName
, &GPtr
->CName
, isHFSPlus
); // copy the string name
1986 // re-locate current directory
1987 CopyCatalogName( (const CatalogName
*) &dprP
->directoryName
, &catalogName
, isHFSPlus
);
1988 BuildCatalogKey( dprP
->parentDirID
, (const CatalogName
*)&catalogName
, isHFSPlus
, &key
);
1989 result
= SearchBTreeRecord( GPtr
->calculatedCatalogFCB
, &key
, dprP
->directoryHint
, &foundKey
, &record2
, &recSize
, &hint
);
1991 if ( result
!= noErr
)
1993 result
= IntError(GPtr
,result
); /* error from BTSearch */
1996 GPtr
->TarBlock
= hint
; /* set target block */
1999 valence
= isHFSPlus
== true ? record2
.hfsPlusFolder
.valence
: (UInt32
)record2
.hfsFolder
.valence
;
2001 if ( valence
!= dprP
->offspringIndex
-1 ) /* check its valence */
2002 if ( ( result
= RcdValErr( GPtr
, E_DirVal
, dprP
->offspringIndex
-1, valence
, dprP
->parentDirID
) ) )
2005 GPtr
->DirLevel
--; /* move up a level */
2007 if(GPtr
->DirLevel
> 0)
2009 dprP
= &(GPtr
->DirPTPtr
)[GPtr
->DirLevel
- 1];
2010 GPtr
->TarID
= dprP
->directoryID
; /* target ID = current directory ID */
2011 GPtr
->CNType
= record
.recordType
; /* target CNode type = directory */
2012 CopyCatalogName( (const CatalogName
*) &dprP
->directoryName
, &GPtr
->CName
, isHFSPlus
);
2018 // verify directory and file counts (all nonfatal, repairable errors)
2020 if (!isHFSPlus
&& (rtdirCnt
!= calculatedVCB
->vcbNmRtDirs
)) /* check count of dirs in root */
2021 if ( ( result
= RcdValErr(GPtr
,E_RtDirCnt
,rtdirCnt
,calculatedVCB
->vcbNmRtDirs
,0) ) )
2024 if (!isHFSPlus
&& (rtfilCnt
!= calculatedVCB
->vcbNmFls
)) /* check count of files in root */
2025 if ( ( result
= RcdValErr(GPtr
,E_RtFilCnt
,rtfilCnt
,calculatedVCB
->vcbNmFls
,0) ) )
2028 if (dirCnt
!= calculatedVCB
->vcbFolderCount
) /* check count of dirs in volume */
2029 if ( ( result
= RcdValErr(GPtr
,E_DirCnt
,dirCnt
,calculatedVCB
->vcbFolderCount
,0) ) )
2032 if (filCnt
!= calculatedVCB
->vcbFileCount
) /* check count of files in volume */
2033 if ( ( result
= RcdValErr(GPtr
,E_FilCnt
,filCnt
,calculatedVCB
->vcbFileCount
,0) ) )
2038 } /* end of CatHChk */
2042 /*------------------------------------------------------------------------------
2044 Function: CreateAttributesBTreeControlBlock
2046 Function: Create the calculated AttributesBTree Control Block
2048 Input: GPtr - pointer to scavenger global area
2050 Output: - 0 = no error
2052 ------------------------------------------------------------------------------*/
2053 OSErr
CreateAttributesBTreeControlBlock( SGlobPtr GPtr
)
2058 BTreeControlBlock
* btcb
;
2062 BlockDescriptor block
;
2065 isHFSPlus
= VolumeObjectIsHFSPlus( );
2066 GPtr
->TarID
= kHFSAttributesFileID
;
2067 GPtr
->TarBlock
= kHeaderNodeNum
;
2068 block
.buffer
= NULL
;
2069 btcb
= GPtr
->calculatedAttributesBTCB
;
2070 vcb
= GPtr
->calculatedVCB
;
2073 // check out allocation info for the Attributes File
2078 HFSPlusVolumeHeader
*volumeHeader
;
2080 err
= GetVolumeObjectVHB( &block
);
2083 volumeHeader
= (HFSPlusVolumeHeader
*) block
.buffer
;
2085 CopyMemory( volumeHeader
->attributesFile
.extents
, GPtr
->calculatedAttributesFCB
->fcbExtents32
, sizeof(HFSPlusExtentRecord
) );
2087 err
= CheckFileExtents( GPtr
, kHFSAttributesFileID
, kDataFork
, NULL
, (void *)GPtr
->calculatedAttributesFCB
->fcbExtents32
, &numABlks
);
2090 if ( volumeHeader
->attributesFile
.totalBlocks
!= numABlks
) // check out the PEOF
2092 RcdError( GPtr
, E_CatPEOF
);
2098 GPtr
->calculatedAttributesFCB
->fcbLogicalSize
= (UInt64
) volumeHeader
->attributesFile
.logicalSize
; // Set Attributes tree's LEOF
2099 GPtr
->calculatedAttributesFCB
->fcbPhysicalSize
= (UInt64
) volumeHeader
->attributesFile
.totalBlocks
*
2100 (UInt64
) volumeHeader
->blockSize
; // Set Attributes tree's PEOF
2104 // See if we actually have an attributes BTree
2108 btcb
->maxKeyLength
= 0;
2109 btcb
->keyCompareProc
= 0;
2110 btcb
->leafRecords
= 0;
2112 btcb
->totalNodes
= 0;
2113 btcb
->freeNodes
= 0;
2114 btcb
->attributes
= 0;
2116 btcb
->treeDepth
= 0;
2118 btcb
->firstLeafNode
= 0;
2119 btcb
->lastLeafNode
= 0;
2121 // GPtr->calculatedVCB->attributesRefNum = 0;
2122 GPtr
->calculatedVCB
->vcbAttributesFile
= NULL
;
2126 // read the BTreeHeader from disk & also validate it's node size.
2127 err
= GetBTreeHeader(GPtr
, GPtr
->calculatedAttributesFCB
, &header
);
2130 btcb
->maxKeyLength
= kAttributeKeyMaximumLength
; // max key length
2131 btcb
->keyCompareProc
= (void *)CompareAttributeKeys
;
2132 btcb
->leafRecords
= header
.leafRecords
;
2133 btcb
->nodeSize
= header
.nodeSize
;
2134 btcb
->totalNodes
= (UInt32
)( GPtr
->calculatedAttributesFCB
->fcbPhysicalSize
/ btcb
->nodeSize
);
2135 btcb
->freeNodes
= btcb
->totalNodes
; // start with everything free
2136 btcb
->attributes
|=(kBTBigKeysMask
+ kBTVariableIndexKeysMask
); // HFS+ Attributes files have large, variable-sized keys
2138 btcb
->treeDepth
= header
.treeDepth
;
2139 btcb
->rootNode
= header
.rootNode
;
2140 btcb
->firstLeafNode
= header
.firstLeafNode
;
2141 btcb
->lastLeafNode
= header
.lastLeafNode
;
2144 // Make sure the header nodes size field is correct by looking at the 1st record offset
2146 err
= CheckNodesFirstOffset( GPtr
, btcb
);
2152 btcb
->maxKeyLength
= 0;
2153 btcb
->keyCompareProc
= 0;
2154 btcb
->leafRecords
= 0;
2156 btcb
->totalNodes
= 0;
2157 btcb
->freeNodes
= 0;
2158 btcb
->attributes
= 0;
2160 btcb
->treeDepth
= 0;
2162 btcb
->firstLeafNode
= 0;
2163 btcb
->lastLeafNode
= 0;
2165 GPtr
->calculatedVCB
->vcbAttributesFile
= NULL
;
2169 // set up our DFA extended BTCB area. Will we have enough memory on all HFS+ volumes.
2171 btcb
->refCon
= AllocateClearMemory( sizeof(BTreeExtensionsRec
) ); // allocate space for our BTCB extensions
2172 if ( btcb
->refCon
== nil
) {
2177 if (btcb
->totalNodes
== 0)
2179 ((BTreeExtensionsRec
*)btcb
->refCon
)->BTCBMPtr
= nil
;
2180 ((BTreeExtensionsRec
*)btcb
->refCon
)->BTCBMSize
= 0;
2181 ((BTreeExtensionsRec
*)btcb
->refCon
)->realFreeNodeCount
= 0;
2185 if ( btcb
->refCon
== nil
) {
2189 size
= (btcb
->totalNodes
+ 7) / 8; // size of BTree bit map
2190 ((BTreeExtensionsRec
*)btcb
->refCon
)->BTCBMPtr
= AllocateClearMemory(size
); // get precleared bitmap
2191 if ( ((BTreeExtensionsRec
*)btcb
->refCon
)->BTCBMPtr
== nil
)
2197 ((BTreeExtensionsRec
*)btcb
->refCon
)->BTCBMSize
= size
; // remember how long it is
2198 ((BTreeExtensionsRec
*)btcb
->refCon
)->realFreeNodeCount
= header
.freeNodes
; // keep track of real free nodes for progress
2203 (void) ReleaseVolumeBlock(vcb
, &block
, kReleaseBlock
);
2209 * Function: RecordLastAttrBits
2212 * Updates the Chinese Remainder Theorem buckets with extended attribute
2213 * information for the previous fileID stored in the global structure.
2216 * GPtr - pointer to scavenger global area
2217 * * GPtr->lastAttrInfo.fileID - fileID of last attribute seen
2221 static void RecordLastAttrBits(SGlobPtr GPtr
)
2223 /* lastAttrInfo structure is initialized to zero and hence ignore
2224 * recording information for fileID = 0. fileIDs < 16 (except for
2225 * fileID = 2) can have extended attributes but do not have
2226 * corresponding entry in catalog Btree. Ignore recording these
2227 * fileIDs for Chinese Remainder Theorem buckets. Currently we only
2228 * set extended attributes for fileID = 1 among these fileIDs
2229 * and this can change in future (see 3984119)
2231 if ((GPtr
->lastAttrInfo
.fileID
== 0) ||
2232 ((GPtr
->lastAttrInfo
.fileID
< kHFSFirstUserCatalogNodeID
) &&
2233 (GPtr
->lastAttrInfo
.fileID
!= kHFSRootFolderID
))) {
2237 if (GPtr
->lastAttrInfo
.hasSecurity
== true) {
2238 /* fileID has both extended attribute and ACL */
2239 RecordXAttrBits(GPtr
, kHFSHasAttributesMask
| kHFSHasSecurityMask
,
2240 GPtr
->lastAttrInfo
.fileID
, kCalculatedAttributesRefNum
);
2241 GPtr
->lastAttrInfo
.hasSecurity
= false;
2243 /* fileID only has extended attribute */
2244 RecordXAttrBits(GPtr
, kHFSHasAttributesMask
,
2245 GPtr
->lastAttrInfo
.fileID
, kCalculatedAttributesRefNum
);
2250 * Function: setLastAttrAllocInfo
2253 * Set the global structure of last extended attribute with
2254 * the allocation block information. Also set the isValid to true
2255 * to indicate that the data is valid and should be used to verify
2256 * allocation blocks.
2259 * GPtr - pointer to scavenger global area
2260 * totalBlocks - total blocks allocated by the attribute
2261 * logicalSize - logical size of the attribute
2262 * calculatedBlocks - blocks accounted by the attribute in current extent
2266 static void setLastAttrAllocInfo(SGlobPtr GPtr
, u_int32_t totalBlocks
,
2267 u_int64_t logicalSize
, u_int32_t calculatedTotalBlocks
)
2269 GPtr
->lastAttrInfo
.totalBlocks
= totalBlocks
;
2270 GPtr
->lastAttrInfo
.logicalSize
= logicalSize
;
2271 GPtr
->lastAttrInfo
.calculatedTotalBlocks
= calculatedTotalBlocks
;
2272 GPtr
->lastAttrInfo
.isValid
= true;
2276 * Function: CheckLastAttrAllocation
2279 * Checks the allocation block information stored for the last
2280 * extended attribute seen during extended attribute BTree traversal.
2281 * Always resets the information stored for last EA allocation.
2283 * Input: GPtr - pointer to scavenger global area
2285 * Output: int - function result:
2289 static int CheckLastAttrAllocation(SGlobPtr GPtr
)
2294 if (GPtr
->lastAttrInfo
.isValid
== true) {
2295 if (GPtr
->lastAttrInfo
.totalBlocks
!=
2296 GPtr
->lastAttrInfo
.calculatedTotalBlocks
) {
2297 result
= RecordBadAllocation(GPtr
->lastAttrInfo
.fileID
,
2298 GPtr
->lastAttrInfo
.attrname
, kEAData
,
2299 GPtr
->lastAttrInfo
.totalBlocks
,
2300 GPtr
->lastAttrInfo
.calculatedTotalBlocks
);
2302 bytes
= (u_int64_t
)GPtr
->lastAttrInfo
.calculatedTotalBlocks
*
2303 (u_int64_t
)GPtr
->calculatedVCB
->vcbBlockSize
;
2304 if (GPtr
->lastAttrInfo
.logicalSize
> bytes
) {
2305 result
= RecordTruncation(GPtr
->lastAttrInfo
.fileID
,
2306 GPtr
->lastAttrInfo
.attrname
, kEAData
,
2307 GPtr
->lastAttrInfo
.logicalSize
, bytes
);
2311 /* Invalidate information in the global structure */
2312 GPtr
->lastAttrInfo
.isValid
= false;
2318 /*------------------------------------------------------------------------------
2319 Function: CheckAttributeRecord
2322 This is call back function called for all leaf records in
2323 Attribute BTree during the verify and repair stage. The basic
2324 functionality of the function is same during verify and repair
2325 stages except that whenever it finds corruption, the verify
2326 stage prints message and the repair stage repairs it. In the verify
2327 stage, this function accounts for allocation blocks used
2328 by extent-based extended attributes and also updates the chinese
2329 remainder theorem buckets corresponding the extended attribute
2332 1. Only in the verify stage, if the fileID or attribute name of current
2333 extended attribute are not same as the previous attribute, check the
2334 allocation block counts for the previous attribute.
2336 2. Only in the verify stage, If the fileID of current attribute is not
2337 same as the previous attribute, record the previous fileID information
2338 for Chinese Remainder Theorem.
2340 3. For attribute type,
2341 kHFSPlusAttrForkData:
2342 ---------------------
2343 Do all of the following during verify stage and nothing in repair
2346 Check the start block for extended attribute from the key. If not
2349 Account for blocks occupied by this extent and store the allocation
2350 information for this extent to check in future. Also update the
2351 last attribute information in the global structure.
2353 kHFSPlusAttrExtents:
2354 --------------------
2355 If the current attribute's fileID is not same as previous fileID, or
2356 if the previous recordType is not a valid forkData or overflow extent
2357 record, report an error in verify stage or mark it for deletion in
2360 Do all of the following during verify stage and nothing in repair
2363 Check the start block for extended attribute from the key. If not
2364 equal to the total blocks seen uptil last attribtue, print error.
2366 Account for blocks occupied by this extent. Update previous
2367 attribute allocation information with blocks seen in current
2368 extent. Also update last attribute block information in the global
2371 kHFSPlusAttrInlineData:
2372 -----------------------
2373 Only in the verify stage, check if the start block in the key is
2374 equal to zero. If not, print error.
2378 In verify stage, report error. In repair stage, mark the record
2381 4. If a record is marked for deletion, delete the record.
2383 5. Before exiting from the function, always do the following -
2384 a. Indicate if the extended attribute was an ACL
2385 b. Update previous fileID and recordType with current information.
2386 c. Update previous attribute name with current attribute name.
2388 Input: GPtr - pointer to scavenger global area
2389 key - key for current attribute
2390 rec - attribute record
2391 reclen - length of the record
2393 Output: int - function result:
2396 ------------------------------------------------------------------------------*/
2398 CheckAttributeRecord(SGlobPtr GPtr
, const HFSPlusAttrKey
*key
, const HFSPlusAttrRecord
*rec
, UInt16 reclen
)
2401 unsigned char attrname
[XATTR_MAXNAMELEN
+1];
2405 struct attributeInfo
*prevAttr
;
2406 Boolean isSameAttr
= true;
2407 Boolean doDelete
= false;
2408 u_int16_t dfaStage
= GetDFAStage();
2410 /* Assert if volume is not HFS Plus */
2411 assert(VolumeObjectIsHFSPlus() == true);
2413 prevAttr
= &(GPtr
->lastAttrInfo
);
2414 fileID
= key
->fileID
;
2415 /* Convert unicode attribute name to UTF-8 string */
2416 (void) utf_encodestr(key
->attrName
, key
->attrNameLen
* 2, attrname
, &attrlen
, sizeof(attrname
));
2417 attrname
[attrlen
] = '\0';
2419 /* Compare the current attribute to last attribute seen */
2420 if ((fileID
!= prevAttr
->fileID
) ||
2421 (strcmp((char *)attrname
, (char *)prevAttr
->attrname
) != 0)) {
2425 /* We check allocation block information and record EA information for
2426 * CRT bucket in verify stage and hence no need to do it again in
2429 if (dfaStage
== kVerifyStage
) {
2430 /* Different attribute - check allocation block information */
2431 if (isSameAttr
== false) {
2432 result
= CheckLastAttrAllocation(GPtr
);
2438 /* Different fileID - record information in CRT bucket */
2439 if (fileID
!= prevAttr
->fileID
) {
2440 RecordLastAttrBits(GPtr
);
2444 switch (rec
->recordType
) {
2445 case kHFSPlusAttrForkData
: {
2446 /* Check start block only in verify stage to avoid printing message
2447 * in repair stage. Note that this corruption is not repairable
2448 * currently. Also check extents only in verify stage to avoid
2449 * false overlap extents error.
2452 if (dfaStage
== kVerifyStage
) {
2453 /* Start block in the key should be zero */
2454 if (key
->startBlock
!= 0) {
2455 RcdError(GPtr
, E_ABlkSt
);
2460 HFSPlusForkData forkData
;
2461 memcpy((void*)(&forkData
), (void*)(&rec
->forkData
.theFork
), sizeof(HFSPlusForkData
));
2462 /* Check the extent information and record overlapping extents, if any */
2463 result
= CheckFileExtents (GPtr
, fileID
, kEAData
, attrname
,
2464 &forkData
.extents
, &blocks
);
2469 /* Store allocation information to check in future */
2470 (void) setLastAttrAllocInfo(GPtr
, rec
->forkData
.theFork
.totalBlocks
,
2471 rec
->forkData
.theFork
.logicalSize
, blocks
);
2476 case kHFSPlusAttrExtents
: {
2477 /* Different attribute/fileID or incorrect previous record type */
2478 if ((isSameAttr
== false) ||
2479 ((prevAttr
->recordType
!= kHFSPlusAttrExtents
) &&
2480 (prevAttr
->recordType
!= kHFSPlusAttrForkData
))) {
2481 if (dfaStage
== kRepairStage
) {
2482 /* Delete record in repair stage */
2485 /* Report error in verify stage */
2486 RcdError(GPtr
, E_AttrRec
);
2487 GPtr
->ABTStat
|= S_AttrRec
;
2492 /* Check start block only in verify stage to avoid printing message
2493 * in repair stage. Note that this corruption is not repairable
2494 * currently. Also check extents only in verify stage to avoid
2495 * false overlap extents error.
2497 if (dfaStage
== kVerifyStage
) {
2498 /* startBlock in the key should be equal to total blocks
2499 * seen uptil last attribute.
2501 if (key
->startBlock
!= prevAttr
->calculatedTotalBlocks
) {
2502 RcdError(GPtr
, E_ABlkSt
);
2507 /* Check the extent information and record overlapping extents, if any */
2508 result
= CheckFileExtents (GPtr
, fileID
, kEAData
, attrname
,
2509 rec
->overflowExtents
.extents
, &blocks
);
2514 /* Increment the blocks seen uptil now for this attribute */
2515 prevAttr
->calculatedTotalBlocks
+= blocks
;
2520 case kHFSPlusAttrInlineData
: {
2521 /* Check start block only in verify stage to avoid printing message
2524 if (dfaStage
== kVerifyStage
) {
2525 /* Start block in the key should be zero */
2526 if (key
->startBlock
!= 0) {
2527 RcdError(GPtr
, E_ABlkSt
);
2536 /* Unknown attribute record */
2537 if (dfaStage
== kRepairStage
) {
2538 /* Delete record in repair stage */
2541 /* Report error in verify stage */
2542 RcdError(GPtr
, E_AttrRec
);
2543 GPtr
->ABTStat
|= S_AttrRec
;
2550 if (doDelete
== true) {
2551 result
= DeleteBTreeRecord(GPtr
->calculatedAttributesFCB
, key
);
2552 DPRINTF (d_info
|d_xattr
, "%s: Deleting attribute %s for fileID %d, type = %d\n", __FUNCTION__
, attrname
, key
->fileID
, rec
->recordType
);
2554 DPRINTF (d_error
|d_xattr
, "%s: Error in deleting record for %s for fileID %d, type = %d\n", __FUNCTION__
, attrname
, key
->fileID
, rec
->recordType
);
2557 /* Set flags to mark header and map dirty */
2558 GPtr
->ABTStat
|= S_BTH
+ S_BTM
;
2563 /* Note that an ACL exists for this fileID */
2564 if (strcmp((char *)attrname
, KAUTH_FILESEC_XATTR
) == 0) {
2565 prevAttr
->hasSecurity
= true;
2568 /* Always update the last recordType, fileID and attribute name before exiting */
2569 prevAttr
->recordType
= rec
->recordType
;
2570 prevAttr
->fileID
= fileID
;
2571 (void) strlcpy((char *)prevAttr
->attrname
, (char *)attrname
, sizeof(prevAttr
->attrname
));
2576 /* If the current record is invalid/bogus, decide whether to update
2577 * fileID stored in global structure for future comparison based on the
2579 * If the current bogus record's fileID is different from fileID of the
2580 * previous good record, we do not want to account for bogus fileID in
2581 * the Chinese Remainder Theorem when we see next good record.
2582 * Hence reset the fileID in global structure to dummy value. Example,
2583 * if the fileIDs are 10 15 20 and record with ID=15 is bogus, we do not
2584 * want to account for record with ID=15.
2585 * If the current bogus record's fileID is same as the fileID of the
2586 * previous good record, we want to account for this fileID in the
2587 * next good record we see after this bogus record. Hence do not
2588 * reset the fileID to dummy value. Example, if the records have fileID
2589 * 10 10 30 and the second record with ID=10 is bogus, we want to
2590 * account for ID=10 when we see record with ID=30.
2592 if (prevAttr
->fileID
!= fileID
) {
2593 prevAttr
->fileID
= 0;
2600 /* Function: RecordXAttrBits
2603 * This function increments the prime number buckets for the associated
2604 * prime bucket set based on the flags and btreetype to determine
2605 * the discrepancy between the attribute btree and catalog btree for
2606 * extended attribute data consistency. This function is based on
2607 * Chinese Remainder Theorem.
2610 * 1. If none of kHFSHasAttributesMask or kHFSHasSecurity mask is set,
2612 * 2. Based on btreetype and the flags, determine which prime number
2613 * bucket should be updated. Initialize pointers accordingly.
2614 * 3. Divide the fileID with pre-defined prime numbers. Store the
2616 * 4. Increment each prime number bucket at an offset of the
2617 * corresponding remainder with one.
2619 * Input: 1. GPtr - pointer to global scavenger area
2620 * 2. flags - can include kHFSHasAttributesMask and/or kHFSHasSecurityMask
2621 * 3. fileid - fileID for which particular extended attribute is seen
2622 * 4. btreetye - can be kHFSPlusCatalogRecord or kHFSPlusAttributeRecord
2623 * indicates which btree prime number bucket should be incremented
2627 void RecordXAttrBits(SGlobPtr GPtr
, UInt16 flags
, HFSCatalogNodeID fileid
, UInt16 btreetype
)
2629 PrimeBuckets
*cur_attr
= NULL
;
2630 PrimeBuckets
*cur_sec
= NULL
;
2632 if ( ((flags
& kHFSHasAttributesMask
) == 0) &&
2633 ((flags
& kHFSHasSecurityMask
) == 0) ) {
2634 /* No attributes exists for this fileID */
2638 /* Determine which bucket are we updating */
2639 if (btreetype
== kCalculatedCatalogRefNum
) {
2640 /* Catalog BTree buckets */
2641 if (flags
& kHFSHasAttributesMask
) {
2642 cur_attr
= &(GPtr
->CBTAttrBucket
);
2643 GPtr
->cat_ea_count
++;
2645 if (flags
& kHFSHasSecurityMask
) {
2646 cur_sec
= &(GPtr
->CBTSecurityBucket
);
2647 GPtr
->cat_acl_count
++;
2649 } else if (btreetype
== kCalculatedAttributesRefNum
) {
2650 /* Attribute BTree buckets */
2651 if (flags
& kHFSHasAttributesMask
) {
2652 cur_attr
= &(GPtr
->ABTAttrBucket
);
2653 GPtr
->attr_ea_count
++;
2655 if (flags
& kHFSHasSecurityMask
) {
2656 cur_sec
= &(GPtr
->ABTSecurityBucket
);
2657 GPtr
->attr_acl_count
++;
2660 /* Incorrect btreetype found */
2665 add_prime_bucket_uint32(cur_attr
, fileid
);
2669 add_prime_bucket_uint32(cur_sec
, fileid
);
2676 /* Function: CompareXattrPrimeBuckets
2679 * This function compares the prime number buckets for catalog btree
2680 * and attribute btree for the given attribute type (normal attribute
2681 * bit or security bit).
2683 * Input: 1. GPtr - pointer to global scavenger area
2684 * 2. BitMask - indicate which attribute type should be compared.
2685 * can include kHFSHasAttributesMask and/or kHFSHasSecurityMask
2686 * Output: zero - buckets were compared successfully
2687 * non-zero - buckets were not compared
2689 static int CompareXattrPrimeBuckets(SGlobPtr GPtr
, UInt16 BitMask
)
2692 PrimeBuckets
*cat
; /* Catalog BTree */
2693 PrimeBuckets
*attr
; /* Attribute BTree */
2695 /* Find the correct PrimeBuckets to compare */
2696 if (BitMask
& kHFSHasAttributesMask
) {
2697 /* Compare buckets for attribute bit */
2698 cat
= &(GPtr
->CBTAttrBucket
);
2699 attr
= &(GPtr
->ABTAttrBucket
);
2700 } else if (BitMask
& kHFSHasSecurityMask
) {
2701 /* Compare buckets for security bit */
2702 cat
= &(GPtr
->CBTSecurityBucket
);
2703 attr
= &(GPtr
->ABTSecurityBucket
);
2705 plog ("%s: Incorrect BitMask found.\n", __FUNCTION__
);
2709 result
= compare_prime_buckets(cat
, attr
);
2711 char catbtree
[32], attrbtree
[32];
2712 /* Unequal values found, set the error bit in ABTStat */
2713 if (BitMask
& kHFSHasAttributesMask
) {
2714 fsckPrint(GPtr
->context
, E_IncorrectAttrCount
);
2715 sprintf (catbtree
, "%u", GPtr
->cat_ea_count
);
2716 sprintf (attrbtree
, "%u", GPtr
->attr_ea_count
);
2717 fsckPrint(GPtr
->context
, E_BadValue
, attrbtree
, catbtree
);
2718 GPtr
->ABTStat
|= S_AttributeCount
;
2720 fsckPrint(GPtr
->context
, E_IncorrectSecurityCount
);
2721 sprintf (catbtree
, "%u", GPtr
->cat_acl_count
);
2722 sprintf (attrbtree
, "%u", GPtr
->attr_acl_count
);
2723 fsckPrint (GPtr
->context
, E_BadValue
, attrbtree
, catbtree
);
2724 GPtr
->ABTStat
|= S_SecurityCount
;
2734 /*------------------------------------------------------------------------------
2736 Function: AttrBTChk - (Attributes BTree Check)
2738 Function: Verifies the attributes BTree structure.
2740 Input: GPtr - pointer to scavenger global area
2742 Output: ExtBTChk - function result:
2745 ------------------------------------------------------------------------------*/
2747 OSErr
AttrBTChk( SGlobPtr GPtr
)
2752 // If this volume has no attributes BTree, then skip this check
2754 if (GPtr
->calculatedVCB
->vcbAttributesFile
== NULL
)
2757 // Write the status message here to avoid potential confusion to user.
2758 fsckPrint(GPtr
->context
, hfsExtAttrBTCheck
);
2761 GPtr
->TarID
= kHFSAttributesFileID
; // target = attributes file
2762 GetVolumeObjectBlockNum( &GPtr
->TarBlock
); // target block = VHB/MDB
2765 // check out the BTree structure
2768 err
= BTCheck( GPtr
, kCalculatedAttributesRefNum
, (CheckLeafRecordProcPtr
)CheckAttributeRecord
);
2769 ReturnIfError( err
); // invalid attributes file BTree
2771 // check the allocation block information about the last attribute
2772 err
= CheckLastAttrAllocation(GPtr
);
2775 // record the last fileID for Chinese Remainder Theorem comparison
2776 RecordLastAttrBits(GPtr
);
2778 // compare the attributes prime buckets calculated from catalog btree and attribute btree
2779 err
= CompareXattrPrimeBuckets(GPtr
, kHFSHasAttributesMask
);
2780 ReturnIfError( err
);
2782 // compare the security prime buckets calculated from catalog btree and attribute btree
2783 err
= CompareXattrPrimeBuckets(GPtr
, kHFSHasSecurityMask
);
2784 ReturnIfError( err
);
2787 // check out the allocation map structure
2790 err
= BTMapChk( GPtr
, kCalculatedAttributesRefNum
);
2791 ReturnIfError( err
); // Invalid attributes BTree map
2794 // Make sure unused nodes in the B-tree are zero filled.
2796 err
= BTCheckUnusedNodes(GPtr
, kCalculatedAttributesRefNum
, &GPtr
->ABTStat
);
2797 ReturnIfError( err
);
2800 // compare BTree header record on disk with scavenger's BTree header record
2803 err
= CmpBTH( GPtr
, kCalculatedAttributesRefNum
);
2804 ReturnIfError( err
);
2807 // compare BTree map on disk with scavenger's BTree map
2810 err
= CmpBTM( GPtr
, kCalculatedAttributesRefNum
);
2816 /*------------------------------------------------------------------------------
2818 Name: RcdValErr - (Record Valence Error)
2820 Function: Allocates a RepairOrder node and linkg it into the 'GPtr->RepairP'
2821 list, to describe an incorrect valence count for possible repair.
2823 Input: GPtr - ptr to scavenger global data
2824 type - error code (E_xxx), which should be >0
2825 correct - the correct valence, as computed here
2826 incorrect - the incorrect valence as found in volume
2827 parid - the parent id, if S_Valence error
2829 Output: 0 - no error
2830 R_NoMem - not enough mem to allocate record
2831 ------------------------------------------------------------------------------*/
2833 static int RcdValErr( SGlobPtr GPtr
, OSErr type
, UInt32 correct
, UInt32 incorrect
, HFSCatalogNodeID parid
) /* the ParID, if needed */
2835 RepairOrderPtr p
; /* the new node we compile */
2836 SInt16 n
; /* size of node we allocate */
2838 char goodStr
[32], badStr
[32];
2840 isHFSPlus
= VolumeObjectIsHFSPlus( );
2841 fsckPrint(GPtr
->context
, type
);
2842 sprintf(goodStr
, "%u", correct
);
2843 sprintf(badStr
, "%u", incorrect
);
2844 fsckPrint(GPtr
->context
, E_BadValue
, goodStr
, badStr
);
2846 if (type
== E_DirVal
) /* if normal directory valence error */
2847 n
= CatalogNameSize( &GPtr
->CName
, isHFSPlus
);
2849 n
= 0; /* other errors don't need the name */
2851 p
= AllocMinorRepairOrder( GPtr
,n
); /* get the node */
2852 if (p
==NULL
) /* quit if out of room */
2855 p
->type
= type
; /* save error info */
2856 p
->correct
= correct
;
2857 p
->incorrect
= incorrect
;
2860 if ( n
!= 0 ) /* if name needed */
2861 CopyCatalogName( (const CatalogName
*) &GPtr
->CName
, (CatalogName
*)&p
->name
, isHFSPlus
);
2863 GPtr
->CatStat
|= S_Valence
; /* set flag to trigger repair */
2865 return( noErr
); /* successful return */
2868 /*------------------------------------------------------------------------------
2870 Name: RcdHsFldCntErr - (Record HasFolderCount)
2872 Function: Allocates a RepairOrder node and linkg it into the 'GPtr->RepairP'
2873 list, to describe folder flag missing the HasFolderCount bit
2875 Input: GPtr - ptr to scavenger global data
2876 type - error code (E_xxx), which should be >0
2877 correct - the folder mask, as computed here
2878 incorrect - the folder mask, as found in volume
2881 Output: 0 - no error
2882 R_NoMem - not enough mem to allocate record
2883 ------------------------------------------------------------------------------*/
2885 int RcdHsFldCntErr( SGlobPtr GPtr
, OSErr type
, UInt32 correct
, UInt32 incorrect
, HFSCatalogNodeID fid
)
2887 RepairOrderPtr p
; /* the new node we compile */
2888 char goodStr
[32], badStr
[32];
2889 fsckPrint(GPtr
->context
, type
, fid
);
2890 sprintf(goodStr
, "%#x", correct
);
2891 sprintf(badStr
, "%#x", incorrect
);
2892 fsckPrint(GPtr
->context
, E_BadValue
, goodStr
, badStr
);
2894 p
= AllocMinorRepairOrder( GPtr
,0 ); /* get the node */
2895 if (p
==NULL
) /* quit if out of room */
2898 p
->type
= type
; /* save error info */
2899 p
->correct
= correct
;
2900 p
->incorrect
= incorrect
;
2903 return( noErr
); /* successful return */
2905 /*------------------------------------------------------------------------------
2907 Name: RcdFCntErr - (Record Folder Count)
2909 Function: Allocates a RepairOrder node and linkg it into the 'GPtr->RepairP'
2910 list, to describe an incorrect folder count for possible repair.
2912 Input: GPtr - ptr to scavenger global data
2913 type - error code (E_xxx), which should be >0
2914 correct - the correct folder count, as computed here
2915 incorrect - the incorrect folder count as found in volume
2918 Output: 0 - no error
2919 R_NoMem - not enough mem to allocate record
2920 ------------------------------------------------------------------------------*/
2922 int RcdFCntErr( SGlobPtr GPtr
, OSErr type
, UInt32 correct
, UInt32 incorrect
, HFSCatalogNodeID fid
)
2924 RepairOrderPtr p
; /* the new node we compile */
2925 char goodStr
[32], badStr
[32];
2927 fsckPrint(GPtr
->context
, type
, fid
);
2928 sprintf(goodStr
, "%u", correct
);
2929 sprintf(badStr
, "%u", incorrect
);
2930 fsckPrint(GPtr
->context
, E_BadValue
, goodStr
, badStr
);
2932 p
= AllocMinorRepairOrder( GPtr
,0 ); /* get the node */
2933 if (p
==NULL
) /* quit if out of room */
2936 p
->type
= type
; /* save error info */
2937 p
->correct
= correct
;
2938 p
->incorrect
= incorrect
;
2941 return( noErr
); /* successful return */
2944 /*------------------------------------------------------------------------------
2946 Name: RcdMDBAllocationBlockStartErr - (Record Allocation Block Start Error)
2948 Function: Allocates a RepairOrder node and linking it into the 'GPtr->RepairP'
2949 list, to describe the error for possible repair.
2951 Input: GPtr - ptr to scavenger global data
2952 type - error code (E_xxx), which should be >0
2953 correct - the correct valence, as computed here
2954 incorrect - the incorrect valence as found in volume
2956 Output: 0 - no error
2957 R_NoMem - not enough mem to allocate record
2958 ------------------------------------------------------------------------------*/
2960 static OSErr
RcdMDBEmbededVolDescriptionErr( SGlobPtr GPtr
, OSErr type
, HFSMasterDirectoryBlock
*mdb
)
2962 RepairOrderPtr p
; // the new node we compile
2963 EmbededVolDescription
*desc
;
2965 RcdError( GPtr
, type
); // first, record the error
2967 p
= AllocMinorRepairOrder( GPtr
, sizeof(EmbededVolDescription
) ); // get the node
2968 if ( p
== nil
) return( R_NoMem
);
2970 p
->type
= type
; // save error info
2971 desc
= (EmbededVolDescription
*) &(p
->name
);
2972 desc
->drAlBlSt
= mdb
->drAlBlSt
;
2973 desc
->drEmbedSigWord
= mdb
->drEmbedSigWord
;
2974 desc
->drEmbedExtent
.startBlock
= mdb
->drEmbedExtent
.startBlock
;
2975 desc
->drEmbedExtent
.blockCount
= mdb
->drEmbedExtent
.blockCount
;
2977 GPtr
->VIStat
|= S_InvalidWrapperExtents
; // set flag to trigger repair
2979 return( noErr
); // successful return
2983 #if 0 // not used at this time
2984 /*------------------------------------------------------------------------------
2986 Name: RcdInvalidWrapperExtents - (Record Invalid Wrapper Extents)
2988 Function: Allocates a RepairOrder node and linking it into the 'GPtr->RepairP'
2989 list, to describe the error for possible repair.
2991 Input: GPtr - ptr to scavenger global data
2992 type - error code (E_xxx), which should be >0
2993 correct - the correct valence, as computed here
2994 incorrect - the incorrect valence as found in volume
2996 Output: 0 - no error
2997 R_NoMem - not enough mem to allocate record
2998 ------------------------------------------------------------------------------*/
3000 static OSErr
RcdInvalidWrapperExtents( SGlobPtr GPtr
, OSErr type
)
3002 RepairOrderPtr p
; // the new node we compile
3004 RcdError( GPtr
, type
); // first, record the error
3006 p
= AllocMinorRepairOrder( GPtr
, 0 ); // get the node
3007 if ( p
== nil
) return( R_NoMem
);
3009 p
->type
= type
; // save error info
3011 GPtr
->VIStat
|= S_BadMDBdrAlBlSt
; // set flag to trigger repair
3013 return( noErr
); // successful return
3018 #if 0 // We just check and fix them in SRepair.c
3019 /*------------------------------------------------------------------------------
3021 Name: RcdOrphanedExtentErr
3023 Function: Allocates a RepairOrder node and linkg it into the 'GPtr->RepairP'
3024 list, to describe an locked volume name for possible repair.
3026 Input: GPtr - ptr to scavenger global data
3027 type - error code (E_xxx), which should be >0
3028 incorrect - the incorrect file flags as found in file record
3030 Output: 0 - no error
3031 R_NoMem - not enough mem to allocate record
3032 ------------------------------------------------------------------------------*/
3034 static OSErr
RcdOrphanedExtentErr ( SGlobPtr GPtr
, SInt16 type
, void *theKey
)
3036 RepairOrderPtr p
; /* the new node we compile */
3037 SInt16 n
; /* size of node we allocate */
3040 isHFSPlus
= VolumeObjectIsHFSPlus( );
3041 RcdError( GPtr
,type
); /* first, record the error */
3044 n
= sizeof( HFSPlusExtentKey
);
3046 n
= sizeof( HFSExtentKey
);
3048 p
= AllocMinorRepairOrder( GPtr
, n
); /* get the node */
3049 if ( p
== NULL
) /* quit if out of room */
3052 CopyMemory( theKey
, p
->name
, n
); /* copy in the key */
3054 p
->type
= type
; /* save error info */
3056 GPtr
->EBTStat
|= S_OrphanedExtent
; /* set flag to trigger repair */
3058 return( noErr
); /* successful return */
3063 /*------------------------------------------------------------------------------
3065 Function: VInfoChk - (Volume Info Check)
3067 Function: Verifies volume level information.
3069 Input: GPtr - pointer to scavenger global area
3071 Output: VInfoChk - function result:
3074 ------------------------------------------------------------------------------*/
3076 OSErr
VInfoChk( SGlobPtr GPtr
)
3084 VolumeObjectPtr myVOPtr
;
3085 CatalogRecord record
;
3086 CatalogKey foundKey
;
3087 BlockDescriptor altBlock
;
3088 BlockDescriptor priBlock
;
3090 vcb
= GPtr
->calculatedVCB
;
3091 altBlock
.buffer
= priBlock
.buffer
= NULL
;
3092 isHFSPlus
= VolumeObjectIsHFSPlus( );
3093 myVOPtr
= GetVolumeObjectPtr( );
3095 // locate the catalog record for the root directoryÉ
3096 result
= GetBTreeRecord( GPtr
->calculatedCatalogFCB
, 0x8001, &foundKey
, &record
, &recSize
, &hint
);
3097 GPtr
->TarID
= kHFSCatalogFileID
; /* target = catalog */
3098 GPtr
->TarBlock
= hint
; /* target block = returned hint */
3099 if ( result
!= noErr
)
3101 result
= IntError( GPtr
, result
);
3105 GPtr
->TarID
= AMDB_FNum
; // target = alternate MDB or VHB
3106 GetVolumeObjectAlternateBlockNum( &GPtr
->TarBlock
);
3107 result
= GetVolumeObjectAlternateBlock( &altBlock
);
3109 // invalidate if we have not marked the alternate as OK
3111 if ( (myVOPtr
->flags
& kVO_AltVHBOK
) == 0 )
3114 else if ( (myVOPtr
->flags
& kVO_AltMDBOK
) == 0 ) {
3117 if ( result
!= noErr
) {
3118 GPtr
->VIStat
= GPtr
->VIStat
| S_MDB
;
3119 if ( VolumeObjectIsHFS( ) ) {
3120 WriteError( GPtr
, E_MDBDamaged
, 0, 0 );
3121 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
3122 plog("\tinvalid alternate MDB at %qd result %d \n", GPtr
->TarBlock
, result
);
3125 WriteError( GPtr
, E_VolumeHeaderDamaged
, 0, 0 );
3126 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
3127 plog("\tinvalid alternate VHB at %qd result %d \n", GPtr
->TarBlock
, result
);
3133 GPtr
->TarID
= MDB_FNum
; // target = primary MDB or VHB
3134 GetVolumeObjectPrimaryBlockNum( &GPtr
->TarBlock
);
3135 result
= GetVolumeObjectPrimaryBlock( &priBlock
);
3137 // invalidate if we have not marked the primary as OK
3139 if ( (myVOPtr
->flags
& kVO_PriVHBOK
) == 0 )
3142 else if ( (myVOPtr
->flags
& kVO_PriMDBOK
) == 0 ) {
3145 if ( result
!= noErr
) {
3146 GPtr
->VIStat
= GPtr
->VIStat
| S_MDB
;
3147 if ( VolumeObjectIsHFS( ) ) {
3148 WriteError( GPtr
, E_MDBDamaged
, 1, 0 );
3149 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
3150 plog("\tinvalid primary MDB at %qd result %d \n", GPtr
->TarBlock
, result
);
3153 WriteError( GPtr
, E_VolumeHeaderDamaged
, 1, 0 );
3154 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
3155 plog("\tinvalid primary VHB at %qd result %d \n", GPtr
->TarBlock
, result
);
3161 // check to see that embedded HFS plus volumes still have both (alternate and primary) MDBs
3162 if ( VolumeObjectIsEmbeddedHFSPlus( ) &&
3163 ( (myVOPtr
->flags
& kVO_PriMDBOK
) == 0 || (myVOPtr
->flags
& kVO_AltMDBOK
) == 0 ) )
3165 GPtr
->VIStat
|= S_WMDB
;
3166 WriteError( GPtr
, E_MDBDamaged
, 0, 0 );
3167 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
)
3168 plog("\tinvalid wrapper MDB \n");
3173 HFSPlusVolumeHeader
* volumeHeader
;
3174 HFSPlusVolumeHeader
* alternateVolumeHeader
;
3176 alternateVolumeHeader
= (HFSPlusVolumeHeader
*) altBlock
.buffer
;
3177 volumeHeader
= (HFSPlusVolumeHeader
*) priBlock
.buffer
;
3179 maxClump
= (UInt64
) (vcb
->vcbTotalBlocks
/ 4) * vcb
->vcbBlockSize
; /* max clump = 1/4 volume size */
3181 // check out creation and last mod dates
3182 vcb
->vcbCreateDate
= alternateVolumeHeader
->createDate
; // use creation date in alt MDB
3183 vcb
->vcbModifyDate
= volumeHeader
->modifyDate
; // don't change last mod date
3184 vcb
->vcbCheckedDate
= volumeHeader
->checkedDate
; // don't change checked date
3186 // 3882639: Removed check for volume attributes in HFS Plus
3187 vcb
->vcbAttributes
= volumeHeader
->attributes
;
3189 // verify allocation map ptr
3190 if ( volumeHeader
->nextAllocation
< vcb
->vcbTotalBlocks
)
3191 vcb
->vcbNextAllocation
= volumeHeader
->nextAllocation
;
3193 vcb
->vcbNextAllocation
= 0;
3195 // verify default clump sizes
3196 if ( (volumeHeader
->rsrcClumpSize
> 0) &&
3197 (volumeHeader
->rsrcClumpSize
<= kMaxClumpSize
) &&
3198 ((volumeHeader
->rsrcClumpSize
% vcb
->vcbBlockSize
) == 0) )
3199 vcb
->vcbRsrcClumpSize
= volumeHeader
->rsrcClumpSize
;
3200 else if ( (alternateVolumeHeader
->rsrcClumpSize
> 0) &&
3201 (alternateVolumeHeader
->rsrcClumpSize
<= kMaxClumpSize
) &&
3202 ((alternateVolumeHeader
->rsrcClumpSize
% vcb
->vcbBlockSize
) == 0) )
3203 vcb
->vcbRsrcClumpSize
= alternateVolumeHeader
->rsrcClumpSize
;
3204 else if (4ULL * vcb
->vcbBlockSize
<= kMaxClumpSize
)
3205 vcb
->vcbRsrcClumpSize
= 4 * vcb
->vcbBlockSize
;
3207 vcb
->vcbRsrcClumpSize
= vcb
->vcbBlockSize
; /* for very large volumes, just use 1 allocation block */
3209 if ( vcb
->vcbRsrcClumpSize
> kMaxClumpSize
)
3210 vcb
->vcbRsrcClumpSize
= vcb
->vcbBlockSize
; /* for very large volumes, just use 1 allocation block */
3212 if ( (volumeHeader
->dataClumpSize
> 0) && (volumeHeader
->dataClumpSize
<= kMaxClumpSize
) &&
3213 ((volumeHeader
->dataClumpSize
% vcb
->vcbBlockSize
) == 0) )
3214 vcb
->vcbDataClumpSize
= volumeHeader
->dataClumpSize
;
3215 else if ( (alternateVolumeHeader
->dataClumpSize
> 0) &&
3216 (alternateVolumeHeader
->dataClumpSize
<= kMaxClumpSize
) &&
3217 ((alternateVolumeHeader
->dataClumpSize
% vcb
->vcbBlockSize
) == 0) )
3218 vcb
->vcbDataClumpSize
= alternateVolumeHeader
->dataClumpSize
;
3219 else if (4ULL * vcb
->vcbBlockSize
<= kMaxClumpSize
)
3220 vcb
->vcbDataClumpSize
= 4 * vcb
->vcbBlockSize
;
3222 vcb
->vcbDataClumpSize
= vcb
->vcbBlockSize
; /* for very large volumes, just use 1 allocation block */
3224 if ( vcb
->vcbDataClumpSize
> kMaxClumpSize
)
3225 vcb
->vcbDataClumpSize
= vcb
->vcbBlockSize
; /* for very large volumes, just use 1 allocation block */
3227 /* Verify next CNode ID.
3228 * If volumeHeader->nextCatalogID < vcb->vcbNextCatalogID, probably
3229 * nextCatalogID has wrapped around.
3230 * If volumeHeader->nextCatalogID > vcb->vcbNextCatalogID, probably
3231 * many files were created and deleted, followed by no new file
3234 if ( (volumeHeader
->nextCatalogID
> vcb
->vcbNextCatalogID
) )
3235 vcb
->vcbNextCatalogID
= volumeHeader
->nextCatalogID
;
3237 //¥¥TBD location and unicode? volumename
3238 // verify the volume name
3239 result
= ChkCName( GPtr
, (const CatalogName
*) &foundKey
.hfsPlus
.nodeName
, isHFSPlus
);
3241 // verify last backup date and backup seqence number
3242 vcb
->vcbBackupDate
= volumeHeader
->backupDate
; /* don't change last backup date */
3244 // verify write count
3245 vcb
->vcbWriteCount
= volumeHeader
->writeCount
; /* don't change write count */
3247 // check out extent file clump size
3248 if ( ((volumeHeader
->extentsFile
.clumpSize
% vcb
->vcbBlockSize
) == 0) &&
3249 (volumeHeader
->extentsFile
.clumpSize
<= maxClump
) )
3250 vcb
->vcbExtentsFile
->fcbClumpSize
= volumeHeader
->extentsFile
.clumpSize
;
3251 else if ( ((alternateVolumeHeader
->extentsFile
.clumpSize
% vcb
->vcbBlockSize
) == 0) &&
3252 (alternateVolumeHeader
->extentsFile
.clumpSize
<= maxClump
) )
3253 vcb
->vcbExtentsFile
->fcbClumpSize
= alternateVolumeHeader
->extentsFile
.clumpSize
;
3255 vcb
->vcbExtentsFile
->fcbClumpSize
=
3256 (alternateVolumeHeader
->extentsFile
.extents
[0].blockCount
* vcb
->vcbBlockSize
);
3258 // check out catalog file clump size
3259 if ( ((volumeHeader
->catalogFile
.clumpSize
% vcb
->vcbBlockSize
) == 0) &&
3260 (volumeHeader
->catalogFile
.clumpSize
<= maxClump
) )
3261 vcb
->vcbCatalogFile
->fcbClumpSize
= volumeHeader
->catalogFile
.clumpSize
;
3262 else if ( ((alternateVolumeHeader
->catalogFile
.clumpSize
% vcb
->vcbBlockSize
) == 0) &&
3263 (alternateVolumeHeader
->catalogFile
.clumpSize
<= maxClump
) )
3264 vcb
->vcbCatalogFile
->fcbClumpSize
= alternateVolumeHeader
->catalogFile
.clumpSize
;
3266 vcb
->vcbCatalogFile
->fcbClumpSize
=
3267 (alternateVolumeHeader
->catalogFile
.extents
[0].blockCount
* vcb
->vcbBlockSize
);
3269 // check out allocations file clump size
3270 if ( ((volumeHeader
->allocationFile
.clumpSize
% vcb
->vcbBlockSize
) == 0) &&
3271 (volumeHeader
->allocationFile
.clumpSize
<= maxClump
) )
3272 vcb
->vcbAllocationFile
->fcbClumpSize
= volumeHeader
->allocationFile
.clumpSize
;
3273 else if ( ((alternateVolumeHeader
->allocationFile
.clumpSize
% vcb
->vcbBlockSize
) == 0) &&
3274 (alternateVolumeHeader
->allocationFile
.clumpSize
<= maxClump
) )
3275 vcb
->vcbAllocationFile
->fcbClumpSize
= alternateVolumeHeader
->allocationFile
.clumpSize
;
3277 vcb
->vcbAllocationFile
->fcbClumpSize
=
3278 (alternateVolumeHeader
->allocationFile
.extents
[0].blockCount
* vcb
->vcbBlockSize
);
3280 // check out attribute file clump size
3281 if (vcb
->vcbAttributesFile
) {
3282 if ( ((volumeHeader
->attributesFile
.clumpSize
% vcb
->vcbBlockSize
) == 0) &&
3283 (volumeHeader
->attributesFile
.clumpSize
<= maxClump
) &&
3284 (volumeHeader
->attributesFile
.clumpSize
!= 0))
3285 vcb
->vcbAttributesFile
->fcbClumpSize
= volumeHeader
->attributesFile
.clumpSize
;
3286 else if ( ((alternateVolumeHeader
->attributesFile
.clumpSize
% vcb
->vcbBlockSize
) == 0) &&
3287 (alternateVolumeHeader
->attributesFile
.clumpSize
<= maxClump
) &&
3288 (alternateVolumeHeader
->attributesFile
.clumpSize
!= 0))
3289 vcb
->vcbAttributesFile
->fcbClumpSize
= alternateVolumeHeader
->attributesFile
.clumpSize
;
3290 else if (vcb
->vcbCatalogFile
->fcbClumpSize
!= 0)
3291 // The original attribute clump may be too small, use catalog's
3292 vcb
->vcbAttributesFile
->fcbClumpSize
= vcb
->vcbCatalogFile
->fcbClumpSize
;
3294 vcb
->vcbAttributesFile
->fcbClumpSize
=
3295 alternateVolumeHeader
->attributesFile
.extents
[0].blockCount
* vcb
->vcbBlockSize
;
3298 CopyMemory( volumeHeader
->finderInfo
, vcb
->vcbFinderInfo
, sizeof(vcb
->vcbFinderInfo
) );
3300 // Now compare verified Volume Header info (in the form of a vcb) with Volume Header info on disk
3301 result
= CompareVolumeHeader( GPtr
, volumeHeader
);
3303 // check to see that embedded volume info is correct in both wrapper MDBs
3304 CheckEmbeddedVolInfoInMDBs( GPtr
);
3309 HFSMasterDirectoryBlock
*mdbP
;
3310 HFSMasterDirectoryBlock
*alternateMDB
;
3313 // get volume name from BTree Key
3316 alternateMDB
= (HFSMasterDirectoryBlock
*) altBlock
.buffer
;
3317 mdbP
= (HFSMasterDirectoryBlock
*) priBlock
.buffer
;
3319 maxClump
= (UInt64
) (vcb
->vcbTotalBlocks
/ 4) * vcb
->vcbBlockSize
; /* max clump = 1/4 volume size */
3321 // check out creation and last mod dates
3322 vcb
->vcbCreateDate
= alternateMDB
->drCrDate
; /* use creation date in alt MDB */
3323 vcb
->vcbModifyDate
= mdbP
->drLsMod
; /* don't change last mod date */
3325 // verify volume attribute flags
3326 if ( (mdbP
->drAtrb
& VAtrb_Msk
) == 0 )
3327 vcb
->vcbAttributes
= mdbP
->drAtrb
;
3329 vcb
->vcbAttributes
= VAtrb_DFlt
;
3331 // verify allocation map ptr
3332 if ( mdbP
->drAllocPtr
< vcb
->vcbTotalBlocks
)
3333 vcb
->vcbNextAllocation
= mdbP
->drAllocPtr
;
3335 vcb
->vcbNextAllocation
= 0;
3337 // verify default clump size
3338 if ( (mdbP
->drClpSiz
> 0) &&
3339 (mdbP
->drClpSiz
<= maxClump
) &&
3340 ((mdbP
->drClpSiz
% vcb
->vcbBlockSize
) == 0) )
3341 vcb
->vcbDataClumpSize
= mdbP
->drClpSiz
;
3342 else if ( (alternateMDB
->drClpSiz
> 0) &&
3343 (alternateMDB
->drClpSiz
<= maxClump
) &&
3344 ((alternateMDB
->drClpSiz
% vcb
->vcbBlockSize
) == 0) )
3345 vcb
->vcbDataClumpSize
= alternateMDB
->drClpSiz
;
3347 vcb
->vcbDataClumpSize
= 4 * vcb
->vcbBlockSize
;
3349 if ( vcb
->vcbDataClumpSize
> kMaxClumpSize
)
3350 vcb
->vcbDataClumpSize
= vcb
->vcbBlockSize
; /* for very large volumes, just use 1 allocation block */
3352 // verify next CNode ID
3353 if ( (mdbP
->drNxtCNID
> vcb
->vcbNextCatalogID
) && (mdbP
->drNxtCNID
<= (vcb
->vcbNextCatalogID
+ 4096)) )
3354 vcb
->vcbNextCatalogID
= mdbP
->drNxtCNID
;
3356 // verify the volume name
3357 result
= ChkCName( GPtr
, (const CatalogName
*) &vcb
->vcbVN
, isHFSPlus
);
3358 if ( result
== noErr
)
3359 if ( CmpBlock( mdbP
->drVN
, vcb
->vcbVN
, vcb
->vcbVN
[0] + 1 ) == 0 )
3360 CopyMemory( mdbP
->drVN
, vcb
->vcbVN
, kHFSMaxVolumeNameChars
+ 1 ); /* ...we have a good one */
3362 // verify last backup date and backup seqence number
3363 vcb
->vcbBackupDate
= mdbP
->drVolBkUp
; /* don't change last backup date */
3364 vcb
->vcbVSeqNum
= mdbP
->drVSeqNum
; /* don't change last backup sequence # */
3366 // verify write count
3367 vcb
->vcbWriteCount
= mdbP
->drWrCnt
; /* don't change write count */
3369 // check out extent file and catalog clump sizes
3370 if ( ((mdbP
->drXTClpSiz
% vcb
->vcbBlockSize
) == 0) && (mdbP
->drXTClpSiz
<= maxClump
) )
3371 vcb
->vcbExtentsFile
->fcbClumpSize
= mdbP
->drXTClpSiz
;
3372 else if ( ((alternateMDB
->drXTClpSiz
% vcb
->vcbBlockSize
) == 0) && (alternateMDB
->drXTClpSiz
<= maxClump
) )
3373 vcb
->vcbExtentsFile
->fcbClumpSize
= alternateMDB
->drXTClpSiz
;
3375 vcb
->vcbExtentsFile
->fcbClumpSize
= (alternateMDB
->drXTExtRec
[0].blockCount
* vcb
->vcbBlockSize
);
3377 if ( ((mdbP
->drCTClpSiz
% vcb
->vcbBlockSize
) == 0) && (mdbP
->drCTClpSiz
<= maxClump
) )
3378 vcb
->vcbCatalogFile
->fcbClumpSize
= mdbP
->drCTClpSiz
;
3379 else if ( ((alternateMDB
->drCTClpSiz
% vcb
->vcbBlockSize
) == 0) && (alternateMDB
->drCTClpSiz
<= maxClump
) )
3380 vcb
->vcbCatalogFile
->fcbClumpSize
= alternateMDB
->drCTClpSiz
;
3382 vcb
->vcbCatalogFile
->fcbClumpSize
= (alternateMDB
->drCTExtRec
[0].blockCount
* vcb
->vcbBlockSize
);
3384 // just copy Finder info for now
3385 CopyMemory(mdbP
->drFndrInfo
, vcb
->vcbFinderInfo
, sizeof(mdbP
->drFndrInfo
));
3387 // now compare verified MDB info with MDB info on disk
3388 result
= CmpMDB( GPtr
, mdbP
);
3392 if (priBlock
.buffer
)
3393 (void) ReleaseVolumeBlock(vcb
, &priBlock
, kReleaseBlock
);
3394 if (altBlock
.buffer
)
3395 (void) ReleaseVolumeBlock(vcb
, &altBlock
, kReleaseBlock
);
3399 } /* end of VInfoChk */
3402 /*------------------------------------------------------------------------------
3404 Function: VLockedChk - (Volume Name Locked Check)
3406 Function: Makes sure the volume name isn't locked. If it is locked, generate a repair order.
3408 This function is not called if file sharing is operating.
3410 Input: GPtr - pointer to scavenger global area
3412 Output: VInfoChk - function result:
3415 ------------------------------------------------------------------------------*/
3417 OSErr
VLockedChk( SGlobPtr GPtr
)
3420 CatalogKey foundKey
;
3421 CatalogRecord record
;
3426 SVCB
*calculatedVCB
= GPtr
->calculatedVCB
;
3427 VolumeObjectPtr myVOPtr
;
3429 myVOPtr
= GetVolumeObjectPtr( );
3430 isHFSPlus
= VolumeObjectIsHFSPlus( );
3431 GPtr
->TarID
= kHFSCatalogFileID
; /* target = catalog file */
3432 GPtr
->TarBlock
= 0; /* no target block yet */
3435 // locate the catalog record for the root directory
3437 result
= GetBTreeRecord( GPtr
->calculatedCatalogFCB
, 0x8001, &foundKey
, &record
, &recSize
, &hint
);
3441 RcdError( GPtr
, E_EntryNotFound
);
3442 return( E_EntryNotFound
);
3445 // put the volume name in the VCB
3446 if ( isHFSPlus
== false )
3448 CopyMemory( foundKey
.hfs
.nodeName
, calculatedVCB
->vcbVN
, sizeof(calculatedVCB
->vcbVN
) );
3450 else if ( myVOPtr
->volumeType
!= kPureHFSPlusVolumeType
)
3452 HFSMasterDirectoryBlock
*mdbP
;
3453 BlockDescriptor block
;
3455 block
.buffer
= NULL
;
3456 if ( (myVOPtr
->flags
& kVO_PriMDBOK
) != 0 )
3457 result
= GetVolumeObjectPrimaryMDB( &block
);
3459 result
= GetVolumeObjectAlternateMDB( &block
);
3460 if ( result
== noErr
) {
3461 mdbP
= (HFSMasterDirectoryBlock
*) block
.buffer
;
3462 CopyMemory( mdbP
->drVN
, calculatedVCB
->vcbVN
, sizeof(mdbP
->drVN
) );
3464 if ( block
.buffer
!= NULL
)
3465 (void) ReleaseVolumeBlock(calculatedVCB
, &block
, kReleaseBlock
);
3466 ReturnIfError(result
);
3468 else // Because we don't have the unicode converters, just fill it with a dummy name.
3470 CopyMemory( "\x0dPure HFS Plus", calculatedVCB
->vcbVN
, sizeof(Str27
) );
3473 GPtr
->TarBlock
= hint
;
3475 CopyCatalogName( (const CatalogName
*)&foundKey
.hfsPlus
.nodeName
, &GPtr
->CName
, isHFSPlus
);
3477 CopyCatalogName( (const CatalogName
*)&foundKey
.hfs
.nodeName
, &GPtr
->CName
, isHFSPlus
);
3479 if ( (record
.recordType
== kHFSPlusFolderRecord
) || (record
.recordType
== kHFSFolderRecord
) )
3481 frFlags
= record
.recordType
== kHFSPlusFolderRecord
?
3482 record
.hfsPlusFolder
.userInfo
.frFlags
:
3483 record
.hfsFolder
.userInfo
.frFlags
;
3485 if ( frFlags
& fNameLocked
) // name locked bit set?
3486 RcdNameLockedErr( GPtr
, E_LockedDirName
, frFlags
);
3493 /*------------------------------------------------------------------------------
3495 Name: RcdNameLockedErr
3497 Function: Allocates a RepairOrder node and linkg it into the 'GPtr->RepairP'
3498 list, to describe an locked volume name for possible repair.
3500 Input: GPtr - ptr to scavenger global data
3501 type - error code (E_xxx), which should be >0
3502 incorrect - the incorrect file flags as found in file record
3504 Output: 0 - no error
3505 R_NoMem - not enough mem to allocate record
3506 ------------------------------------------------------------------------------*/
3508 static int RcdNameLockedErr( SGlobPtr GPtr
, SInt16 type
, UInt32 incorrect
) /* for a consistency check */
3510 RepairOrderPtr p
; /* the new node we compile */
3511 int n
; /* size of node we allocate */
3514 isHFSPlus
= VolumeObjectIsHFSPlus( );
3515 RcdError( GPtr
, type
); /* first, record the error */
3517 n
= CatalogNameSize( &GPtr
->CName
, isHFSPlus
);
3519 p
= AllocMinorRepairOrder( GPtr
, n
); /* get the node */
3520 if ( p
==NULL
) /* quit if out of room */
3523 CopyCatalogName( (const CatalogName
*) &GPtr
->CName
, (CatalogName
*)&p
->name
, isHFSPlus
);
3525 p
->type
= type
; /* save error info */
3526 p
->correct
= incorrect
& ~fNameLocked
; /* mask off the name locked bit */
3527 p
->incorrect
= incorrect
;
3528 p
->maskBit
= (UInt16
)fNameLocked
;
3531 GPtr
->CatStat
|= S_LockedDirName
; /* set flag to trigger repair */
3533 return( noErr
); /* successful return */
3536 /*------------------------------------------------------------------------------
3538 Name: RecordBadExtent
3540 Function: Allocates a RepairOrder for repairing bad extent.
3542 Input: GPtr - ptr to scavenger global data
3543 fileID - fileID of the file with bad extent
3544 forkType - bad extent's fork type
3545 startBlock - start block of the bad extent record
3546 badExtentIndex - index of bad extent entry in the extent record
3548 Output: 0 - no error
3549 R_NoMem - not enough mem to allocate record
3550 ------------------------------------------------------------------------------*/
3552 static int RecordBadExtent(SGlobPtr GPtr
, UInt32 fileID
, UInt8 forkType
,
3553 UInt32 startBlock
, UInt32 badExtentIndex
)
3558 isHFSPlus
= VolumeObjectIsHFSPlus();
3560 p
= AllocMinorRepairOrder(GPtr
, 0);
3566 p
->forkType
= forkType
;
3567 p
->correct
= badExtentIndex
;
3568 p
->hint
= startBlock
;
3571 GPtr
->CatStat
|= S_BadExtent
;
3576 * Build a catalog node thread key.
3578 __unused
static void
3579 buildthreadkey(UInt32 parentID
, int std_hfs
, CatalogKey
*key
)
3582 key
->hfs
.keyLength
= kHFSCatalogKeyMinimumLength
;
3583 key
->hfs
.reserved
= 0;
3584 key
->hfs
.parentID
= parentID
;
3585 key
->hfs
.nodeName
[0] = 0;
3587 key
->hfsPlus
.keyLength
= kHFSPlusCatalogKeyMinimumLength
;
3588 key
->hfsPlus
.parentID
= parentID
;
3589 key
->hfsPlus
.nodeName
.length
= 0;
3595 printpath(SGlobPtr GPtr
, UInt32 fileID
)
3598 char path
[PATH_MAX
* 4];
3599 unsigned int pathlen
= PATH_MAX
* 4;
3601 if (fileID
< kHFSFirstUserCatalogNodeID
) {
3603 case kHFSExtentsFileID
:
3604 printf("$Extents_Overflow_File\n");
3606 case kHFSCatalogFileID
:
3607 printf("$Catalog_File\n");
3609 case kHFSAllocationFileID
:
3610 printf("$Allocation_Bitmap_File\n");
3612 case kHFSAttributesFileID
:
3613 printf("$Attributes_File\n");
3616 printf("$File_ID_%d\n", fileID
);
3621 result
= GetFileNamePathByID(GPtr
, fileID
, path
, &pathlen
, NULL
, NULL
, NULL
);
3623 printf ("error %d getting path for id=%u\n", result
, fileID
);
3626 printf("\"ROOT_OF_VOLUME%s\" (file id=%u)\n", path
, fileID
);
3630 CheckPhysicalMatch(SVCB
*vcb
, UInt32 startblk
, UInt32 blkcount
, UInt32 fileNumber
, UInt8 forkType
)
3633 u_int64_t blk
, blk1
, blk2
;
3636 offset
= (u_int64_t
) startblk
* (u_int64_t
) vcb
->vcbBlockSize
;
3638 if (vcb
->vcbSignature
== kHFSPlusSigWord
)
3639 offset
+= vcb
->vcbEmbeddedOffset
; // offset into the wrapper
3641 offset
+= vcb
->vcbAlBlSt
* 512ULL; // offset to start of volume
3643 blk1
= offset
/ gBlockSize
;
3644 blk2
= blk1
+ ((blkcount
* vcb
->vcbBlockSize
) / gBlockSize
);
3646 for (i
= 0; i
< gBlkListEntries
; ++i
) {
3647 blk
= gBlockList
[i
];
3649 if (blk
>= blk1
&& blk
< blk2
) {
3650 // printf("block %d is in file %d\n", blk, fileNumber);
3651 /* Do we need to grow the found blocks list? */
3652 if (gFoundBlockEntries
% FOUND_BLOCKS_QUANTUM
== 0) {
3653 struct found_blocks
*new_blocks
;
3654 new_blocks
= realloc(gFoundBlocksList
, (gFoundBlockEntries
+ FOUND_BLOCKS_QUANTUM
) * sizeof(struct found_blocks
));
3655 if (new_blocks
== NULL
) {
3656 fprintf(stderr
, "CheckPhysicalMatch: Out of memory!\n");
3659 gFoundBlocksList
= new_blocks
;
3661 gFoundBlocksList
[gFoundBlockEntries
].block
= blk
;
3662 gFoundBlocksList
[gFoundBlockEntries
].fileID
= fileNumber
;
3663 ++gFoundBlockEntries
;
3668 static int compare_found_blocks(const void *x1_arg
, const void *x2_arg
)
3670 const struct found_blocks
*x1
= x1_arg
;
3671 const struct found_blocks
*x2
= x2_arg
;
3673 if (x1
->block
< x2
->block
)
3675 else if (x1
->block
> x2
->block
)
3678 if (x1
->fileID
< x2
->fileID
)
3680 else if (x1
->fileID
> x2
->fileID
)
3688 dumpblocklist(SGlobPtr GPtr
)
3693 /* Sort the found blocks */
3694 qsort(gFoundBlocksList
, gFoundBlockEntries
, sizeof(struct found_blocks
), compare_found_blocks
);
3697 * Print out the blocks with matching files. In the case of overlapped
3698 * extents, the same block number will be printed multiple times, with
3699 * each file containing an overlapping extent. If overlapping extents
3700 * come from the same file, then that path will be printed multiple times.
3702 for (i
= 0; i
< gFoundBlockEntries
; ++i
) {
3703 block
= gFoundBlocksList
[i
].block
;
3705 printf("block %llu:\t", (unsigned long long) block
);
3706 printpath(GPtr
, gFoundBlocksList
[i
].fileID
);
3708 /* Remove block from the gBlockList */
3709 for (j
= 0; j
< gBlkListEntries
; ++j
) {
3710 if (gBlockList
[j
] == block
) {
3711 gBlockList
[j
] = gBlockList
[--gBlkListEntries
];
3717 /* Print out the blocks without matching files */
3718 for (j
= 0; j
< gBlkListEntries
; ++j
) {
3719 printf("block %llu:\t*** NO MATCH ***\n", (unsigned long long) gBlockList
[j
]);
3723 /*------------------------------------------------------------------------------
3725 Function: CheckFileExtents - (Check File Extents)
3728 Verifies the extent info for a file data or extented attribute data. It
3729 checks the correctness of extent data. If the extent information is
3730 correct/valid, it updates in-memory volume bitmap, total number of valid
3731 blocks for given file, and if overlapping extents exist, adds them to
3732 the overlap extents list. If the extent information is not correct, it
3733 considers the file truncated beyond the bad extent entry and reports
3734 only the total number of good blocks seen. Therefore the caller detects
3735 adds the extent information to repair order. It does not include the
3736 invalid extent and any extents after it for checking volume bitmap and
3737 hence overlapping extents. Note that currently the function
3738 returns error if invalid extent is found for system files or for
3739 extended attributes.
3741 For data fork and resource fork of file - This function checks extent
3742 record present in catalog record as well as extent overflow records, if
3743 any, for given fileID.
3745 For extended attribute data - This function only checks the extent record
3746 passed as parameter. If any extended attribute has overflow extents in
3747 the attribute btree, this function does not look them up. It is the left
3748 to the caller to check remaining extents for given file's extended attribute.
3751 GPtr - pointer to scavenger global area
3752 fileNumber - file number for fork/extended attribute
3753 forkType - fork type
3754 00 - kDataFork - data fork
3755 01 - kEAData - extended attribute data extent
3756 ff - kRsrcFork - resource fork
3757 attrname - if fork type is kEAData, attrname contains pointer to the
3758 name of extended attribute whose extent is being checked; else
3759 it should be NULL. Note that the function assumes that this is
3760 NULL-terminated string.
3761 extents - ptr to 1st extent record for the file
3764 CheckFileExtents - function result:
3767 blocksUsed - number of allocation blocks allocated to the file
3768 ------------------------------------------------------------------------------*/
3770 OSErr
CheckFileExtents( SGlobPtr GPtr
, UInt32 fileNumber
, UInt8 forkType
,
3771 const unsigned char *attrname
, const void *extents
,
3774 UInt32 blockCount
= 0;
3775 UInt32 extentBlockCount
;
3776 UInt32 extentStartBlock
;
3778 HFSPlusExtentKey key
;
3779 HFSPlusExtentKey extentKey
;
3780 HFSPlusExtentRecord extentRecord
;
3784 Boolean firstRecord
;
3786 unsigned int lastExtentIndex
;
3787 Boolean foundBadExtent
;
3789 /* For all extended attribute extents, the attrname should not be NULL */
3790 if (forkType
== kEAData
) {
3791 assert(attrname
!= NULL
);
3794 isHFSPlus
= VolumeObjectIsHFSPlus( );
3796 foundBadExtent
= false;
3797 lastExtentIndex
= GPtr
->numExtents
;
3799 while ( (extents
!= nil
) && (err
== noErr
) )
3801 // checkout the extent record first
3802 err
= ChkExtRec( GPtr
, fileNumber
, extents
, &lastExtentIndex
);
3804 DPRINTF (d_info
, "%s: Bad extent for fileID %u in extent %u for startblock %u\n", __FUNCTION__
, fileNumber
, lastExtentIndex
, blockCount
);
3805 if (cur_debug_level
& d_dump_record
)
3808 HexDump(extents
, sizeof(HFSPlusExtentRecord
), FALSE
);
3812 /* Stop verification if bad extent is found for system file or EA */
3813 if ((fileNumber
< kHFSFirstUserCatalogNodeID
) ||
3814 (forkType
== kEAData
)) {
3818 /* store information about bad extent in repair order */
3819 (void) RecordBadExtent(GPtr
, fileNumber
, forkType
, blockCount
, lastExtentIndex
);
3820 foundBadExtent
= true;
3824 /* Check only till the last valid extent entry reported by ChkExtRec */
3825 for ( i
=0 ; i
<lastExtentIndex
; i
++ ) // now checkout the extents
3827 // HFS+/HFS moving extent fields into local variables for evaluation
3828 if ( isHFSPlus
== true )
3830 extentBlockCount
= ((HFSPlusExtentDescriptor
*)extents
)[i
].blockCount
;
3831 extentStartBlock
= ((HFSPlusExtentDescriptor
*)extents
)[i
].startBlock
;
3835 extentBlockCount
= ((HFSExtentDescriptor
*)extents
)[i
].blockCount
;
3836 extentStartBlock
= ((HFSExtentDescriptor
*)extents
)[i
].startBlock
;
3839 if ( extentBlockCount
== 0 )
3842 if (gBlkListEntries
!= 0)
3843 CheckPhysicalMatch(GPtr
->calculatedVCB
, extentStartBlock
, extentBlockCount
, fileNumber
, forkType
);
3845 err
= CaptureBitmapBits(extentStartBlock
, extentBlockCount
);
3846 if (err
== E_OvlExt
) {
3847 err
= AddExtentToOverlapList(GPtr
, fileNumber
, (char *)attrname
, extentStartBlock
, extentBlockCount
, forkType
);
3850 blockCount
+= extentBlockCount
;
3853 if ( fileNumber
== kHFSExtentsFileID
) // Extents file has no overflow extents
3856 /* Found bad extent for this file, do not find any extents after
3857 * current extent. We assume that the file is truncated at the
3860 if (foundBadExtent
== true) {
3864 /* For extended attributes, only check the extent passed as parameter. The
3865 * caller will take care of checking other extents, if any, for given
3866 * extended attribute.
3868 if (forkType
== kEAData
) {
3872 if ( firstRecord
== true )
3874 firstRecord
= false;
3876 // Set up the extent key
3877 BuildExtentKey( isHFSPlus
, forkType
, fileNumber
, blockCount
, (void *)&key
);
3879 err
= SearchBTreeRecord( GPtr
->calculatedExtentsFCB
, &key
, kNoHint
, (void *) &extentKey
, (void *) &extentRecord
, &recSize
, &hint
);
3881 if ( err
== btNotFound
)
3883 err
= noErr
; // no more extent records
3887 else if ( err
!= noErr
)
3889 err
= IntError( GPtr
, err
); // error from SearchBTreeRecord
3895 err
= GetBTreeRecord( GPtr
->calculatedExtentsFCB
, 1, &extentKey
, extentRecord
, &recSize
, &hint
);
3897 if ( err
== btNotFound
)
3899 err
= noErr
; // no more extent records
3903 else if ( err
!= noErr
)
3905 err
= IntError( GPtr
, err
); /* error from BTGetRecord */
3909 // Check same file and fork
3912 if ( (extentKey
.fileID
!= fileNumber
) || (extentKey
.forkType
!= forkType
) )
3917 if ( (((HFSExtentKey
*) &extentKey
)->fileID
!= fileNumber
) || (((HFSExtentKey
*) &extentKey
)->forkType
!= forkType
) )
3922 extents
= (void *) &extentRecord
;
3925 *blocksUsed
= blockCount
;
3931 void BuildExtentKey( Boolean isHFSPlus
, UInt8 forkType
, HFSCatalogNodeID fileNumber
, UInt32 blockNumber
, void * key
)
3935 HFSPlusExtentKey
*hfsPlusKey
= (HFSPlusExtentKey
*) key
;
3937 hfsPlusKey
->keyLength
= kHFSPlusExtentKeyMaximumLength
;
3938 hfsPlusKey
->forkType
= forkType
;
3939 hfsPlusKey
->pad
= 0;
3940 hfsPlusKey
->fileID
= fileNumber
;
3941 hfsPlusKey
->startBlock
= blockNumber
;
3945 HFSExtentKey
*hfsKey
= (HFSExtentKey
*) key
;
3947 hfsKey
->keyLength
= kHFSExtentKeyMaximumLength
;
3948 hfsKey
->forkType
= forkType
;
3949 hfsKey
->fileID
= fileNumber
;
3950 hfsKey
->startBlock
= (UInt16
) blockNumber
;
3957 // Adds this extent to our OverlappedExtentList for later repair.
3959 static OSErr
AddExtentToOverlapList( SGlobPtr GPtr
, HFSCatalogNodeID fileNumber
, const char *attrname
, UInt32 extentStartBlock
, UInt32 extentBlockCount
, UInt8 forkType
)
3961 size_t newHandleSize
;
3962 ExtentInfo extentInfo
;
3963 ExtentsTable
**extentsTableH
;
3966 ClearMemory(&extentInfo
, sizeof(extentInfo
));
3967 extentInfo
.fileID
= fileNumber
;
3968 extentInfo
.startBlock
= extentStartBlock
;
3969 extentInfo
.blockCount
= extentBlockCount
;
3970 extentInfo
.forkType
= forkType
;
3971 /* store the name of extended attribute */
3972 if (forkType
== kEAData
) {
3973 assert(attrname
!= NULL
);
3975 attrlen
= strlen(attrname
) + 1;
3976 extentInfo
.attrname
= malloc(attrlen
);
3977 if (extentInfo
.attrname
== NULL
) {
3980 strlcpy(extentInfo
.attrname
, attrname
, attrlen
);
3983 // If it's uninitialized
3984 if ( GPtr
->overlappedExtents
== nil
)
3986 GPtr
->overlappedExtents
= (ExtentsTable
**) NewHandleClear( sizeof(ExtentsTable
) );
3987 extentsTableH
= GPtr
->overlappedExtents
;
3991 extentsTableH
= GPtr
->overlappedExtents
;
3993 if ( ExtentInfoExists( extentsTableH
, &extentInfo
) == true )
3996 // Grow the Extents table for a new entry.
3997 newHandleSize
= ( sizeof(ExtentInfo
) ) + ( GetHandleSize( (Handle
)extentsTableH
) );
3998 SetHandleSize( (Handle
)extentsTableH
, newHandleSize
);
4001 // Copy the new extents into the end of the table
4002 CopyMemory( &extentInfo
, &((**extentsTableH
).extentInfo
[(**extentsTableH
).count
]), sizeof(ExtentInfo
) );
4004 // Update the overlap extent bit
4005 GPtr
->VIStat
|= S_OverlappingExtents
;
4007 // Update the extent table count
4008 (**extentsTableH
).count
++;
4014 /* Compare if the given extentInfo exsists in the extents table */
4015 static Boolean
ExtentInfoExists( ExtentsTable
**extentsTableH
, ExtentInfo
*extentInfo
)
4018 ExtentInfo
*aryExtentInfo
;
4021 for ( i
= 0 ; i
< (**extentsTableH
).count
; i
++ )
4023 aryExtentInfo
= &((**extentsTableH
).extentInfo
[i
]);
4025 if ( extentInfo
->fileID
== aryExtentInfo
->fileID
)
4027 if ( (extentInfo
->startBlock
== aryExtentInfo
->startBlock
) &&
4028 (extentInfo
->blockCount
== aryExtentInfo
->blockCount
) &&
4029 (extentInfo
->forkType
== aryExtentInfo
->forkType
) )
4031 /* startBlock, blockCount, forkType are same.
4032 * Compare the extended attribute names, if they exist.
4035 /* If no attribute name exists, the two extents are same */
4036 if ((extentInfo
->attrname
== NULL
) &&
4037 (aryExtentInfo
->attrname
== NULL
)) {
4041 /* If only one attribute name exists, the two extents are not same */
4042 if (((extentInfo
->attrname
!= NULL
) && (aryExtentInfo
->attrname
== NULL
)) ||
4043 ((extentInfo
->attrname
== NULL
) && (aryExtentInfo
->attrname
!= NULL
))) {
4047 /* Both attribute name exist. Compare the names */
4048 if (!strcmp(extentInfo
->attrname
, aryExtentInfo
->attrname
)) {
4061 /* Function : DoesOverlap
4064 * This function takes a start block and the count of blocks in a
4065 * given extent and compares it against the list of overlapped
4066 * extents in the global structure.
4067 * This is useful in finding the original files that overlap with
4068 * the files found in catalog btree check. If a file is found
4069 * overlapping, it is added to the overlap list.
4072 * 1. GPtr - global scavenger pointer.
4073 * 2. fileID - file ID being checked.
4074 * 3. attrname - name of extended attribute being checked, should be NULL for regular files
4075 * 4. startBlock - start block in extent.
4076 * 5. blockCount - total number of blocks in extent.
4077 * 6. forkType - type of fork being check (kDataFork, kRsrcFork, kEAData).
4079 * Output: isOverlapped - Boolean value of true or false.
4081 static Boolean
DoesOverlap(SGlobPtr GPtr
, UInt32 fileID
, const char *attrname
, UInt32 startBlock
, UInt32 blockCount
, UInt8 forkType
)
4084 Boolean isOverlapped
= false;
4085 ExtentInfo
*curExtentInfo
;
4086 ExtentsTable
**extentsTableH
= GPtr
->overlappedExtents
;
4088 for (i
= 0; i
< (**extentsTableH
).count
; i
++) {
4089 curExtentInfo
= &((**extentsTableH
).extentInfo
[i
]);
4091 if (curExtentInfo
->startBlock
< startBlock
) {
4092 if ((curExtentInfo
->startBlock
+ curExtentInfo
->blockCount
) > startBlock
) {
4093 isOverlapped
= true;
4096 } else { /* curExtentInfo->startBlock >= startBlock */
4097 if (curExtentInfo
->startBlock
< (startBlock
+ blockCount
)) {
4098 isOverlapped
= true;
4102 } /* for loop Extents Table */
4104 /* Add this extent to overlap list */
4106 AddExtentToOverlapList(GPtr
, fileID
, attrname
, startBlock
, blockCount
, forkType
);
4109 return isOverlapped
;
4112 /* Function : CheckHFSPlusExtentRecords
4115 * For all valid extents, this function calls DoesOverlap to find
4116 * if a given extent is overlapping with another extent existing
4117 * in the overlap list.
4120 * 1. GPtr - global scavenger pointer.
4121 * 2. fileID - file ID being checked.
4122 * 3. attrname - name of extended attribute being checked, should be NULL for regular files
4123 * 4. extent - extent information to check.
4124 * 5. forkType - type of fork being check (kDataFork, kRsrcFork, kEAData).
4128 static void CheckHFSPlusExtentRecords(SGlobPtr GPtr
, UInt32 fileID
, const char *attrname
, HFSPlusExtentRecord extent
, UInt8 forkType
)
4132 /* Check for overlapping extents for all extents in given extent data */
4133 for (i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
4134 if (extent
[i
].startBlock
== 0) {
4137 DoesOverlap(GPtr
, fileID
, attrname
, extent
[i
].startBlock
, extent
[i
].blockCount
, forkType
);
4140 } /* CheckHFSPlusExtentRecords */
4142 /* Function : CheckHFSExtentRecords
4145 * For all valid extents, this function calls DoesOverlap to find
4146 * if a given extent is overlapping with another extent existing
4147 * in the overlap list.
4150 * 1. GPtr - global scavenger pointer.
4151 * 2. fileID - file ID being checked.
4152 * 3. extent - extent information to check.
4153 * 4. forkType - type of fork being check (kDataFork, kRsrcFork).
4157 static void CheckHFSExtentRecords(SGlobPtr GPtr
, UInt32 fileID
, HFSExtentRecord extent
, UInt8 forkType
)
4161 /* Check for overlapping extents for all extents in given extents */
4162 for (i
= 0; i
< kHFSExtentDensity
; i
++) {
4163 if (extent
[i
].startBlock
== 0) {
4166 DoesOverlap(GPtr
, fileID
, NULL
, extent
[i
].startBlock
, extent
[i
].blockCount
, forkType
);
4169 } /* CheckHFSExtentRecords */
4171 /* Function: FindOrigOverlapFiles
4174 * This function is called only if btree check results in
4175 * overlapped extents errors. The btree checks do not find
4176 * out the original files whose extents are overlapping with one
4177 * being reported in its check. This function finds out all the
4178 * original files whose that are being overlapped.
4180 * This function relies on comparison of extents with Overlap list
4181 * created in verify stage. The list is also updated with the
4182 * overlapped extents found in this function.
4184 * 1. Compare extents for all the files located in volume header.
4185 * 2. Traverse catalog btree and compare extents of all files.
4186 * 3. Traverse extents btree and compare extents for all entries.
4188 * Input: GPtr - pointer to global scanvenger area.
4190 * Output: err - function result
4191 * zero means success
4192 * non-zero means failure
4194 int FindOrigOverlapFiles(SGlobPtr GPtr
)
4199 UInt16 selCode
; /* select access pattern for BTree */
4203 CatalogRecord catRecord
;
4206 ExtentRecord extentRecord
;
4207 ExtentKey extentKey
;
4209 HFSPlusAttrRecord attrRecord
;
4210 HFSPlusAttrKey attrKey
;
4211 char attrName
[XATTR_MAXNAMELEN
];
4214 SVCB
*calculatedVCB
= GPtr
->calculatedVCB
;
4216 isHFSPlus
= VolumeObjectIsHFSPlus();
4218 /* Check file extents from volume header */
4220 /* allocation file */
4221 if (calculatedVCB
->vcbAllocationFile
) {
4222 CheckHFSPlusExtentRecords(GPtr
, calculatedVCB
->vcbAllocationFile
->fcbFileID
, NULL
,
4223 calculatedVCB
->vcbAllocationFile
->fcbExtents32
, kDataFork
);
4227 if (calculatedVCB
->vcbExtentsFile
) {
4228 CheckHFSPlusExtentRecords(GPtr
, calculatedVCB
->vcbExtentsFile
->fcbFileID
, NULL
,
4229 calculatedVCB
->vcbExtentsFile
->fcbExtents32
, kDataFork
);
4233 if (calculatedVCB
->vcbCatalogFile
) {
4234 CheckHFSPlusExtentRecords(GPtr
, calculatedVCB
->vcbCatalogFile
->fcbFileID
, NULL
,
4235 calculatedVCB
->vcbCatalogFile
->fcbExtents32
, kDataFork
);
4238 /* attributes file */
4239 if (calculatedVCB
->vcbAttributesFile
) {
4240 CheckHFSPlusExtentRecords(GPtr
, calculatedVCB
->vcbAttributesFile
->fcbFileID
, NULL
,
4241 calculatedVCB
->vcbAttributesFile
->fcbExtents32
, kDataFork
);
4245 if (calculatedVCB
->vcbStartupFile
) {
4246 CheckHFSPlusExtentRecords(GPtr
, calculatedVCB
->vcbStartupFile
->fcbFileID
, NULL
,
4247 calculatedVCB
->vcbStartupFile
->fcbExtents32
, kDataFork
);
4251 if (calculatedVCB
->vcbExtentsFile
) {
4252 CheckHFSExtentRecords(GPtr
, calculatedVCB
->vcbExtentsFile
->fcbFileID
,
4253 calculatedVCB
->vcbExtentsFile
->fcbExtents16
, kDataFork
);
4257 if (calculatedVCB
->vcbCatalogFile
) {
4258 CheckHFSExtentRecords(GPtr
, calculatedVCB
->vcbCatalogFile
->fcbFileID
,
4259 calculatedVCB
->vcbCatalogFile
->fcbExtents16
, kDataFork
);
4263 /* Traverse the catalog btree */
4264 selCode
= 0x8001; /* Get first record from BTree */
4265 err
= GetBTreeRecord(GPtr
->calculatedCatalogFCB
, selCode
, &catKey
, &catRecord
, &recordSize
, &hint
);
4267 goto traverseExtents
;
4269 selCode
= 1; /* Get next record */
4271 if ((catRecord
.recordType
== kHFSPlusFileRecord
) ||
4272 (catRecord
.recordType
== kHFSFileRecord
)) {
4275 /* HFSPlus data fork */
4276 CheckHFSPlusExtentRecords(GPtr
, catRecord
.hfsPlusFile
.fileID
, NULL
,
4277 catRecord
.hfsPlusFile
.dataFork
.extents
, kDataFork
);
4279 /* HFSPlus resource fork */
4280 CheckHFSPlusExtentRecords(GPtr
, catRecord
.hfsPlusFile
.fileID
, NULL
,
4281 catRecord
.hfsPlusFile
.resourceFork
.extents
, kRsrcFork
);
4283 /* HFS data extent */
4284 CheckHFSExtentRecords(GPtr
, catRecord
.hfsFile
.fileID
,
4285 catRecord
.hfsFile
.dataExtents
, kDataFork
);
4287 /* HFS resource extent */
4288 CheckHFSExtentRecords(GPtr
, catRecord
.hfsFile
.fileID
,
4289 catRecord
.hfsFile
.rsrcExtents
, kRsrcFork
);
4293 /* Access the next record */
4294 err
= GetBTreeRecord( GPtr
->calculatedCatalogFCB
, selCode
, &catKey
, &catRecord
, &recordSize
, &hint
);
4295 } while (err
== noErr
);
4298 /* Traverse the extents btree */
4299 selCode
= 0x8001; /* Get first record from BTree */
4300 err
= GetBTreeRecord(GPtr
->calculatedExtentsFCB
, selCode
, &extentKey
, &extentRecord
, &recordSize
, &hint
);
4302 goto traverseAttribute
;
4304 selCode
= 1; /* Get next record */
4307 CheckHFSPlusExtentRecords(GPtr
, extentKey
.hfsPlus
.fileID
, NULL
,
4308 extentRecord
.hfsPlus
, extentKey
.hfsPlus
.forkType
);
4310 CheckHFSExtentRecords(GPtr
, extentKey
.hfs
.fileID
, extentRecord
.hfs
,
4311 extentKey
.hfs
.forkType
);
4314 /* Access the next record */
4315 err
= GetBTreeRecord(GPtr
->calculatedExtentsFCB
, selCode
, &extentKey
, &extentRecord
, &recordSize
, &hint
);
4316 } while (err
== noErr
);
4319 /* Extended attributes are only supported in HFS Plus */
4324 /* Traverse the attribute btree */
4325 selCode
= 0x8001; /* Get first record from BTree */
4326 /* Warning: Attribute record of type kHFSPlusAttrInlineData may be
4327 * truncated on read! (4425232). This function only uses recordType
4328 * field from inline attribute record.
4330 err
= GetBTreeRecord(GPtr
->calculatedAttributesFCB
, selCode
, &attrKey
, &attrRecord
, &recordSize
, &hint
);
4334 selCode
= 1; /* Get next record */
4336 if (attrRecord
.recordType
== kHFSPlusAttrForkData
) {
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
.forkData
.theFork
.extents
, kEAData
);
4341 } else if (attrRecord
.recordType
== kHFSPlusAttrExtents
) {
4342 (void) utf_encodestr(attrKey
.attrName
, attrKey
.attrNameLen
* 2, (unsigned char *)attrName
, &len
, sizeof(attrName
));
4343 attrName
[len
] = '\0';
4345 CheckHFSPlusExtentRecords(GPtr
, attrKey
.fileID
, attrName
, attrRecord
.overflowExtents
.extents
, kEAData
);
4348 /* Access the next record
4349 * Warning: Attribute record of type kHFSPlusAttrInlineData may be
4350 * truncated on read! (4425232). This function only uses recordType
4351 * field from inline attribute record.
4353 err
= GetBTreeRecord(GPtr
->calculatedAttributesFCB
, selCode
, &attrKey
, &attrRecord
, &recordSize
, &hint
);
4354 } while (err
== noErr
);
4357 if (err
== btNotFound
) {
4361 } /* FindOrigOverlapFiles */
4363 /* Function: PrintOverlapFiles
4365 * Description: Print the information about all unique overlapping files.
4366 * 1. Sort the overlap extent in increasing order of fileID
4367 * 2. For every unique fileID, prefix the string with fileID and find the
4368 * filename/path based on fileID.
4369 * If fileID > kHFSFirstUserCatalogNodeID, find path to file
4370 * Else, find name of the system file.
4371 * 3. Print the new string.
4372 * Note that the path is printed only for HFS Plus volumes and not for
4373 * plain HFS volumes. This is done by not allocating buffer for finding
4377 * GPtr - Global scavenger structure pointer.
4382 void PrintOverlapFiles (SGlobPtr GPtr
)
4385 ExtentsTable
**extentsTableH
;
4386 ExtentInfo
*extentInfo
;
4387 unsigned int numOverlapExtents
;
4388 unsigned int buflen
, filepathlen
;
4389 char *filepath
= NULL
;
4395 isHFSPlus
= VolumeObjectIsHFSPlus();
4397 extentsTableH
= GPtr
->overlappedExtents
;
4398 numOverlapExtents
= (**extentsTableH
).count
;
4400 /* Sort the list according to file ID */
4401 qsort((**extentsTableH
).extentInfo
, numOverlapExtents
, sizeof(ExtentInfo
),
4402 CompareExtentFileID
);
4404 buflen
= PATH_MAX
* 4;
4405 /* Allocate buffer to read data */
4407 filepath
= malloc (buflen
);
4410 for (i
= 0; i
< numOverlapExtents
; i
++) {
4411 extentInfo
= &((**extentsTableH
).extentInfo
[i
]);
4413 /* Skip the same fileID */
4414 if (lastID
== extentInfo
->fileID
) {
4418 lastID
= extentInfo
->fileID
;
4422 filepathlen
= buflen
;
4423 if (extentInfo
->fileID
>= kHFSFirstUserCatalogNodeID
) {
4424 /* Lookup the file path */
4425 err
= GetFileNamePathByID (GPtr
, extentInfo
->fileID
, filepath
, &filepathlen
, NULL
, NULL
, NULL
);
4427 /* Get system filename */
4428 err
= GetSystemFileName (extentInfo
->fileID
, filepath
, &filepathlen
);
4432 /* print fileID, filepath */
4433 fsckPrint(GPtr
->context
, E_OvlExt
, extentInfo
->fileID
, filepath
);
4437 if (fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
4438 plog ("\textentType=0x%x, startBlock=0x%x, blockCount=0x%x, attrName=%s\n",
4439 extentInfo
->forkType
, extentInfo
->startBlock
, extentInfo
->blockCount
, extentInfo
->attrname
);
4443 if (printMsg
== false) {
4444 /* print only fileID */
4445 fsckPrint(GPtr
->context
, E_OvlExtID
, extentInfo
->fileID
);
4454 } /* PrintOverlapFiles */
4456 /* Function: CompareExtentFileID
4458 * Description: Compares the fileID from two ExtentInfo and return the
4459 * comparison result. (since we have to arrange in ascending order)
4462 * first and second - void pointers to ExtentInfo structure.
4465 * >0 if first > second
4466 * =0 if first == second
4467 * <0 if first < second
4469 static int CompareExtentFileID(const void *first
, const void *second
)
4471 return (((ExtentInfo
*)first
)->fileID
-
4472 ((ExtentInfo
*)second
)->fileID
);
4473 } /* CompareExtentFileID */
4475 /* Function: journal_replay
4477 * Description: Replay journal on a journaled HFS+ volume. This function
4478 * returns success if the volume is not journaled or the journal was not
4479 * dirty. If there was any error in replaying the journal, a non-zero value
4483 * 0 - success, non-zero - failure.
4485 //int journal_replay(SGlobPtr gptr)
4486 int journal_replay(const char *block_device
)
4493 jfd
= open(block_device
, O_RDWR
);
4497 fplog(stderr
, "Unable to open block device %s: %s", block_device
, strerror(errno
));
4501 retval
= getvfsbyname("hfs", &vfc
);
4508 mib
[1] = vfc
.vfc_typenum
;
4509 mib
[2] = HFS_REPLAY_JOURNAL
;
4511 retval
= sysctl(mib
, 4, NULL
, NULL
, NULL
, 0);