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
) )
498 // If we have write access on volume and we are allowed to write,
499 // mark the volume clean/dirty
500 if ((fsWriteRef
!= -1) && (dataArea
.canWrite
!= 0)) {
504 CheckForClean(&dataArea
, kMarkVolumeDirty
, &update
);
507 CheckForClean(&dataArea
, kMarkVolumeClean
, &update
);
510 /* Report back that volume was modified */
514 ScavCtrl( &dataArea
, scavTerminate
, &temp
); // Note: use a temp var so that real scav error can be returned
517 fsckPrint( fsckContext
, fsckProgress
, 100); // End each run with 100% message, if desired
521 if (exitEarly
&& majorErrors
)
532 /*------------------------------------------------------------------------------
534 Function: ScavCtrl - (Scavenger Control)
536 Function: Controls the scavenging process. Interfaces with the User Interface
537 Layer (written in PASCAL).
539 Input: ScavOp - scavenging operation to be performed:
541 scavInitialize = start initial volume check
542 scavVerify = start verify
543 scavRepair = start repair
544 scavTerminate = finished scavenge
546 GPtr - pointer to scavenger global area
549 Output: ScavRes - scavenge result code (R_xxx, or 0 if no error)
551 ------------------------------------------------------------------------------*/
553 void ScavCtrl( SGlobPtr GPtr
, UInt32 ScavOp
, short *ScavRes
)
557 #if SHOW_ELAPSED_TIMES
558 struct timeval myStartTime
;
559 struct timeval myEndTime
;
560 struct timeval myElapsedTime
;
561 struct timezone zone
;
565 // initialize some stuff
567 result
= noErr
; // assume good status
572 // dispatch next scavenge operation
576 case scavInitialize
: // INITIAL VOLUME CHECK
581 if ( ( result
= ScavSetUp( GPtr
) ) ) // set up BEFORE CheckForStop
583 if ( IsBlueBoxSharedDrive( GPtr
->DrvPtr
) )
585 if ( ( result
= CheckForStop( GPtr
) ) ) // in order to initialize wrCnt
588 /* Call for all chkLevel options and check return value only
589 * for kDirtyCheck for preen option and kNeverCheck for quick option
591 clean
= CheckForClean(GPtr
, kCheckVolume
, &modified
);
592 if ((GPtr
->chkLevel
== kDirtyCheck
) || (GPtr
->chkLevel
== kNeverCheck
)) {
594 /* volume was unmounted cleanly */
595 GPtr
->cleanUnmount
= true;
599 if (GPtr
->chkLevel
== kNeverCheck
) {
602 else if (clean
== 0) {
604 * We lie for journaled file systems since
605 * they get cleaned up in mount by replaying
607 * Note: CheckIfJournaled will return negative
608 * if it finds lastMountedVersion = FSK!.
610 if (CheckIfJournaled(GPtr
, false))
611 GPtr
->cleanUnmount
= true;
619 if (CheckIfJournaled(GPtr
, false)
620 && GPtr
->chkLevel
!= kForceCheck
621 && !(GPtr
->chkLevel
== kPartialCheck
&& GPtr
->repairLevel
== kForceRepairs
)
622 && !(GPtr
->chkLevel
== kAlwaysCheck
&& GPtr
->repairLevel
== kMajorRepairs
)) {
626 if (GPtr
->liveVerifyState
) {
627 fsckPrint(GPtr
->context
, hfsLiveVerifyCheck
);
628 } else if (GPtr
->canWrite
== 0 && nflag
== 0) {
629 fsckPrint(GPtr
->context
, hfsVerifyVolWithWrite
);
633 * In the first pass, if fsck_hfs is verifying a
634 * journaled volume, and it's not a live verification,
635 * check to see if the journal is empty. If it is not,
636 * flag it as a journal error, and print a message.
637 * (A live verify will almost certainly have a non-empty
638 * journal, but that should be safe in this case due
639 * to the freeze command flushing everything.)
641 if ((GPtr
->scanCount
== 0) &&
642 (CheckIfJournaled(GPtr
, true) == 1) &&
643 (GPtr
->canWrite
== 0 || GPtr
->writeRef
== -1) &&
645 fsckJournalInfo_t jnlInfo
= { 0 };
650 if (IsJournalEmpty(GPtr
, &jnlInfo
) == 0) {
651 // disable_journal can currently only be set with debug enabled
652 if (disable_journal
) {
653 fsckPrint(GPtr
->context
, E_DirtyJournal
);
654 GPtr
->JStat
|= S_DirtyJournal
;
656 (void)GetDeviceSize(GPtr
->calculatedVCB
->vcbDriveNumber
, &numBlocks
, &blockSize
);
658 // For debugging the cache. WAY to verbose to run with even normal debug
660 printf("Before journal replay\n");
664 if (journal_open(jnlInfo
.jnlfd
,
670 ^(off_t start
, void *data
, size_t len
) {
673 rv
= CacheRead(&fscache
, start
, (int)len
, &buf
);
676 memcpy(buf
->Buffer
, data
, len
);
677 rv
= CacheWrite(&fscache
, buf
, 0, kLockWrite
);
682 fsckPrint(GPtr
->context
, E_DirtyJournal
);
683 GPtr
->JStat
|= S_DirtyJournal
;
685 plog("Journal replay simulation succeeded\n");
687 // Still way too verbose to run
694 plog("Journal is empty\n");
696 if (jnlInfo
.jnlfd
!= -1)
697 close(jnlInfo
.jnlfd
);
698 if (jnlInfo
.name
!= NULL
)
702 result
= IVChk( GPtr
);
707 case scavVerify
: // VERIFY
710 #if SHOW_ELAPSED_TIMES
711 gettimeofday( &myStartTime
, &zone
);
714 /* Initialize volume bitmap structure */
715 if ( BitMapCheckBegin(GPtr
) != 0)
718 #if SHOW_ELAPSED_TIMES
719 gettimeofday( &myEndTime
, &zone
);
720 timersub( &myEndTime
, &myStartTime
, &myElapsedTime
);
721 plog( "\n%s - BitMapCheck elapsed time \n", __FUNCTION__
);
722 plog( "########## secs %d msecs %d \n\n",
723 myElapsedTime
.tv_sec
, myElapsedTime
.tv_usec
);
726 if ( IsBlueBoxSharedDrive( GPtr
->DrvPtr
) )
728 if ( ( result
= CheckForStop( GPtr
) ) )
731 #if SHOW_ELAPSED_TIMES
732 gettimeofday( &myStartTime
, &zone
);
735 /* Create calculated BTree structures */
736 if ( ( result
= CreateExtentsBTreeControlBlock( GPtr
) ) )
738 if ( ( result
= CreateCatalogBTreeControlBlock( GPtr
) ) )
740 if ( ( result
= CreateAttributesBTreeControlBlock( GPtr
) ) )
742 if ( ( result
= CreateExtendedAllocationsFCB( GPtr
) ) )
745 #if SHOW_ELAPSED_TIMES
746 gettimeofday( &myEndTime
, &zone
);
747 timersub( &myEndTime
, &myStartTime
, &myElapsedTime
);
748 plog( "\n%s - create control blocks elapsed time \n", __FUNCTION__
);
749 plog( ">>>>>>>>>>>>> secs %d msecs %d \n\n",
750 myElapsedTime
.tv_sec
, myElapsedTime
.tv_usec
);
753 // Now that preflight of the BTree structures is calculated, compute the CheckDisk items
754 CalculateItemCount( GPtr
, &GPtr
->itemsToProcess
, &GPtr
->onePercent
);
755 GPtr
->itemsProcessed
+= GPtr
->onePercent
; // We do this 4 times as set up in CalculateItemCount() to smooth the scroll
757 if ( ( result
= VLockedChk( GPtr
) ) )
760 GPtr
->itemsProcessed
+= GPtr
->onePercent
; // We do this 4 times as set up in CalculateItemCount() to smooth the scroll
761 fsckPrint(GPtr
->context
, hfsExtBTCheck
);
763 #if SHOW_ELAPSED_TIMES
764 gettimeofday( &myStartTime
, &zone
);
767 /* Verify extent btree structure */
768 if ((result
= ExtBTChk(GPtr
)))
771 #if SHOW_ELAPSED_TIMES
772 gettimeofday( &myEndTime
, &zone
);
773 timersub( &myEndTime
, &myStartTime
, &myElapsedTime
);
774 plog( "\n%s - ExtBTChk elapsed time \n", __FUNCTION__
);
775 plog( ">>>>>>>>>>>>> secs %d msecs %d \n\n",
776 myElapsedTime
.tv_sec
, myElapsedTime
.tv_usec
);
779 if ((result
= CheckForStop(GPtr
)))
782 GPtr
->itemsProcessed
+= GPtr
->onePercent
; // We do this 4 times as set up in CalculateItemCount() to smooth the scroll
784 /* Check extents of bad block file */
785 if ((result
= BadBlockFileExtentCheck(GPtr
)))
787 if ((result
= CheckForStop(GPtr
)))
790 GPtr
->itemsProcessed
+= GPtr
->onePercent
; // We do this 4 times as set up in CalculateItemCount() to smooth the scroll
791 GPtr
->itemsProcessed
+= GPtr
->onePercent
;
792 fsckPrint(GPtr
->context
, hfsCatBTCheck
);
794 #if SHOW_ELAPSED_TIMES
795 gettimeofday( &myStartTime
, &zone
);
798 if ( GPtr
->chkLevel
== kPartialCheck
)
800 /* skip the rest of the verify code path the first time */
801 /* through when we are rebuilding the catalog B-Tree file. */
802 /* we will be back here after the rebuild. */
803 if (GPtr
->rebuildOptions
& REBUILD_CATALOG
) {
804 GPtr
->CBTStat
|= S_RebuildBTree
;
806 if (GPtr
->rebuildOptions
& REBUILD_EXTENTS
) {
807 GPtr
->EBTStat
|= S_RebuildBTree
;
809 if (GPtr
->rebuildOptions
& REBUILD_ATTRIBUTE
) {
810 GPtr
->ABTStat
|= S_RebuildBTree
;
812 result
= errRebuildBtree
;
816 /* Check catalog btree. For given fileID, the function accounts
817 * for all extents existing in catalog record as well as in
818 * overflow extent btree
820 if ((result
= CheckCatalogBTree(GPtr
)))
823 #if SHOW_ELAPSED_TIMES
824 gettimeofday( &myEndTime
, &zone
);
825 timersub( &myEndTime
, &myStartTime
, &myElapsedTime
);
826 plog( "\n%s - CheckCatalogBTree elapsed time \n", __FUNCTION__
);
827 plog( ">>>>>>>>>>>>> secs %d msecs %d \n\n",
828 myElapsedTime
.tv_sec
, myElapsedTime
.tv_usec
);
831 if ((result
= CheckForStop(GPtr
)))
835 fsckPrint(GPtr
->context
, hfsCatHierCheck
);
837 #if SHOW_ELAPSED_TIMES
838 gettimeofday( &myStartTime
, &zone
);
841 /* Check catalog hierarchy */
842 if ((result
= CatHChk(GPtr
)))
845 #if SHOW_ELAPSED_TIMES
846 gettimeofday( &myEndTime
, &zone
);
847 timersub( &myEndTime
, &myStartTime
, &myElapsedTime
);
848 plog( "\n%s - CatHChk elapsed time \n", __FUNCTION__
);
849 plog( ">>>>>>>>>>>>> secs %d msecs %d \n\n",
850 myElapsedTime
.tv_sec
, myElapsedTime
.tv_usec
);
853 if ((result
= CheckForStop(GPtr
)))
856 if (VolumeObjectIsHFSX(GPtr
)) {
857 result
= CheckFolderCount(GPtr
);
861 if ((result
=CheckForStop(GPtr
)))
865 /* Check attribute btree. The function accounts for all extents
866 * for extended attributes whose values are stored in
869 if ((result
= AttrBTChk(GPtr
)))
872 if ((result
= CheckForStop(GPtr
)))
876 * fsck_hfs has accounted for all valid allocation blocks by
877 * traversing all catalog records and attribute records.
878 * These traversals may have found overlapping extents. Note
879 * that the overlapping extents are detected in CaptureBitmapBits
880 * when it tries to set a bit corresponding to allocation block
881 * and finds that it is already set. Therefore fsck_hfs does not
882 * know the orignal file involved overlapped extents.
884 if (GPtr
->VIStat
& S_OverlappingExtents
) {
885 /* Find original files involved in overlapped extents */
886 result
= FindOrigOverlapFiles(GPtr
);
891 /* Print all unique overlapping file IDs and paths */
892 (void) PrintOverlapFiles(GPtr
);
896 /* Directory inodes store first link information in
897 * an extended attribute. Therefore start directory
898 * hard link check after extended attribute checks.
900 result
= dirhardlink_check(GPtr
);
901 /* On error or unrepairable corruption, stop the verification */
902 if ((result
!= 0) || (GPtr
->CatStat
& S_LinkErrNoRepair
)) {
911 fsckPrint(GPtr
->context
, hfsVolBitmapCheck
);
913 #if SHOW_ELAPSED_TIMES
914 gettimeofday( &myStartTime
, &zone
);
917 /* Compare in-memory volume bitmap with on-disk bitmap */
918 if ((result
= CheckVolumeBitMap(GPtr
, false)))
921 #if SHOW_ELAPSED_TIMES
922 gettimeofday( &myEndTime
, &zone
);
923 timersub( &myEndTime
, &myStartTime
, &myElapsedTime
);
924 plog( "\n%s - CheckVolumeBitMap elapsed time \n", __FUNCTION__
);
925 plog( ">>>>>>>>>>>>> secs %d msecs %d \n\n",
926 myElapsedTime
.tv_sec
, myElapsedTime
.tv_usec
);
929 if ((result
= CheckForStop(GPtr
)))
932 fsckPrint(GPtr
->context
, hfsVolInfoCheck
);
934 #if SHOW_ELAPSED_TIMES
935 gettimeofday( &myStartTime
, &zone
);
938 /* Verify volume level information */
939 if ((result
= VInfoChk(GPtr
)))
942 #if SHOW_ELAPSED_TIMES
943 gettimeofday( &myEndTime
, &zone
);
944 timersub( &myEndTime
, &myStartTime
, &myElapsedTime
);
945 plog( "\n%s - VInfoChk elapsed time \n", __FUNCTION__
);
946 plog( ">>>>>>>>>>>>> secs %d msecs %d \n\n",
947 myElapsedTime
.tv_sec
, myElapsedTime
.tv_usec
);
950 stat
= GPtr
->VIStat
| GPtr
->ABTStat
| GPtr
->EBTStat
| GPtr
->CBTStat
|
951 GPtr
->CatStat
| GPtr
->JStat
;
955 if ( (GPtr
->RepLevel
== repairLevelNoProblemsFound
) || (GPtr
->RepLevel
== repairLevelVolumeRecoverable
) )
957 // 2200106, We isolate very minor errors so that if the volume cannot be unmounted
958 // CheckDisk will just return noErr
959 unsigned int minorErrors
= (GPtr
->CatStat
& ~S_LockedDirName
) |
960 GPtr
->VIStat
| GPtr
->ABTStat
| GPtr
->EBTStat
| GPtr
->CBTStat
| GPtr
->JStat
;
961 if ( minorErrors
== 0 )
962 GPtr
->RepLevel
= repairLevelVeryMinorErrors
;
964 GPtr
->RepLevel
= repairLevelVolumeRecoverable
;
967 else if ( GPtr
->RepLevel
== repairLevelNoProblemsFound
)
971 GPtr
->itemsProcessed
= GPtr
->itemsToProcess
;
972 result
= CheckForStop(GPtr
); // one last check for modified volume
976 case scavRepair
: // REPAIR
978 if ( IsBlueBoxSharedDrive( GPtr
->DrvPtr
) )
980 if ( ( result
= CheckForStop(GPtr
) ) )
982 if ( GPtr
->CBTStat
& S_RebuildBTree
983 || GPtr
->EBTStat
& S_RebuildBTree
984 || GPtr
->ABTStat
& S_RebuildBTree
) {
985 // fsckPrint(GPtr->context, hfsRebuildCatalogBTree);
986 // fsckPrint(GPtr->context, hfsRebuildAttrBTree);
987 // actually print nothing yet -- we print out when we are rebuilding the trees
989 fsckPrint(GPtr
->context
, fsckRepairingVolume
);
990 if (embedded
== 1 && debug
== 0)
991 fsckPrint(GPtr
->context
, fsckLimitedRepairs
);
993 result
= RepairVolume( GPtr
);
997 case scavTerminate
: // CLEANUP AFTER SCAVENGE
999 result
= ScavTerm(GPtr
);
1002 } // end ScavOp switch
1006 // Map internal error codes to scavenger result codes
1008 if ( (result
< 0) || (result
> Max_RCode
) )
1012 case scavInitialize
:
1014 if ( result
== ioErr
)
1016 else if ( result
== errRebuildBtree
)
1018 GPtr
->RepLevel
= repairLevelCatalogBtreeRebuild
;
1023 GPtr
->RepLevel
= repairLevelUnrepairable
;
1033 GPtr
->ScavRes
= result
;
1037 } // end of ScavCtrl
1041 /*------------------------------------------------------------------------------
1043 Function: CheckForStop
1045 Function: Checks for the user hitting the "STOP" button during a scavenge,
1046 which interrupts the operation. Additionally, we monitor the write
1047 count of a mounted volume, to be sure that the volume is not
1048 modified by another app while we scavenge.
1050 Input: GPtr - pointer to scavenger global area
1052 Output: Function result:
1054 R_UInt - STOP button hit
1055 R_Modified - another app has touched the volume
1056 -------------------------------------------------------------------------------*/
1058 short CheckForStop( SGlob
*GPtr
)
1060 OSErr err
= noErr
; // Initialize err to noErr
1061 long ticks
= TickCount();
1062 UInt16 dfaStage
= (UInt16
) GetDFAStage();
1064 //plog("%d, %d", dfaStage, kAboutToRepairStage);
1066 //if ( ((ticks - 10) > GPtr->lastTickCount) || (dfaStage == kAboutToRepairStage) ) // To reduce cursor flicker on fast machines, call through on a timed interval
1068 if ( GPtr
->userCancelProc
!= nil
)
1070 UInt64 progress
= 0;
1071 Boolean progressChanged
;
1072 // UInt16 elapsedTicks;
1074 if ( dfaStage
!= kRepairStage
)
1076 progress
= GPtr
->itemsProcessed
* 100;
1077 progress
/= GPtr
->itemsToProcess
;
1078 progressChanged
= ( progress
!= GPtr
->lastProgress
);
1079 GPtr
->lastProgress
= progress
;
1081 #if( DisplayTimeRemaining )
1082 if ( (progressChanged
) && (progress
> 5) )
1084 elapsedTicks
= TickCount() - GPtr
->startTicks
;
1085 GPtr
->secondsRemaining
= ( ( ( 100 * elapsedTicks
) / progress
) - elapsedTicks
) / 60;
1088 err
= CallUserCancelProc( GPtr
->userCancelProc
, (UInt16
)progress
, (UInt16
)GPtr
->secondsRemaining
, progressChanged
, dfaStage
, GPtr
->context
, GPtr
->scanCount
);
1092 (void) CallUserCancelProc( GPtr
->userCancelProc
, (UInt16
)progress
, 0, false, dfaStage
, GPtr
->context
, GPtr
->scanCount
);
1100 if ( GPtr
->realVCB
) // If the volume is mounted
1101 if ( GPtr
->realVCB
->vcbWrCnt
!= GPtr
->wrCnt
)
1102 err
= R_Modified
; // Its been modified behind our back
1104 GPtr
->lastTickCount
= ticks
;
1112 /*------------------------------------------------------------------------------
1114 Function: ScavSetUp - (Scavenger Set Up)
1116 Function: Sets up scavenger globals for a new scavenge operation. Memory is
1117 allocated for the Scavenger's static data structures (VCB, FCBs,
1118 BTCBs, and TPTs). The contents of the data structures are
1119 initialized to zero.
1121 Input: GPtr - pointer to scavenger global area
1123 Output: ScavSetUp - function result:
1126 ------------------------------------------------------------------------------*/
1128 struct ScavStaticStructures
{
1131 BTreeControlBlock btcb
[4]; // 4 btcb's
1132 SBTPT btreePath
; // scavenger BTree path table
1134 typedef struct ScavStaticStructures ScavStaticStructures
;
1137 static int ScavSetUp( SGlob
*GPtr
)
1146 GPtr
->MinorRepairsP
= nil
;
1148 GPtr
->itemsProcessed
= 0;
1149 GPtr
->lastProgress
= 0;
1150 GPtr
->startTicks
= TickCount();
1153 // allocate the static data structures (VCB, FCB's, BTCB'S, DPT and BTPT)
1156 ScavStaticStructures
*pointer
;
1158 pointer
= (ScavStaticStructures
*) AllocateClearMemory( sizeof(ScavStaticStructures
) );
1159 if ( pointer
== nil
) {
1160 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
1161 plog( "\t error %d - could not allocate %ld bytes of memory \n",
1162 R_NoMem
, sizeof(ScavStaticStructures
) );
1166 GPtr
->scavStaticPtr
= pointer
;
1168 GPtr
->DirPTPtr
= AllocateClearMemory(sizeof(SDPR
) * CMMaxDepth
);
1169 if ( GPtr
->DirPTPtr
== nil
) {
1170 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
1171 plog( "\t error %d - could not allocate %ld bytes of memory \n",
1172 R_NoMem
, sizeof(SDPR
) * CMMaxDepth
);
1176 GPtr
->dirPathCount
= CMMaxDepth
;
1178 GPtr
->calculatedVCB
= vcb
= &pointer
->vcb
;
1179 vcb
->vcbGPtr
= GPtr
;
1181 GPtr
->FCBAPtr
= (Ptr
) &pointer
->fcbList
;
1182 GPtr
->calculatedExtentsFCB
= &pointer
->fcbList
[0];
1183 GPtr
->calculatedCatalogFCB
= &pointer
->fcbList
[1];
1184 GPtr
->calculatedAllocationsFCB
= &pointer
->fcbList
[2];
1185 GPtr
->calculatedAttributesFCB
= &pointer
->fcbList
[3];
1186 GPtr
->calculatedStartupFCB
= &pointer
->fcbList
[4];
1187 GPtr
->calculatedRepairFCB
= &pointer
->fcbList
[5];
1189 GPtr
->calculatedExtentsBTCB
= &pointer
->btcb
[0];
1190 GPtr
->calculatedCatalogBTCB
= &pointer
->btcb
[1];
1191 GPtr
->calculatedRepairBTCB
= &pointer
->btcb
[2];
1192 GPtr
->calculatedAttributesBTCB
= &pointer
->btcb
[3];
1194 GPtr
->BTPTPtr
= (SBTPT
*) &pointer
->btreePath
;
1198 SetDFAStage( kVerifyStage
);
1199 SetFCBSPtr( GPtr
->FCBAPtr
);
1202 // locate the driveQ element for drive being scavenged
1204 GPtr
->DrvPtr
= 0; // <8> initialize so we can know if drive disappears
1207 // Set up Real structures
1210 err
= FindDrive( &ioRefNum
, &(GPtr
->DrvPtr
), GPtr
->DrvNum
);
1212 if ( IsBlueBoxSharedDrive( GPtr
->DrvPtr
) )
1215 err
= GetVolumeFeatures( GPtr
); // Sets up GPtr->volumeFeatures and GPtr->realVCB
1218 if ( GPtr
->DrvPtr
== NULL
) // <8> drive is no longer there!
1221 drvP
= GPtr
->DrvPtr
;
1223 // Save current value of vcbWrCnt, to detect modifications to volume by other apps etc
1224 if ( GPtr
->volumeFeatures
& volumeIsMountedMask
)
1226 FlushVol( nil
, GPtr
->realVCB
->vcbVRefNum
); // Ask HFS to update all changes to disk
1227 GPtr
->wrCnt
= GPtr
->realVCB
->vcbWrCnt
; // Remember write count after writing changes
1231 // Finish initializing the VCB
1233 // The calculated structures
1235 InitBlockCache(vcb
);
1236 vcb
->vcbDriveNumber
= GPtr
->DrvNum
;
1237 vcb
->vcbDriverReadRef
= GPtr
->DrvNum
;
1238 vcb
->vcbDriverWriteRef
= -1; /* XXX need to get real fd here */
1240 vcb
->vcbDriveNumber
= drvP
->dQDrive
;
1241 vcb
->vcbDriverReadRef
= drvP
->dQRefNum
;
1242 vcb
->vcbDriverWriteRef
= drvP
->dQRefNum
;
1243 vcb
->vcbFSID
= drvP
->dQFSID
;
1245 // vcb->vcbVRefNum = Vol_RefN;
1248 // finish initializing the FCB's
1253 // Create Calculated Extents FCB
1254 fcb
= GPtr
->calculatedExtentsFCB
;
1255 fcb
->fcbFileID
= kHFSExtentsFileID
;
1256 fcb
->fcbVolume
= vcb
;
1257 fcb
->fcbBtree
= GPtr
->calculatedExtentsBTCB
;
1258 vcb
->vcbExtentsFile
= fcb
;
1260 // Create Calculated Catalog FCB
1261 fcb
= GPtr
->calculatedCatalogFCB
;
1262 fcb
->fcbFileID
= kHFSCatalogFileID
;
1263 fcb
->fcbVolume
= vcb
;
1264 fcb
->fcbBtree
= GPtr
->calculatedCatalogBTCB
;
1265 vcb
->vcbCatalogFile
= fcb
;
1267 // Create Calculated Allocations FCB
1268 fcb
= GPtr
->calculatedAllocationsFCB
;
1269 fcb
->fcbFileID
= kHFSAllocationFileID
;
1270 fcb
->fcbVolume
= vcb
;
1271 fcb
->fcbBtree
= NULL
; // no BitMap B-Tree
1272 vcb
->vcbAllocationFile
= fcb
;
1274 // Create Calculated Attributes FCB
1275 fcb
= GPtr
->calculatedAttributesFCB
;
1276 fcb
->fcbFileID
= kHFSAttributesFileID
;
1277 fcb
->fcbVolume
= vcb
;
1278 fcb
->fcbBtree
= GPtr
->calculatedAttributesBTCB
;
1279 vcb
->vcbAttributesFile
= fcb
;
1281 /* Create Calculated Startup FCB */
1282 fcb
= GPtr
->calculatedStartupFCB
;
1283 fcb
->fcbFileID
= kHFSStartupFileID
;
1284 fcb
->fcbVolume
= vcb
;
1285 fcb
->fcbBtree
= NULL
;
1286 vcb
->vcbStartupFile
= fcb
;
1289 // finish initializing the BTCB's
1291 BTreeControlBlock
*btcb
;
1293 btcb
= GPtr
->calculatedExtentsBTCB
; // calculatedExtentsBTCB
1294 btcb
->fcbPtr
= GPtr
->calculatedExtentsFCB
;
1295 btcb
->getBlockProc
= GetFileBlock
;
1296 btcb
->releaseBlockProc
= ReleaseFileBlock
;
1297 btcb
->setEndOfForkProc
= SetEndOfForkProc
;
1299 btcb
= GPtr
->calculatedCatalogBTCB
; // calculatedCatalogBTCB
1300 btcb
->fcbPtr
= GPtr
->calculatedCatalogFCB
;
1301 btcb
->getBlockProc
= GetFileBlock
;
1302 btcb
->releaseBlockProc
= ReleaseFileBlock
;
1303 btcb
->setEndOfForkProc
= SetEndOfForkProc
;
1305 btcb
= GPtr
->calculatedAttributesBTCB
; // calculatedAttributesBTCB
1306 btcb
->fcbPtr
= GPtr
->calculatedAttributesFCB
;
1307 btcb
->getBlockProc
= GetFileBlock
;
1308 btcb
->releaseBlockProc
= ReleaseFileBlock
;
1309 btcb
->setEndOfForkProc
= SetEndOfForkProc
;
1314 // Initialize some global stuff
1317 GPtr
->RepLevel
= repairLevelNoProblemsFound
;
1319 GPtr
->IntErr
= noErr
;
1325 GPtr
->VeryMinorErrorsStat
= 0;
1328 /* Assume that the volume is dirty unmounted */
1329 GPtr
->cleanUnmount
= false;
1332 // Initialize VolumeObject
1335 InitializeVolumeObject( GPtr
);
1337 /* Check if the volume type of initialized object is valid. If not, return error */
1338 if (VolumeObjectIsValid() == false) {
1342 // Keep a valid file id list for HFS volumes
1343 GPtr
->validFilesList
= (UInt32
**)NewHandle( 0 );
1344 if ( GPtr
->validFilesList
== nil
) {
1345 if ( fsckGetVerbosity(GPtr
->context
) >= kDebugLog
) {
1346 plog( "\t error %d - could not allocate file ID list \n", R_NoMem
);
1351 // Convert the security attribute name from utf8 to utf16. This will
1352 // avoid repeated conversion of all extended attributes to compare with
1353 // security attribute name
1354 (void) utf_decodestr((unsigned char *)KAUTH_FILESEC_XATTR
, strlen(KAUTH_FILESEC_XATTR
), GPtr
->securityAttrName
, &GPtr
->securityAttrLen
, sizeof(GPtr
->securityAttrName
));
1358 } /* end of ScavSetUp */
1363 /*------------------------------------------------------------------------------
1365 Function: ScavTerm - (Scavenge Termination))
1367 Function: Terminates the current scavenging operation. Memory for the
1368 VCB, FCBs, BTCBs, volume bit map, and BTree bit maps is
1371 Input: GPtr - pointer to scavenger global area
1373 Output: ScavTerm - function result:
1376 ------------------------------------------------------------------------------*/
1378 static int ScavTerm( SGlobPtr GPtr
)
1381 BTreeControlBlock
*btcbP
;
1384 ExtentsTable
**extentsTableH
;
1385 ExtentInfo
*curExtentInfo
;
1388 (void) BitMapCheckEnd();
1390 while( (rP
= GPtr
->MinorRepairsP
) != nil
) // loop freeing leftover (undone) repair orders
1392 GPtr
->MinorRepairsP
= rP
->link
; // (in case repairs were not made)
1397 if( GPtr
->validFilesList
!= nil
)
1398 DisposeHandle( (Handle
) GPtr
->validFilesList
);
1400 if( GPtr
->overlappedExtents
!= nil
) {
1401 extentsTableH
= GPtr
->overlappedExtents
;
1403 /* Overlapped extents list also allocated memory for attribute name */
1404 for (i
=0; i
<(**extentsTableH
).count
; i
++) {
1405 curExtentInfo
= &((**extentsTableH
).extentInfo
[i
]);
1407 /* Deallocate memory for attribute name, if any */
1408 if (curExtentInfo
->attrname
) {
1409 free(curExtentInfo
->attrname
);
1413 DisposeHandle( (Handle
) GPtr
->overlappedExtents
);
1416 if( GPtr
->fileIdentifierTable
!= nil
)
1417 DisposeHandle( (Handle
) GPtr
->fileIdentifierTable
);
1419 if( GPtr
->calculatedVCB
== nil
) // already freed?
1422 // If the FCB's and BTCB's have been set up, dispose of them
1423 fcbP
= GPtr
->calculatedExtentsFCB
; // release extent file BTree bit map
1426 btcbP
= (BTreeControlBlock
*)fcbP
->fcbBtree
;
1429 if( btcbP
->refCon
!= nil
)
1431 if(((BTreeExtensionsRec
*)btcbP
->refCon
)->BTCBMPtr
!= nil
)
1433 DisposeMemory(((BTreeExtensionsRec
*)btcbP
->refCon
)->BTCBMPtr
);
1436 DisposeMemory( (Ptr
)btcbP
->refCon
);
1438 btcbP
->refCon
= nil
;
1441 fcbP
= GPtr
->calculatedCatalogFCB
; // release catalog BTree bit map
1442 btcbP
= (BTreeControlBlock
*)fcbP
->fcbBtree
;
1444 if( btcbP
->refCon
!= nil
)
1446 if(((BTreeExtensionsRec
*)btcbP
->refCon
)->BTCBMPtr
!= nil
)
1448 DisposeMemory(((BTreeExtensionsRec
*)btcbP
->refCon
)->BTCBMPtr
);
1451 DisposeMemory( (Ptr
)btcbP
->refCon
);
1453 btcbP
->refCon
= nil
;
1458 DisposeMemory(GPtr
->DirPTPtr
);
1459 DisposeMemory((ScavStaticStructures
*)GPtr
->scavStaticPtr
);
1460 GPtr
->scavStaticPtr
= nil
;
1461 GPtr
->calculatedVCB
= nil
;
1466 #define BLUE_BOX_SHARED_DRVR_NAME "\p.BlueBoxShared"
1467 #define BLUE_BOX_FLOPPY_WHERE_STRING "\pdisk%d (Shared)"
1468 #define SONY_DRVR_NAME "\p.Sony"
1470 /*------------------------------------------------------------------------------
1472 Routine: IsBlueBoxSharedDrive
1474 Function: Given a DQE address, return a boolean that determines whether
1475 or not a drive is a Blue Box disk being accessed via Shared mode.
1476 Such drives do not support i/o and cannot be scavenged.
1478 Input: Arg 1 - DQE pointer
1480 Output: D0.L - 0 if drive not to be used
1482 ------------------------------------------------------------------------------*/
1484 struct IconAndStringRec
{
1488 typedef struct IconAndStringRec IconAndStringRec
, * IconAndStringRecPtr
;
1491 Boolean
IsBlueBoxSharedDrive ( DrvQElPtr dqPtr
)
1494 Str255 blueBoxSharedDriverName
= BLUE_BOX_SHARED_DRVR_NAME
;
1495 Str255 blueBoxFloppyWhereString
= BLUE_BOX_FLOPPY_WHERE_STRING
;
1496 Str255 sonyDriverName
= SONY_DRVR_NAME
;
1497 DCtlHandle driverDCtlHandle
;
1498 DCtlPtr driverDCtlPtr
;
1499 DRVRHeaderPtr drvrHeaderPtr
;
1500 StringPtr driverName
;
1502 if ( dqPtr
== NULL
)
1505 // Now look at the name of the Driver name. If it is .BlueBoxShared keep it out of the list of available disks.
1506 driverDCtlHandle
= GetDCtlEntry(dqPtr
->dQRefNum
);
1507 driverDCtlPtr
= *driverDCtlHandle
;
1508 if((((driverDCtlPtr
->dCtlFlags
) & Is_Native_Mask
) == 0) && (driverDCtlPtr
->dCtlDriver
!= nil
))
1510 if (((driverDCtlPtr
->dCtlFlags
) & Is_Ram_Based_Mask
) == 0)
1512 drvrHeaderPtr
= (DRVRHeaderPtr
)driverDCtlPtr
->dCtlDriver
;
1516 //¥¥¥ bek - lock w/o unlock/restore? should be getstate/setstate?
1517 HLock((Handle
)(driverDCtlPtr
)->dCtlDriver
);
1518 drvrHeaderPtr
= (DRVRHeaderPtr
)*((Handle
)(driverDCtlPtr
->dCtlDriver
));
1521 driverName
= (StringPtr
)&(drvrHeaderPtr
->drvrName
);
1522 if (!(IdenticalString(driverName
,blueBoxSharedDriverName
,nil
)))
1527 // Special case for the ".Sony" floppy driver which might be accessed in Shared mode inside the Blue Box
1528 // Test its "where" string instead of the driver name.
1529 if (!(IdenticalString(driverName
,sonyDriverName
,nil
)))
1531 CntrlParam paramBlock
;
1533 paramBlock
.ioCompletion
= nil
;
1534 paramBlock
.ioNamePtr
= nil
;
1535 paramBlock
.ioVRefNum
= dqPtr
->dQDrive
;
1536 paramBlock
.ioCRefNum
= dqPtr
->dQRefNum
;
1537 paramBlock
.csCode
= kDriveIcon
; // return physical icon
1539 // If PBControl(kDriveIcon) returns an error then the driver is not the Blue Box driver.
1540 if ( noErr
== PBControlSync( (ParmBlkPtr
) ¶mBlock
) )
1542 IconAndStringRecPtr iconAndStringRecPtr
;
1543 StringPtr whereStringPtr
;
1545 iconAndStringRecPtr
= * (IconAndStringRecPtr
*) & paramBlock
.csParam
;
1546 whereStringPtr
= (StringPtr
) & iconAndStringRecPtr
->string
;
1547 if (!(IdenticalString(whereStringPtr
,blueBoxFloppyWhereString
,nil
)))
1562 /*------------------------------------------------------------------------------
1564 Function: printVerifyStatus - (Print Verify Status)
1566 Function: Prints out the Verify Status words.
1568 Input: GPtr - pointer to scavenger global area
1571 ------------------------------------------------------------------------------*/
1573 void printVerifyStatus(SGlobPtr GPtr
)
1577 stat
= GPtr
->VIStat
| GPtr
->ABTStat
| GPtr
->EBTStat
| GPtr
->CBTStat
| GPtr
->CatStat
;
1580 plog(" Verify Status: VIStat = 0x%04x, ABTStat = 0x%04x EBTStat = 0x%04x\n",
1581 GPtr
->VIStat
, GPtr
->ABTStat
, GPtr
->EBTStat
);
1582 plog(" CBTStat = 0x%04x CatStat = 0x%08x\n",
1583 GPtr
->CBTStat
, GPtr
->CatStat
);