2 * Copyright (c) 1999-2011 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
26 Contains: This file contains the routines which control the scavenging operations.
28 Version: xxx put version here xxx
30 Written by: Bill Bruffey
32 Copyright: © 1985, 1986, 1992-1999 by Apple Computer, Inc., all rights reserved.
35 #define SHOW_ELAPSED_TIMES 0
38 #if SHOW_ELAPSED_TIMES
42 #include "Scavenger.h"
43 #include "fsck_journal.h"
47 #ifndef CONFIG_HFS_TRIM
48 #define CONFIG_HFS_TRIM 1
51 #define DisplayTimeRemaining 0
53 /* Variable containing diskdev_cmds tag number and date/time when the binary was built.
54 * This variable is populated automatically using version.pl by B&I when building the
55 * project. For development purposes, if the current directory name looks something
56 * like a tag name or a version number is provided to buildit, buildit populates it
57 * correctly. For all other ways to build the code, like 'make', the tag number will
58 * be left empty and only project name and build date/time will be shown.
60 * TODO: Get this building properly within Xcode, without need for the version.pl script!
62 extern const unsigned char fsck_hfsVersionString
[];
68 // Static function prototypes
70 static void printVerifyStatus( SGlobPtr GPtr
);
71 static Boolean
IsBlueBoxSharedDrive ( DrvQElPtr dqPtr
);
72 static int ScavSetUp( SGlobPtr GPtr
);
73 static int ScavTerm( SGlobPtr GPtr
);
75 /* this procedure receives progress calls and will allow canceling of procedures - we are using it here to print out the progress of the current operation for DFA and DiskUtility - ESP 1/10/00 */
77 int cancelProc(UInt16 progress
, UInt16 secondsRemaining
, Boolean progressChanged
, UInt16 stage
, void *context
, int passno
)
79 if (progressChanged
) {
83 static int lastPct
= -1;
88 base
= (passno
* 100) / kMaxReScan
; // Multiply by 100 because we're doing ints
89 scale
= 100 / kMaxReScan
;
91 pct
= ((progress
* scale
) / 100) + base
;
92 if (pct
!= lastPct
&& pct
!= 100) {
93 fsckPrint((fsck_ctx_t
)context
, fsckProgress
, pct
);
101 static const int kMaxMediumErrors
= 25;
104 * Determine whether an error is major or minor. The main critera we chose for
105 * this is whether you can continue to use -- reading, creating, and deleting --
106 * in a volume with the error present. This should at some point go into the
107 * message structure itself.
110 isMinorError(int msg
, int *counts
)
115 case hfsCatHierCheck
:
116 case hfsExtAttrBTCheck
:
117 case hfsVolBitmapCheck
:
118 case hfsVolInfoCheck
:
119 case hfsHardLinkCheck
:
120 case hfsRebuildExtentBTree
:
121 case hfsRebuildCatalogBTree
:
122 case hfsRebuildAttrBTree
:
123 case hfsCaseSensitive
:
124 case hfsMultiLinkDirCheck
:
125 case hfsJournalVolCheck
:
126 case hfsLiveVerifyCheck
:
127 case hfsVerifyVolWithWrite
:
142 case E_CatalogFlagsNotZero
:
144 case E_InvalidClumpSize
:
145 case E_LockedDirName
:
151 case E_InvalidLinkCount
:
153 case E_InvalidPermissions
:
154 case E_InvalidUID_Unused
:
156 case E_IncorrectNumThdRcd
:
157 case E_SymlinkCreate
:
158 case E_IncorrectAttrCount
:
159 case E_IncorrectSecurityCount
:
164 case E_BadPermPrivDir
:
165 case E_DirInodeBadFlags
:
166 case E_DirInodeBadParent
:
167 case E_DirInodeBadName
:
168 case E_DirHardLinkChain
:
169 case E_DirHardLinkOwnerFlags
:
170 case E_DirHardLinkFinderInfo
:
171 case E_DirLinkAncestorFlags
:
172 case E_DirHardLinkNesting
:
173 case E_InvalidLinkChainPrev
:
174 case E_InvalidLinkChainNext
:
175 case E_FileInodeBadFlags
:
176 case E_FileInodeBadName
:
177 case E_FileHardLinkChain
:
178 case E_FileHardLinkFinderInfo
:
179 case E_InvalidLinkChainFirst
:
180 case E_FileLinkBadFlags
:
181 case E_DirLinkBadFlags
:
182 case E_OrphanFileLink
:
183 case E_OrphanDirLink
:
184 case E_OrphanFileInode
:
185 case E_OrphanDirInode
:
186 case E_UnusedNodeNotZeroed
:
187 case E_VBMDamagedOverAlloc
:
189 case E_BadSymLinkLength
:
190 case E_BadSymLinkName
:
193 * A lot of EOF errors may indicate that there were some more significant
194 * problems with the volume; just one by itself, with no other volume layout
195 * problems, won't affect the volume usage. So we keep track of them.
199 if (++counts
[abs(msg
)] > kMaxMediumErrors
)
207 /*------------------------------------------------------------------------------
210 Routines: CheckHFS - Controls the scavenging process.
212 ------------------------------------------------------------------------------*/
214 static jmp_buf envBuf
;
216 CheckHFS( const char *rdevnode
, int fsReadRef
, int fsWriteRef
, int checkLevel
,
217 int repairLevel
, fsck_ctx_t fsckContext
, int lostAndFoundMode
,
218 int canWrite
, int *modified
, int liveMode
, int rebuildOptions
)
220 SGlob dataArea
; // Allocate the scav globals
222 FileIdentifierTable
*fileIdentifierTable
= nil
;
228 Boolean exitEarly
= 0;
229 __block
int *msgCounts
= NULL
;
230 Boolean majorErrors
= 0;
232 if (checkLevel
== kMajorCheck
) {
233 checkLevel
= kForceCheck
;
235 msgCounts
= malloc(sizeof(int) * E_LastError
);
238 autoRepair
= (fsWriteRef
!= -1 && repairLevel
!= kNeverRepair
);
240 /* Initialize the messages only once before the verify stage */
242 extern fsck_message_t hfs_messages
[];
243 extern fsck_message_t hfs_errors
[];
245 if (fsckAddMessages(fsckContext
, hfs_messages
) == -1 ||
246 fsckAddMessages(fsckContext
, hfs_errors
) == -1) {
253 * Get the project name and version that is being built.
255 * The __fsck_hfsVersionString contents are of the form:
256 * "@(#)PROGRAM:fsck_hfs PROJECT:hfs-557~332\n"
259 const char project
[] = " PROJECT:";
262 tmp
= strstr((const char *)fsck_hfsVersionString
, project
);
264 vstr
= strdup(tmp
+ strlen(project
));
265 tmp
= strstr(vstr
, "\n");
269 vstr
= strdup((const char *)fsck_hfsVersionString
);
272 fsckPrint(fsckContext
, fsckInformation
, "fsck_hfs", vstr
);
276 if (setjmp(envBuf
) == 1) {
278 * setjmp() returns the second argument to longjmp(), so if it returns 1, then
279 * we've hit a major error.
281 dataArea
.RepLevel
= repairLevelVeryMinorErrors
;
285 if (exitEarly
&& fsckContext
) {
287 * Set the after-printing block to a small bit of code that checks to see if
288 * the message in question corresponds to a major or a minor error. If it's
289 * major, we longjmp just above, which causes us to exit out early.
291 fsckSetBlock(fsckContext
, fsckPhaseAfterMessage
, (fsckBlock_t
) ^(fsck_ctx_t c
, int msgNum
, va_list args
) {
292 if (abs(msgNum
) > E_FirstError
&& abs(msgNum
) < E_LastError
) {
293 if (isMinorError(abs(msgNum
), msgCounts
) == 1)
294 return fsckBlockContinue
;
296 return fsckBlockAbort
;
298 return fsckBlockContinue
;
304 ClearMemory( &dataArea
, sizeof(SGlob
) );
306 memset(msgCounts
, 0, sizeof(int) * E_LastError
);
308 // Initialize some scavenger globals
309 dataArea
.itemsProcessed
= 0; // Initialize to 0% complete
310 dataArea
.itemsToProcess
= 1;
311 dataArea
.chkLevel
= checkLevel
;
312 dataArea
.repairLevel
= repairLevel
;
313 dataArea
.rebuildOptions
= rebuildOptions
;
314 dataArea
.canWrite
= canWrite
;
315 dataArea
.writeRef
= fsWriteRef
;
316 dataArea
.lostAndFoundMode
= lostAndFoundMode
;
317 dataArea
.DrvNum
= fsReadRef
;
318 dataArea
.liveVerifyState
= liveMode
;
319 dataArea
.scanCount
= scanCount
;
320 if (strlcpy(dataArea
.deviceNode
, rdevnode
, sizeof(dataArea
.deviceNode
)) != strlen(rdevnode
)) {
321 dataArea
.deviceNode
[0] = '\0';
324 /* there are cases where we cannot get the name of the volume so we */
325 /* set our default name to one blank */
326 dataArea
.volumeName
[ 0 ] = ' ';
327 dataArea
.volumeName
[ 1 ] = '\0';
330 dataArea
.context
= fsckContext
;
331 dataArea
.guiControl
= true;
332 dataArea
.userCancelProc
= cancelProc
;
335 // Initialize the scavenger
337 ScavCtrl( &dataArea
, scavInitialize
, &scavError
);
338 if ( checkLevel
== kNeverCheck
|| (checkLevel
== kDirtyCheck
&& dataArea
.cleanUnmount
) ||
339 scavError
== R_NoMem
|| scavError
== R_BadSig
) {
340 // also need to bail when allocate fails in ScavSetUp or we bus error!
344 isJournaled
= CheckIfJournaled( &dataArea
, false );
345 if (isJournaled
!= 0 &&
347 checkLevel
!= kForceCheck
&&
348 !(checkLevel
== kPartialCheck
&& repairLevel
== kForceRepairs
)) {
349 if (fsckGetOutputStyle(dataArea
.context
) == fsckOutputTraditional
) {
350 plog("fsck_hfs: Volume is journaled. No checking performed.\n");
351 plog("fsck_hfs: Use the -f option to force checking.\n");
356 dataArea
.calculatedVCB
->vcbDriveNumber
= fsReadRef
;
357 dataArea
.calculatedVCB
->vcbDriverWriteRef
= fsWriteRef
;
359 // Only show the progress bar if we're doing a real check.
365 // Now verify the volume
367 if ( scavError
== noErr
)
368 ScavCtrl( &dataArea
, scavVerify
, &scavError
);
371 if (scavError
== noErr
&& fsckGetVerbosity(dataArea
.context
) >= kDebugLog
)
372 printVerifyStatus(&dataArea
);
374 // Looped for maximum times for verify and repair. This was the last verify and
375 // we bail out if problems were found
376 if (scanCount
>= kMaxReScan
&& (dataArea
.RepLevel
!= repairLevelNoProblemsFound
)) {
377 fsckPrint(dataArea
.context
, fsckVolumeNotRepairedTries
, dataArea
.volumeName
, scanCount
);
382 if ( dataArea
.RepLevel
== repairLevelUnrepairable
)
383 err
= cdUnrepairableErr
;
386 (dataArea
.RepLevel
== repairLevelVolumeRecoverable
||
387 dataArea
.RepLevel
== repairLevelCatalogBtreeRebuild
||
388 dataArea
.RepLevel
== repairLevelVeryMinorErrors
) ) {
389 fsckPrint(dataArea
.context
, fsckVolumeCorruptNeedsRepair
, dataArea
.volumeName
);
394 if ( scavError
== noErr
&& dataArea
.RepLevel
== repairLevelNoProblemsFound
) {
395 if (CONFIG_HFS_TRIM
&&
396 (dataArea
.canWrite
!= 0) && (dataArea
.writeRef
!= -1) &&
399 fsckPrint(dataArea
.context
, fsckTrimming
);
400 TrimFreeBlocks(&dataArea
);
403 if (scanCount
== 0) {
404 fsckPrint(dataArea
.context
, fsckVolumeOK
, dataArea
.volumeName
);
406 fsckPrint(dataArea
.context
, fsckRepairSuccessful
, dataArea
.volumeName
);
411 // Repair the volume if it needs repairs, its repairable and we were able to unmount it
413 if ( dataArea
.RepLevel
== repairLevelNoProblemsFound
&& repairLevel
== kForceRepairs
)
415 if (rebuildOptions
& REBUILD_CATALOG
) {
416 dataArea
.CBTStat
|= S_RebuildBTree
;
418 if (rebuildOptions
& REBUILD_EXTENTS
) {
419 dataArea
.EBTStat
|= S_RebuildBTree
;
421 if (rebuildOptions
& REBUILD_ATTRIBUTE
) {
422 dataArea
.ABTStat
|= S_RebuildBTree
;
424 dataArea
.RepLevel
= repairLevelCatalogBtreeRebuild
;
427 if ( ((scavError
== noErr
) || (scavError
== errRebuildBtree
)) &&
428 (autoRepair
== true) &&
429 (dataArea
.RepLevel
!= repairLevelUnrepairable
) &&
430 (dataArea
.RepLevel
!= repairLevelNoProblemsFound
) )
432 // we cannot repair a volume when others have write access to the block device
435 if ( dataArea
.canWrite
== 0 ) {
437 fsckPrint(dataArea
.context
, fsckVolumeNotRepairedInUse
, dataArea
.volumeName
);
440 ScavCtrl( &dataArea
, scavRepair
, &scavError
);
442 if ( scavError
== noErr
)
444 *modified
= 1; /* Report back that we made repairs */
446 /* we just repaired a volume, so scan it again to check if it corrected everything properly */
447 ScavCtrl( &dataArea
, scavTerminate
, &temp
);
448 repairLevel
= kMajorRepairs
;
449 checkLevel
= kAlwaysCheck
;
450 fsckPrint(dataArea
.context
, fsckRecheckingVolume
);
455 fsckPrint(dataArea
.context
, fsckVolumeNotRepaired
, dataArea
.volumeName
);
458 else if ( scavError
!= noErr
) {
460 fsckPrint(dataArea
.context
, fsckVolumeVerifyIncomplete
, dataArea
.volumeName
);
461 if ( fsckGetVerbosity(dataArea
.context
) >= kDebugLog
)
462 plog("\tvolume check failed with error %d \n", scavError
);
465 // Set up structures for post processing
466 if ( (autoRepair
== true) && (dataArea
.fileIdentifierTable
!= nil
) )
468 // *repairInfo = *repairInfo | kVolumeHadOverlappingExtents; // Report back that volume has overlapping extents
469 fileIdentifierTable
= (FileIdentifierTable
*) AllocateMemory( GetHandleSize( (Handle
) dataArea
.fileIdentifierTable
) );
470 CopyMemory( *(dataArea
.fileIdentifierTable
), fileIdentifierTable
, GetHandleSize( (Handle
) dataArea
.fileIdentifierTable
) );
477 if ( fileIdentifierTable
!= nil
)
479 DisposeMemory( fileIdentifierTable
);
483 if (gBlkListEntries
!= 0)
484 dumpblocklist(&dataArea
);
491 // Terminate the scavenger
494 if ( fsckGetVerbosity(dataArea
.context
) >= kDebugLog
&&
495 (err
!= noErr
|| dataArea
.RepLevel
!= repairLevelNoProblemsFound
) )
497 if (err
!= 0 && embedded
== 1) {
500 uint32_t len
= 512; // the size of an HFS+ volume header
501 int rv
= CacheRead(&fscache
, offset
, len
, &buf
);
503 fprintf(stderr
, "Offset %llu length %u:\n", offset
, len
);
504 DumpData(buf
->Buffer
, len
);
505 CacheRelease(&fscache
, buf
, 0);
507 fprintf(stderr
, "%s(%d): rv = %d\n", __FUNCTION__
, __LINE__
, rv
);
512 // If we have write access on volume and we are allowed to write,
513 // mark the volume clean/dirty
514 if ((fsWriteRef
!= -1) && (dataArea
.canWrite
!= 0)) {
518 CheckForClean(&dataArea
, kMarkVolumeDirty
, &update
);
521 CheckForClean(&dataArea
, kMarkVolumeClean
, &update
);
524 /* Report back that volume was modified */
528 ScavCtrl( &dataArea
, scavTerminate
, &temp
); // Note: use a temp var so that real scav error can be returned
531 fsckPrint( fsckContext
, fsckProgress
, 100); // End each run with 100% message, if desired
535 if (exitEarly
&& majorErrors
)
546 /*------------------------------------------------------------------------------
548 Function: ScavCtrl - (Scavenger Control)
550 Function: Controls the scavenging process. Interfaces with the User Interface
551 Layer (written in PASCAL).
553 Input: ScavOp - scavenging operation to be performed:
555 scavInitialize = start initial volume check
556 scavVerify = start verify
557 scavRepair = start repair
558 scavTerminate = finished scavenge
560 GPtr - pointer to scavenger global area
563 Output: ScavRes - scavenge result code (R_xxx, or 0 if no error)
565 ------------------------------------------------------------------------------*/
567 void ScavCtrl( SGlobPtr GPtr
, UInt32 ScavOp
, short *ScavRes
)
571 #if SHOW_ELAPSED_TIMES
572 struct timeval myStartTime
;
573 struct timeval myEndTime
;
574 struct timeval myElapsedTime
;
575 struct timezone zone
;
579 // initialize some stuff
581 result
= noErr
; // assume good status
586 // dispatch next scavenge operation
590 case scavInitialize
: // INITIAL VOLUME CHECK
595 if ( ( result
= ScavSetUp( GPtr
) ) ) // set up BEFORE CheckForStop
597 if ( IsBlueBoxSharedDrive( GPtr
->DrvPtr
) )
599 if ( ( result
= CheckForStop( GPtr
) ) ) // in order to initialize wrCnt
602 /* Call for all chkLevel options and check return value only
603 * for kDirtyCheck for preen option and kNeverCheck for quick option
605 clean
= CheckForClean(GPtr
, kCheckVolume
, &modified
);
606 if ((GPtr
->chkLevel
== kDirtyCheck
) || (GPtr
->chkLevel
== kNeverCheck
)) {
608 /* volume was unmounted cleanly */
609 GPtr
->cleanUnmount
= true;
613 if (GPtr
->chkLevel
== kNeverCheck
) {
616 else if (clean
== 0) {
618 * We lie for journaled file systems since
619 * they get cleaned up in mount by replaying
621 * Note: CheckIfJournaled will return negative
622 * if it finds lastMountedVersion = FSK!.
624 if (CheckIfJournaled(GPtr
, false))
625 GPtr
->cleanUnmount
= true;
633 if (CheckIfJournaled(GPtr
, false)
634 && GPtr
->chkLevel
!= kForceCheck
635 && !(GPtr
->chkLevel
== kPartialCheck
&& GPtr
->repairLevel
== kForceRepairs
)
636 && !(GPtr
->chkLevel
== kAlwaysCheck
&& GPtr
->repairLevel
== kMajorRepairs
)) {
640 if (GPtr
->liveVerifyState
) {
641 fsckPrint(GPtr
->context
, hfsLiveVerifyCheck
);
642 } else if (GPtr
->canWrite
== 0 && nflag
== 0) {
643 fsckPrint(GPtr
->context
, hfsVerifyVolWithWrite
);
647 * In the first pass, if fsck_hfs is verifying a
648 * journaled volume, and it's not a live verification,
649 * check to see if the journal is empty. If it is not,
650 * flag it as a journal error, and print a message.
651 * (A live verify will almost certainly have a non-empty
652 * journal, but that should be safe in this case due
653 * to the freeze command flushing everything.)
655 if ((GPtr
->scanCount
== 0) &&
656 (CheckIfJournaled(GPtr
, true) == 1) &&
657 (GPtr
->canWrite
== 0 || GPtr
->writeRef
== -1) &&
659 fsckJournalInfo_t jnlInfo
= { 0 };
664 if (IsJournalEmpty(GPtr
, &jnlInfo
) == 0) {
665 // disable_journal can currently only be set with debug enabled
666 if (disable_journal
) {
667 fsckPrint(GPtr
->context
, E_DirtyJournal
);
668 GPtr
->JStat
|= S_DirtyJournal
;
670 (void)GetDeviceSize(GPtr
->calculatedVCB
->vcbDriveNumber
, &numBlocks
, &blockSize
);
672 // For debugging the cache. WAY to verbose to run with even normal debug
674 printf("Before journal replay\n");
678 if (journal_open(jnlInfo
.jnlfd
,
684 ^(off_t start
, void *data
, size_t len
) {
687 rv
= CacheRead(&fscache
, start
, (int)len
, &buf
);
690 memcpy(buf
->Buffer
, data
, len
);
691 rv
= CacheWrite(&fscache
, buf
, 0, kLockWrite
);
696 fsckPrint(GPtr
->context
, E_DirtyJournal
);
697 GPtr
->JStat
|= S_DirtyJournal
;
699 plog("Journal replay simulation succeeded\n");
701 // Still way too verbose to run
708 plog("Journal is empty\n");
710 if (jnlInfo
.jnlfd
!= -1)
711 close(jnlInfo
.jnlfd
);
712 if (jnlInfo
.name
!= NULL
)
716 result
= IVChk( GPtr
);
721 case scavVerify
: // VERIFY
724 #if SHOW_ELAPSED_TIMES
725 gettimeofday( &myStartTime
, &zone
);
728 /* Initialize volume bitmap structure */
729 if ( BitMapCheckBegin(GPtr
) != 0)
732 #if SHOW_ELAPSED_TIMES
733 gettimeofday( &myEndTime
, &zone
);
734 timersub( &myEndTime
, &myStartTime
, &myElapsedTime
);
735 plog( "\n%s - BitMapCheck elapsed time \n", __FUNCTION__
);
736 plog( "########## secs %d msecs %d \n\n",
737 myElapsedTime
.tv_sec
, myElapsedTime
.tv_usec
);
740 if ( IsBlueBoxSharedDrive( GPtr
->DrvPtr
) )
742 if ( ( result
= CheckForStop( GPtr
) ) )
745 #if SHOW_ELAPSED_TIMES
746 gettimeofday( &myStartTime
, &zone
);
749 /* Create calculated BTree structures */
750 if ( ( result
= CreateExtentsBTreeControlBlock( GPtr
) ) )
752 if ( ( result
= CreateCatalogBTreeControlBlock( GPtr
) ) )
754 if ( ( result
= CreateAttributesBTreeControlBlock( GPtr
) ) )
756 if ( ( result
= CreateExtendedAllocationsFCB( GPtr
) ) )
759 #if SHOW_ELAPSED_TIMES
760 gettimeofday( &myEndTime
, &zone
);
761 timersub( &myEndTime
, &myStartTime
, &myElapsedTime
);
762 plog( "\n%s - create control blocks elapsed time \n", __FUNCTION__
);
763 plog( ">>>>>>>>>>>>> secs %d msecs %d \n\n",
764 myElapsedTime
.tv_sec
, myElapsedTime
.tv_usec
);
767 // Now that preflight of the BTree structures is calculated, compute the CheckDisk items
768 CalculateItemCount( GPtr
, &GPtr
->itemsToProcess
, &GPtr
->onePercent
);
769 GPtr
->itemsProcessed
+= GPtr
->onePercent
; // We do this 4 times as set up in CalculateItemCount() to smooth the scroll
771 if ( ( result
= VLockedChk( GPtr
) ) )
774 GPtr
->itemsProcessed
+= GPtr
->onePercent
; // We do this 4 times as set up in CalculateItemCount() to smooth the scroll
775 fsckPrint(GPtr
->context
, hfsExtBTCheck
);
777 #if SHOW_ELAPSED_TIMES
778 gettimeofday( &myStartTime
, &zone
);
781 /* Verify extent btree structure */
782 if ((result
= ExtBTChk(GPtr
)))
785 #if SHOW_ELAPSED_TIMES
786 gettimeofday( &myEndTime
, &zone
);
787 timersub( &myEndTime
, &myStartTime
, &myElapsedTime
);
788 plog( "\n%s - ExtBTChk elapsed time \n", __FUNCTION__
);
789 plog( ">>>>>>>>>>>>> secs %d msecs %d \n\n",
790 myElapsedTime
.tv_sec
, myElapsedTime
.tv_usec
);
793 if ((result
= CheckForStop(GPtr
)))
796 GPtr
->itemsProcessed
+= GPtr
->onePercent
; // We do this 4 times as set up in CalculateItemCount() to smooth the scroll
798 /* Check extents of bad block file */
799 if ((result
= BadBlockFileExtentCheck(GPtr
)))
801 if ((result
= CheckForStop(GPtr
)))
804 GPtr
->itemsProcessed
+= GPtr
->onePercent
; // We do this 4 times as set up in CalculateItemCount() to smooth the scroll
805 GPtr
->itemsProcessed
+= GPtr
->onePercent
;
806 fsckPrint(GPtr
->context
, hfsCatBTCheck
);
808 #if SHOW_ELAPSED_TIMES
809 gettimeofday( &myStartTime
, &zone
);
812 if ( GPtr
->chkLevel
== kPartialCheck
)
814 /* skip the rest of the verify code path the first time */
815 /* through when we are rebuilding the catalog B-Tree file. */
816 /* we will be back here after the rebuild. */
817 if (GPtr
->rebuildOptions
& REBUILD_CATALOG
) {
818 GPtr
->CBTStat
|= S_RebuildBTree
;
820 if (GPtr
->rebuildOptions
& REBUILD_EXTENTS
) {
821 GPtr
->EBTStat
|= S_RebuildBTree
;
823 if (GPtr
->rebuildOptions
& REBUILD_ATTRIBUTE
) {
824 GPtr
->ABTStat
|= S_RebuildBTree
;
826 result
= errRebuildBtree
;
830 /* Check catalog btree. For given fileID, the function accounts
831 * for all extents existing in catalog record as well as in
832 * overflow extent btree
834 if ((result
= CheckCatalogBTree(GPtr
)))
837 #if SHOW_ELAPSED_TIMES
838 gettimeofday( &myEndTime
, &zone
);
839 timersub( &myEndTime
, &myStartTime
, &myElapsedTime
);
840 plog( "\n%s - CheckCatalogBTree elapsed time \n", __FUNCTION__
);
841 plog( ">>>>>>>>>>>>> secs %d msecs %d \n\n",
842 myElapsedTime
.tv_sec
, myElapsedTime
.tv_usec
);
845 if ((result
= CheckForStop(GPtr
)))
849 fsckPrint(GPtr
->context
, hfsCatHierCheck
);
851 #if SHOW_ELAPSED_TIMES
852 gettimeofday( &myStartTime
, &zone
);
855 /* Check catalog hierarchy */
856 if ((result
= CatHChk(GPtr
)))
859 #if SHOW_ELAPSED_TIMES
860 gettimeofday( &myEndTime
, &zone
);
861 timersub( &myEndTime
, &myStartTime
, &myElapsedTime
);
862 plog( "\n%s - CatHChk elapsed time \n", __FUNCTION__
);
863 plog( ">>>>>>>>>>>>> secs %d msecs %d \n\n",
864 myElapsedTime
.tv_sec
, myElapsedTime
.tv_usec
);
867 if ((result
= CheckForStop(GPtr
)))
870 if (VolumeObjectIsHFSX(GPtr
)) {
871 result
= CheckFolderCount(GPtr
);
875 if ((result
=CheckForStop(GPtr
)))
879 /* Check attribute btree. The function accounts for all extents
880 * for extended attributes whose values are stored in
883 if ((result
= AttrBTChk(GPtr
)))
886 if ((result
= CheckForStop(GPtr
)))
890 * fsck_hfs has accounted for all valid allocation blocks by
891 * traversing all catalog records and attribute records.
892 * These traversals may have found overlapping extents. Note
893 * that the overlapping extents are detected in CaptureBitmapBits
894 * when it tries to set a bit corresponding to allocation block
895 * and finds that it is already set. Therefore fsck_hfs does not
896 * know the orignal file involved overlapped extents.
898 if (GPtr
->VIStat
& S_OverlappingExtents
) {
899 /* Find original files involved in overlapped extents */
900 result
= FindOrigOverlapFiles(GPtr
);
905 /* Print all unique overlapping file IDs and paths */
906 (void) PrintOverlapFiles(GPtr
);
910 /* Directory inodes store first link information in
911 * an extended attribute. Therefore start directory
912 * hard link check after extended attribute checks.
914 result
= dirhardlink_check(GPtr
);
915 /* On error or unrepairable corruption, stop the verification */
916 if ((result
!= 0) || (GPtr
->CatStat
& S_LinkErrNoRepair
)) {
925 fsckPrint(GPtr
->context
, hfsVolBitmapCheck
);
927 #if SHOW_ELAPSED_TIMES
928 gettimeofday( &myStartTime
, &zone
);
931 /* Compare in-memory volume bitmap with on-disk bitmap */
932 if ((result
= CheckVolumeBitMap(GPtr
, false)))
935 #if SHOW_ELAPSED_TIMES
936 gettimeofday( &myEndTime
, &zone
);
937 timersub( &myEndTime
, &myStartTime
, &myElapsedTime
);
938 plog( "\n%s - CheckVolumeBitMap elapsed time \n", __FUNCTION__
);
939 plog( ">>>>>>>>>>>>> secs %d msecs %d \n\n",
940 myElapsedTime
.tv_sec
, myElapsedTime
.tv_usec
);
943 if ((result
= CheckForStop(GPtr
)))
946 fsckPrint(GPtr
->context
, hfsVolInfoCheck
);
948 #if SHOW_ELAPSED_TIMES
949 gettimeofday( &myStartTime
, &zone
);
952 /* Verify volume level information */
953 if ((result
= VInfoChk(GPtr
)))
956 #if SHOW_ELAPSED_TIMES
957 gettimeofday( &myEndTime
, &zone
);
958 timersub( &myEndTime
, &myStartTime
, &myElapsedTime
);
959 plog( "\n%s - VInfoChk elapsed time \n", __FUNCTION__
);
960 plog( ">>>>>>>>>>>>> secs %d msecs %d \n\n",
961 myElapsedTime
.tv_sec
, myElapsedTime
.tv_usec
);
964 stat
= GPtr
->VIStat
| GPtr
->ABTStat
| GPtr
->EBTStat
| GPtr
->CBTStat
|
965 GPtr
->CatStat
| GPtr
->JStat
;
969 if ( (GPtr
->RepLevel
== repairLevelNoProblemsFound
) || (GPtr
->RepLevel
== repairLevelVolumeRecoverable
) )
971 // 2200106, We isolate very minor errors so that if the volume cannot be unmounted
972 // CheckDisk will just return noErr
973 unsigned int minorErrors
= (GPtr
->CatStat
& ~S_LockedDirName
) |
974 GPtr
->VIStat
| GPtr
->ABTStat
| GPtr
->EBTStat
| GPtr
->CBTStat
| GPtr
->JStat
;
975 if ( minorErrors
== 0 )
976 GPtr
->RepLevel
= repairLevelVeryMinorErrors
;
978 GPtr
->RepLevel
= repairLevelVolumeRecoverable
;
981 else if ( GPtr
->RepLevel
== repairLevelNoProblemsFound
)
985 GPtr
->itemsProcessed
= GPtr
->itemsToProcess
;
986 result
= CheckForStop(GPtr
); // one last check for modified volume
990 case scavRepair
: // REPAIR
992 if ( IsBlueBoxSharedDrive( GPtr
->DrvPtr
) )
994 if ( ( result
= CheckForStop(GPtr
) ) )
996 if ( GPtr
->CBTStat
& S_RebuildBTree
997 || GPtr
->EBTStat
& S_RebuildBTree
998 || GPtr
->ABTStat
& S_RebuildBTree
) {
999 // fsckPrint(GPtr->context, hfsRebuildCatalogBTree);
1000 // fsckPrint(GPtr->context, hfsRebuildAttrBTree);
1001 // actually print nothing yet -- we print out when we are rebuilding the trees
1003 fsckPrint(GPtr
->context
, fsckRepairingVolume
);
1004 if (embedded
== 1 && debug
== 0)
1005 fsckPrint(GPtr
->context
, fsckLimitedRepairs
);
1007 result
= RepairVolume( GPtr
);
1011 case scavTerminate
: // CLEANUP AFTER SCAVENGE
1013 result
= ScavTerm(GPtr
);
1016 } // end ScavOp switch
1020 // Map internal error codes to scavenger result codes
1022 if ( (result
< 0) || (result
> Max_RCode
) )
1026 case scavInitialize
:
1028 if ( result
== ioErr
)
1030 else if ( result
== errRebuildBtree
)
1032 GPtr
->RepLevel
= repairLevelCatalogBtreeRebuild
;
1037 GPtr
->RepLevel
= repairLevelUnrepairable
;
1047 GPtr
->ScavRes
= result
;
1051 } // end of ScavCtrl
1055 /*------------------------------------------------------------------------------
1057 Function: CheckForStop
1059 Function: Checks for the user hitting the "STOP" button during a scavenge,
1060 which interrupts the operation. Additionally, we monitor the write
1061 count of a mounted volume, to be sure that the volume is not
1062 modified by another app while we scavenge.
1064 Input: GPtr - pointer to scavenger global area
1066 Output: Function result:
1068 R_UInt - STOP button hit
1069 R_Modified - another app has touched the volume
1070 -------------------------------------------------------------------------------*/
1072 short CheckForStop( SGlob
*GPtr
)
1074 OSErr err
= noErr
; // Initialize err to noErr
1075 long ticks
= TickCount();
1076 UInt16 dfaStage
= (UInt16
) GetDFAStage();
1078 //plog("%d, %d", dfaStage, kAboutToRepairStage);
1080 //if ( ((ticks - 10) > GPtr->lastTickCount) || (dfaStage == kAboutToRepairStage) ) // To reduce cursor flicker on fast machines, call through on a timed interval
1082 if ( GPtr
->userCancelProc
!= nil
)
1084 UInt64 progress
= 0;
1085 Boolean progressChanged
;
1086 // UInt16 elapsedTicks;
1088 if ( dfaStage
!= kRepairStage
)
1090 progress
= GPtr
->itemsProcessed
* 100;
1091 progress
/= GPtr
->itemsToProcess
;
1092 progressChanged
= ( progress
!= GPtr
->lastProgress
);
1093 GPtr
->lastProgress
= progress
;
1095 #if( DisplayTimeRemaining )
1096 if ( (progressChanged
) && (progress
> 5) )
1098 elapsedTicks
= TickCount() - GPtr
->startTicks
;
1099 GPtr
->secondsRemaining
= ( ( ( 100 * elapsedTicks
) / progress
) - elapsedTicks
) / 60;
1102 err
= CallUserCancelProc( GPtr
->userCancelProc
, (UInt16
)progress
, (UInt16
)GPtr
->secondsRemaining
, progressChanged
, dfaStage
, GPtr
->context
, GPtr
->scanCount
);
1106 (void) CallUserCancelProc( GPtr
->userCancelProc
, (UInt16
)progress
, 0, false, dfaStage
, GPtr
->context
, GPtr
->scanCount
);
1114 if ( GPtr
->realVCB
) // If the volume is mounted
1115 if ( GPtr
->realVCB
->vcbWrCnt
!= GPtr
->wrCnt
)
1116 err
= R_Modified
; // Its been modified behind our back
1118 GPtr
->lastTickCount
= ticks
;
1126 /*------------------------------------------------------------------------------
1128 Function: ScavSetUp - (Scavenger Set Up)
1130 Function: Sets up scavenger globals for a new scavenge operation. Memory is
1131 allocated for the Scavenger's static data structures (VCB, FCBs,
1132 BTCBs, and TPTs). The contents of the data structures are
1133 initialized to zero.
1135 Input: GPtr - pointer to scavenger global area
1137 Output: ScavSetUp - function result:
1140 ------------------------------------------------------------------------------*/
1142 struct ScavStaticStructures
{
1145 BTreeControlBlock btcb
[4]; // 4 btcb's
1146 SBTPT btreePath
; // scavenger BTree path table
1148 typedef struct ScavStaticStructures ScavStaticStructures
;
1151 static int ScavSetUp( SGlob
*GPtr
)
1160 GPtr
->MinorRepairsP
= nil
;
1162 GPtr
->itemsProcessed
= 0;
1163 GPtr
->lastProgress
= 0;
1164 GPtr
->startTicks
= TickCount();
1167 // allocate the static data structures (VCB, FCB's, BTCB'S, DPT and BTPT)
1170 ScavStaticStructures
*pointer
;
1172 pointer
= (ScavStaticStructures
*) AllocateClearMemory( sizeof(ScavStaticStructures
) );
1173 if ( pointer
== nil
) {
1174 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
1175 plog( "\t error %d - could not allocate %ld bytes of memory \n",
1176 R_NoMem
, sizeof(ScavStaticStructures
) );
1180 GPtr
->scavStaticPtr
= pointer
;
1182 GPtr
->DirPTPtr
= AllocateClearMemory(sizeof(SDPR
) * CMMaxDepth
);
1183 if ( GPtr
->DirPTPtr
== nil
) {
1184 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
1185 plog( "\t error %d - could not allocate %ld bytes of memory \n",
1186 R_NoMem
, sizeof(SDPR
) * CMMaxDepth
);
1190 GPtr
->dirPathCount
= CMMaxDepth
;
1192 GPtr
->calculatedVCB
= vcb
= &pointer
->vcb
;
1193 vcb
->vcbGPtr
= GPtr
;
1195 GPtr
->FCBAPtr
= (Ptr
) &pointer
->fcbList
;
1196 GPtr
->calculatedExtentsFCB
= &pointer
->fcbList
[0];
1197 GPtr
->calculatedCatalogFCB
= &pointer
->fcbList
[1];
1198 GPtr
->calculatedAllocationsFCB
= &pointer
->fcbList
[2];
1199 GPtr
->calculatedAttributesFCB
= &pointer
->fcbList
[3];
1200 GPtr
->calculatedStartupFCB
= &pointer
->fcbList
[4];
1201 GPtr
->calculatedRepairFCB
= &pointer
->fcbList
[5];
1203 GPtr
->calculatedExtentsBTCB
= &pointer
->btcb
[0];
1204 GPtr
->calculatedCatalogBTCB
= &pointer
->btcb
[1];
1205 GPtr
->calculatedRepairBTCB
= &pointer
->btcb
[2];
1206 GPtr
->calculatedAttributesBTCB
= &pointer
->btcb
[3];
1208 GPtr
->BTPTPtr
= (SBTPT
*) &pointer
->btreePath
;
1212 SetDFAStage( kVerifyStage
);
1213 SetFCBSPtr( GPtr
->FCBAPtr
);
1216 // locate the driveQ element for drive being scavenged
1218 GPtr
->DrvPtr
= 0; // <8> initialize so we can know if drive disappears
1221 // Set up Real structures
1224 err
= FindDrive( &ioRefNum
, &(GPtr
->DrvPtr
), GPtr
->DrvNum
);
1226 if ( IsBlueBoxSharedDrive( GPtr
->DrvPtr
) )
1229 err
= GetVolumeFeatures( GPtr
); // Sets up GPtr->volumeFeatures and GPtr->realVCB
1232 if ( GPtr
->DrvPtr
== NULL
) // <8> drive is no longer there!
1235 drvP
= GPtr
->DrvPtr
;
1237 // Save current value of vcbWrCnt, to detect modifications to volume by other apps etc
1238 if ( GPtr
->volumeFeatures
& volumeIsMountedMask
)
1240 FlushVol( nil
, GPtr
->realVCB
->vcbVRefNum
); // Ask HFS to update all changes to disk
1241 GPtr
->wrCnt
= GPtr
->realVCB
->vcbWrCnt
; // Remember write count after writing changes
1245 // Finish initializing the VCB
1247 // The calculated structures
1249 InitBlockCache(vcb
);
1250 vcb
->vcbDriveNumber
= GPtr
->DrvNum
;
1251 vcb
->vcbDriverReadRef
= GPtr
->DrvNum
;
1252 vcb
->vcbDriverWriteRef
= -1; /* XXX need to get real fd here */
1254 vcb
->vcbDriveNumber
= drvP
->dQDrive
;
1255 vcb
->vcbDriverReadRef
= drvP
->dQRefNum
;
1256 vcb
->vcbDriverWriteRef
= drvP
->dQRefNum
;
1257 vcb
->vcbFSID
= drvP
->dQFSID
;
1259 // vcb->vcbVRefNum = Vol_RefN;
1262 // finish initializing the FCB's
1267 // Create Calculated Extents FCB
1268 fcb
= GPtr
->calculatedExtentsFCB
;
1269 fcb
->fcbFileID
= kHFSExtentsFileID
;
1270 fcb
->fcbVolume
= vcb
;
1271 fcb
->fcbBtree
= GPtr
->calculatedExtentsBTCB
;
1272 vcb
->vcbExtentsFile
= fcb
;
1274 // Create Calculated Catalog FCB
1275 fcb
= GPtr
->calculatedCatalogFCB
;
1276 fcb
->fcbFileID
= kHFSCatalogFileID
;
1277 fcb
->fcbVolume
= vcb
;
1278 fcb
->fcbBtree
= GPtr
->calculatedCatalogBTCB
;
1279 vcb
->vcbCatalogFile
= fcb
;
1281 // Create Calculated Allocations FCB
1282 fcb
= GPtr
->calculatedAllocationsFCB
;
1283 fcb
->fcbFileID
= kHFSAllocationFileID
;
1284 fcb
->fcbVolume
= vcb
;
1285 fcb
->fcbBtree
= NULL
; // no BitMap B-Tree
1286 vcb
->vcbAllocationFile
= fcb
;
1288 // Create Calculated Attributes FCB
1289 fcb
= GPtr
->calculatedAttributesFCB
;
1290 fcb
->fcbFileID
= kHFSAttributesFileID
;
1291 fcb
->fcbVolume
= vcb
;
1292 fcb
->fcbBtree
= GPtr
->calculatedAttributesBTCB
;
1293 vcb
->vcbAttributesFile
= fcb
;
1295 /* Create Calculated Startup FCB */
1296 fcb
= GPtr
->calculatedStartupFCB
;
1297 fcb
->fcbFileID
= kHFSStartupFileID
;
1298 fcb
->fcbVolume
= vcb
;
1299 fcb
->fcbBtree
= NULL
;
1300 vcb
->vcbStartupFile
= fcb
;
1303 // finish initializing the BTCB's
1305 BTreeControlBlock
*btcb
;
1307 btcb
= GPtr
->calculatedExtentsBTCB
; // calculatedExtentsBTCB
1308 btcb
->fcbPtr
= GPtr
->calculatedExtentsFCB
;
1309 btcb
->getBlockProc
= GetFileBlock
;
1310 btcb
->releaseBlockProc
= ReleaseFileBlock
;
1311 btcb
->setEndOfForkProc
= SetEndOfForkProc
;
1313 btcb
= GPtr
->calculatedCatalogBTCB
; // calculatedCatalogBTCB
1314 btcb
->fcbPtr
= GPtr
->calculatedCatalogFCB
;
1315 btcb
->getBlockProc
= GetFileBlock
;
1316 btcb
->releaseBlockProc
= ReleaseFileBlock
;
1317 btcb
->setEndOfForkProc
= SetEndOfForkProc
;
1319 btcb
= GPtr
->calculatedAttributesBTCB
; // calculatedAttributesBTCB
1320 btcb
->fcbPtr
= GPtr
->calculatedAttributesFCB
;
1321 btcb
->getBlockProc
= GetFileBlock
;
1322 btcb
->releaseBlockProc
= ReleaseFileBlock
;
1323 btcb
->setEndOfForkProc
= SetEndOfForkProc
;
1328 // Initialize some global stuff
1331 GPtr
->RepLevel
= repairLevelNoProblemsFound
;
1333 GPtr
->IntErr
= noErr
;
1339 GPtr
->VeryMinorErrorsStat
= 0;
1342 /* Assume that the volume is dirty unmounted */
1343 GPtr
->cleanUnmount
= false;
1346 // Initialize VolumeObject
1349 InitializeVolumeObject( GPtr
);
1351 /* Check if the volume type of initialized object is valid. If not, return error */
1352 if (VolumeObjectIsValid() == false) {
1356 // Keep a valid file id list for HFS volumes
1357 GPtr
->validFilesList
= (UInt32
**)NewHandle( 0 );
1358 if ( GPtr
->validFilesList
== nil
) {
1359 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
1360 plog( "\t error %d - could not allocate file ID list \n", R_NoMem
);
1365 // Convert the security attribute name from utf8 to utf16. This will
1366 // avoid repeated conversion of all extended attributes to compare with
1367 // security attribute name
1368 (void) utf_decodestr((unsigned char *)KAUTH_FILESEC_XATTR
, strlen(KAUTH_FILESEC_XATTR
), GPtr
->securityAttrName
, &GPtr
->securityAttrLen
, sizeof(GPtr
->securityAttrName
));
1372 } /* end of ScavSetUp */
1377 /*------------------------------------------------------------------------------
1379 Function: ScavTerm - (Scavenge Termination))
1381 Function: Terminates the current scavenging operation. Memory for the
1382 VCB, FCBs, BTCBs, volume bit map, and BTree bit maps is
1385 Input: GPtr - pointer to scavenger global area
1387 Output: ScavTerm - function result:
1390 ------------------------------------------------------------------------------*/
1392 static int ScavTerm( SGlobPtr GPtr
)
1395 BTreeControlBlock
*btcbP
;
1398 ExtentsTable
**extentsTableH
;
1399 ExtentInfo
*curExtentInfo
;
1402 (void) BitMapCheckEnd();
1404 while( (rP
= GPtr
->MinorRepairsP
) != nil
) // loop freeing leftover (undone) repair orders
1406 GPtr
->MinorRepairsP
= rP
->link
; // (in case repairs were not made)
1411 if( GPtr
->validFilesList
!= nil
)
1412 DisposeHandle( (Handle
) GPtr
->validFilesList
);
1414 if( GPtr
->overlappedExtents
!= nil
) {
1415 extentsTableH
= GPtr
->overlappedExtents
;
1417 /* Overlapped extents list also allocated memory for attribute name */
1418 for (i
=0; i
<(**extentsTableH
).count
; i
++) {
1419 curExtentInfo
= &((**extentsTableH
).extentInfo
[i
]);
1421 /* Deallocate memory for attribute name, if any */
1422 if (curExtentInfo
->attrname
) {
1423 free(curExtentInfo
->attrname
);
1427 DisposeHandle( (Handle
) GPtr
->overlappedExtents
);
1430 if( GPtr
->fileIdentifierTable
!= nil
)
1431 DisposeHandle( (Handle
) GPtr
->fileIdentifierTable
);
1433 if( GPtr
->calculatedVCB
== nil
) // already freed?
1436 // If the FCB's and BTCB's have been set up, dispose of them
1437 fcbP
= GPtr
->calculatedExtentsFCB
; // release extent file BTree bit map
1440 btcbP
= (BTreeControlBlock
*)fcbP
->fcbBtree
;
1443 if( btcbP
->refCon
!= nil
)
1445 if(((BTreeExtensionsRec
*)btcbP
->refCon
)->BTCBMPtr
!= nil
)
1447 DisposeMemory(((BTreeExtensionsRec
*)btcbP
->refCon
)->BTCBMPtr
);
1450 DisposeMemory( (Ptr
)btcbP
->refCon
);
1452 btcbP
->refCon
= nil
;
1455 fcbP
= GPtr
->calculatedCatalogFCB
; // release catalog BTree bit map
1456 btcbP
= (BTreeControlBlock
*)fcbP
->fcbBtree
;
1458 if( btcbP
->refCon
!= nil
)
1460 if(((BTreeExtensionsRec
*)btcbP
->refCon
)->BTCBMPtr
!= nil
)
1462 DisposeMemory(((BTreeExtensionsRec
*)btcbP
->refCon
)->BTCBMPtr
);
1465 DisposeMemory( (Ptr
)btcbP
->refCon
);
1467 btcbP
->refCon
= nil
;
1472 DisposeMemory(GPtr
->DirPTPtr
);
1473 DisposeMemory((ScavStaticStructures
*)GPtr
->scavStaticPtr
);
1474 GPtr
->scavStaticPtr
= nil
;
1475 GPtr
->calculatedVCB
= nil
;
1480 #define BLUE_BOX_SHARED_DRVR_NAME "\p.BlueBoxShared"
1481 #define BLUE_BOX_FLOPPY_WHERE_STRING "\pdisk%d (Shared)"
1482 #define SONY_DRVR_NAME "\p.Sony"
1484 /*------------------------------------------------------------------------------
1486 Routine: IsBlueBoxSharedDrive
1488 Function: Given a DQE address, return a boolean that determines whether
1489 or not a drive is a Blue Box disk being accessed via Shared mode.
1490 Such drives do not support i/o and cannot be scavenged.
1492 Input: Arg 1 - DQE pointer
1494 Output: D0.L - 0 if drive not to be used
1496 ------------------------------------------------------------------------------*/
1498 struct IconAndStringRec
{
1502 typedef struct IconAndStringRec IconAndStringRec
, * IconAndStringRecPtr
;
1505 Boolean
IsBlueBoxSharedDrive ( DrvQElPtr dqPtr
)
1508 Str255 blueBoxSharedDriverName
= BLUE_BOX_SHARED_DRVR_NAME
;
1509 Str255 blueBoxFloppyWhereString
= BLUE_BOX_FLOPPY_WHERE_STRING
;
1510 Str255 sonyDriverName
= SONY_DRVR_NAME
;
1511 DCtlHandle driverDCtlHandle
;
1512 DCtlPtr driverDCtlPtr
;
1513 DRVRHeaderPtr drvrHeaderPtr
;
1514 StringPtr driverName
;
1516 if ( dqPtr
== NULL
)
1519 // Now look at the name of the Driver name. If it is .BlueBoxShared keep it out of the list of available disks.
1520 driverDCtlHandle
= GetDCtlEntry(dqPtr
->dQRefNum
);
1521 driverDCtlPtr
= *driverDCtlHandle
;
1522 if((((driverDCtlPtr
->dCtlFlags
) & Is_Native_Mask
) == 0) && (driverDCtlPtr
->dCtlDriver
!= nil
))
1524 if (((driverDCtlPtr
->dCtlFlags
) & Is_Ram_Based_Mask
) == 0)
1526 drvrHeaderPtr
= (DRVRHeaderPtr
)driverDCtlPtr
->dCtlDriver
;
1530 //¥¥¥ bek - lock w/o unlock/restore? should be getstate/setstate?
1531 HLock((Handle
)(driverDCtlPtr
)->dCtlDriver
);
1532 drvrHeaderPtr
= (DRVRHeaderPtr
)*((Handle
)(driverDCtlPtr
->dCtlDriver
));
1535 driverName
= (StringPtr
)&(drvrHeaderPtr
->drvrName
);
1536 if (!(IdenticalString(driverName
,blueBoxSharedDriverName
,nil
)))
1541 // Special case for the ".Sony" floppy driver which might be accessed in Shared mode inside the Blue Box
1542 // Test its "where" string instead of the driver name.
1543 if (!(IdenticalString(driverName
,sonyDriverName
,nil
)))
1545 CntrlParam paramBlock
;
1547 paramBlock
.ioCompletion
= nil
;
1548 paramBlock
.ioNamePtr
= nil
;
1549 paramBlock
.ioVRefNum
= dqPtr
->dQDrive
;
1550 paramBlock
.ioCRefNum
= dqPtr
->dQRefNum
;
1551 paramBlock
.csCode
= kDriveIcon
; // return physical icon
1553 // If PBControl(kDriveIcon) returns an error then the driver is not the Blue Box driver.
1554 if ( noErr
== PBControlSync( (ParmBlkPtr
) ¶mBlock
) )
1556 IconAndStringRecPtr iconAndStringRecPtr
;
1557 StringPtr whereStringPtr
;
1559 iconAndStringRecPtr
= * (IconAndStringRecPtr
*) & paramBlock
.csParam
;
1560 whereStringPtr
= (StringPtr
) & iconAndStringRecPtr
->string
;
1561 if (!(IdenticalString(whereStringPtr
,blueBoxFloppyWhereString
,nil
)))
1576 /*------------------------------------------------------------------------------
1578 Function: printVerifyStatus - (Print Verify Status)
1580 Function: Prints out the Verify Status words.
1582 Input: GPtr - pointer to scavenger global area
1585 ------------------------------------------------------------------------------*/
1587 void printVerifyStatus(SGlobPtr GPtr
)
1591 stat
= GPtr
->VIStat
| GPtr
->ABTStat
| GPtr
->EBTStat
| GPtr
->CBTStat
| GPtr
->CatStat
;
1594 plog(" Verify Status: VIStat = 0x%04x, ABTStat = 0x%04x EBTStat = 0x%04x\n",
1595 GPtr
->VIStat
, GPtr
->ABTStat
, GPtr
->EBTStat
);
1596 plog(" CBTStat = 0x%04x CatStat = 0x%08x\n",
1597 GPtr
->CBTStat
, GPtr
->CatStat
);