1/*
2 * Copyright (c) 1999-2011 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23/*
24	File:		SControl.c
25
26	Contains:	This file contains the routines which control the scavenging operations.
27
28	Version:	xxx put version here xxx
29
30	Written by:	Bill Bruffey
31
32	Copyright:	� 1985, 1986, 1992-1999 by Apple Computer, Inc., all rights reserved.
33*/
34
35#define SHOW_ELAPSED_TIMES  0
36
37
38#if SHOW_ELAPSED_TIMES
39#include <sys/time.h>
40#endif
41
42#include "Scavenger.h"
43#include "fsck_journal.h"
44#include <setjmp.h>
45#include <unistd.h>
46
47#ifndef CONFIG_HFS_TRIM
48#define CONFIG_HFS_TRIM 1
49#endif
50
51#define	DisplayTimeRemaining 0
52
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.
59 *
60 * TODO: Get this building properly within Xcode, without need for the version.pl script!
61 */
62extern const unsigned char fsck_hfsVersionString[];
63
64int gGUIControl;
65extern char lflag;
66
67
68// Static function prototypes
69
70static void printVerifyStatus( SGlobPtr GPtr );
71static Boolean IsBlueBoxSharedDrive ( DrvQElPtr dqPtr );
72static int ScavSetUp( SGlobPtr GPtr );
73static int ScavTerm( SGlobPtr GPtr );
74
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 */
76
77int cancelProc(UInt16 progress, UInt16 secondsRemaining, Boolean progressChanged, UInt16 stage, void *context, int passno)
78{
79    if (progressChanged) {
80	int base;
81	int pct;
82	int scale;
83	static int lastPct = -1;
84	if (passno < 0) {
85	    base = 0;
86	    scale = 100;
87	} else {
88	    base = (passno * 100) / kMaxReScan;	// Multiply by 100 because we're doing ints
89	    scale = 100 / kMaxReScan;
90	}
91	pct = ((progress * scale) / 100) + base;
92	if (pct != lastPct && pct != 100) {
93	    fsckPrint((fsck_ctx_t)context, fsckProgress, pct);
94	    lastPct = pct;
95	    draw_progress(pct);
96	}
97    }
98    return 0;
99}
100
101static const int kMaxMediumErrors = 25;
102
103/*
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.
108 */
109static int
110isMinorError(int msg, int *counts)
111{
112	switch (msg) {
113	case hfsExtBTCheck:
114	case hfsCatBTCheck:
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:
128	case hfsCheckHFS:
129	case hfsCheckNoJnl:
130	case E_DirVal:
131	case E_CName:
132	case E_NoFile:
133	case E_NoRtThd:
134	case E_NoThd:
135	case E_NoDir:
136	case E_RtDirCnt:
137	case E_RtFilCnt:
138	case E_DirCnt:
139	case E_FilCnt:
140	case E_CatDepth:
141	case E_NoFThdFlg:
142	case E_CatalogFlagsNotZero:
143	case E_BadFileName:
144	case E_InvalidClumpSize:
145	case E_LockedDirName:
146	case E_FreeBlocks:
147	case E_LeafCnt:
148	case E_BadValue:
149	case E_InvalidID:
150	case E_DiskFull:
151	case E_InvalidLinkCount:
152	case E_UnlinkedFile:
153	case E_InvalidPermissions:
154	case E_InvalidUID_Unused:
155	case E_IllegalName:
156	case E_IncorrectNumThdRcd:
157	case E_SymlinkCreate:
158	case E_IncorrectAttrCount:
159	case E_IncorrectSecurityCount:
160	case E_PEOAttr:
161	case E_LEOAttr:
162	case E_FldCount:
163	case E_HsFldCount:
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:
188	case E_BadSymLink:
189	case E_BadSymLinkLength:
190	case E_BadSymLinkName:
191		return 1;
192	/*
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.
196	 */
197	case E_PEOF:
198	case E_LEOF:
199		if (++counts[abs(msg)] > kMaxMediumErrors)
200			return 0;
201		return 1;
202	default:
203		return 0;
204	}
205}
206
207/*------------------------------------------------------------------------------
208
209External
210 Routines:		CheckHFS	- Controls the scavenging process.
211
212------------------------------------------------------------------------------*/
213
214static jmp_buf				envBuf;
215int
216CheckHFS( 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 )
219{
220	SGlob				dataArea;	// Allocate the scav globals
221	short				temp;
222	FileIdentifierTable	*fileIdentifierTable	= nil;
223	OSErr				err = noErr;
224	OSErr				scavError = 0;
225	int					scanCount = 0;
226	int					isJournaled = 0;
227	Boolean 			autoRepair;
228	Boolean				exitEarly = 0;
229	__block int *msgCounts = NULL;
230	Boolean				majorErrors = 0;
231
232	if (checkLevel == kMajorCheck) {
233		checkLevel = kForceCheck;
234		exitEarly = 1;
235		msgCounts = malloc(sizeof(int) * E_LastError);
236	}
237
238	autoRepair = (fsWriteRef != -1 && repairLevel != kNeverRepair);
239
240	/* Initialize the messages only once before the verify stage */
241	if (fsckContext) {
242		extern fsck_message_t hfs_messages[];
243		extern fsck_message_t hfs_errors[];
244
245		if (fsckAddMessages(fsckContext, hfs_messages) == -1 ||
246		    fsckAddMessages(fsckContext, hfs_errors) == -1) {
247			// XXX
248			return -1;
249		}
250	}
251
252	/*
253	 * Get the project name and version that is being built.
254	 *
255	 * The __fsck_hfsVersionString contents are of the form:
256	 * "@(#)PROGRAM:fsck_hfs  PROJECT:hfs-557~332\n"
257	 */
258	if (1) {
259		const char project[] = "  PROJECT:";
260		char *vstr, *tmp;
261
262		tmp = strstr((const char *)fsck_hfsVersionString, project);
263		if (tmp) {
264		    vstr = strdup(tmp + strlen(project));
265		    tmp = strstr(vstr, "\n");
266		    if (tmp)
267			*tmp = 0;
268		} else {
269		    vstr = strdup((const char *)fsck_hfsVersionString);
270		}
271
272		fsckPrint(fsckContext, fsckInformation, "fsck_hfs", vstr);
273		free(vstr);
274	}
275
276	if (setjmp(envBuf) == 1) {
277		/*
278		 * setjmp() returns the second argument to longjmp(), so if it returns 1, then
279		 * we've hit a major error.
280		 */
281		dataArea.RepLevel = repairLevelVeryMinorErrors;
282		majorErrors = 1;
283		goto EarlyExitLabel;
284	} else {
285		if (exitEarly && fsckContext) {
286			/*
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.
290			 */
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;
295					longjmp(envBuf, 1);
296					return fsckBlockAbort;
297				} else {
298					return fsckBlockContinue;
299				}
300			});
301		}
302	}
303DoAgain:
304	ClearMemory( &dataArea, sizeof(SGlob) );
305	if (msgCounts)
306		memset(msgCounts, 0, sizeof(int) * E_LastError);
307
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';
322	}
323
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';
328
329	if (fsckContext) {
330		dataArea.context = fsckContext;
331		dataArea.guiControl = true;
332		dataArea.userCancelProc = cancelProc;
333	}
334	//
335	//	Initialize the scavenger
336	//
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!
341		goto termScav;
342	}
343
344	isJournaled = CheckIfJournaled( &dataArea, false );
345	if (isJournaled != 0 &&
346	    scanCount == 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");
352			}
353		scavError = 0;
354		goto termScav;
355	}
356	dataArea.calculatedVCB->vcbDriveNumber = fsReadRef;
357	dataArea.calculatedVCB->vcbDriverWriteRef = fsWriteRef;
358
359	// Only show the progress bar if we're doing a real check.
360	if (fsckContext) {
361		start_progress();
362	}
363
364	//
365	//	Now verify the volume
366	//
367	if ( scavError == noErr )
368		ScavCtrl( &dataArea, scavVerify, &scavError );
369
370EarlyExitLabel:
371	if (scavError == noErr && fsckGetVerbosity(dataArea.context) >= kDebugLog)
372		printVerifyStatus(&dataArea);
373
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);
378		scavError = R_RFail;
379		goto termScav;
380	}
381
382	if ( dataArea.RepLevel == repairLevelUnrepairable )
383		err = cdUnrepairableErr;
384
385	if ( !autoRepair &&
386	     (dataArea.RepLevel == repairLevelVolumeRecoverable ||
387	      dataArea.RepLevel == repairLevelCatalogBtreeRebuild  ||
388	      dataArea.RepLevel == repairLevelVeryMinorErrors) ) {
389		fsckPrint(dataArea.context, fsckVolumeCorruptNeedsRepair, dataArea.volumeName);
390		scavError = R_VFail;
391		goto termScav;
392	}
393
394	if ( scavError == noErr && dataArea.RepLevel == repairLevelNoProblemsFound ) {
395		if (CONFIG_HFS_TRIM &&
396		    (dataArea.canWrite != 0) && (dataArea.writeRef != -1) &&
397		    IsTrimSupported())
398		{
399			fsckPrint(dataArea.context, fsckTrimming);
400			TrimFreeBlocks(&dataArea);
401		}
402
403		if (scanCount == 0) {
404			fsckPrint(dataArea.context, fsckVolumeOK, dataArea.volumeName);
405		} else {
406			fsckPrint(dataArea.context, fsckRepairSuccessful, dataArea.volumeName);
407		}
408	}
409
410	//
411	//	Repair the volume if it needs repairs, its repairable and we were able to unmount it
412	//
413	if ( dataArea.RepLevel == repairLevelNoProblemsFound && repairLevel == kForceRepairs )
414	{
415		if (rebuildOptions & REBUILD_CATALOG) {
416			dataArea.CBTStat |= S_RebuildBTree;
417		}
418		if (rebuildOptions & REBUILD_EXTENTS) {
419			dataArea.EBTStat |= S_RebuildBTree;
420		}
421		if (rebuildOptions & REBUILD_ATTRIBUTE) {
422			dataArea.ABTStat |= S_RebuildBTree;
423		}
424		dataArea.RepLevel = repairLevelCatalogBtreeRebuild;
425	}
426
427	if ( ((scavError == noErr) || (scavError == errRebuildBtree))  &&
428		 (autoRepair == true)	&&
429		 (dataArea.RepLevel != repairLevelUnrepairable)	&&
430		 (dataArea.RepLevel != repairLevelNoProblemsFound) )
431	{
432		// we cannot repair a volume when others have write access to the block device
433		// for the volume
434
435		if ( dataArea.canWrite == 0 ) {
436			scavError = R_WrErr;
437			fsckPrint(dataArea.context, fsckVolumeNotRepairedInUse, dataArea.volumeName);
438		}
439		else
440			ScavCtrl( &dataArea, scavRepair, &scavError );
441
442		if ( scavError == noErr )
443		{
444			*modified = 1;	/* Report back that we made repairs */
445
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);
451			scanCount++;
452			goto DoAgain;
453		}
454		else {
455			fsckPrint(dataArea.context, fsckVolumeNotRepaired, dataArea.volumeName);
456		}
457	}
458	else if ( scavError != noErr ) {
459		// Is this correct?
460		fsckPrint(dataArea.context, fsckVolumeVerifyIncomplete, dataArea.volumeName);
461		if ( fsckGetVerbosity(dataArea.context) >= kDebugLog )
462			plog("\tvolume check failed with error %d \n", scavError);
463	}
464
465	//	Set up structures for post processing
466	if ( (autoRepair == true) && (dataArea.fileIdentifierTable != nil) )
467	{
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 ) );
471	}
472
473
474	//
475	//	Post processing
476	//
477	if ( fileIdentifierTable != nil )
478	{
479		DisposeMemory( fileIdentifierTable );
480	}
481
482termScav:
483	if (gBlkListEntries != 0)
484		dumpblocklist(&dataArea);
485
486	if (err == noErr) {
487		err = scavError;
488	}
489
490	//
491	//	Terminate the scavenger
492	//
493
494	if ( fsckGetVerbosity(dataArea.context) >= kDebugLog &&
495		 (err != noErr || dataArea.RepLevel != repairLevelNoProblemsFound) )
496		PrintVolumeObject();
497	if (err != 0 && embedded == 1) {
498		Buf_t *buf = NULL;
499		off_t offset = 1024;
500		uint32_t len = 512;	// the size of an HFS+ volume header
501		int rv = CacheRead(&fscache, offset, len, &buf);
502		if (rv == 0) {
503			fprintf(stderr, "Offset %llu length %u:\n", offset, len);
504			DumpData(buf->Buffer, len);
505			CacheRelease(&fscache, buf, 0);
506		} else {
507			fprintf(stderr, "%s(%d):  rv = %d\n", __FUNCTION__, __LINE__, rv);
508		}
509		fflush(stderr);
510	}
511
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)) {
515		Boolean update;
516		if (scavError) {
517			// Mark volume dirty
518			CheckForClean(&dataArea, kMarkVolumeDirty, &update);
519		} else {
520			// Mark volume clean
521			CheckForClean(&dataArea, kMarkVolumeClean, &update);
522		}
523		if (update) {
524			/* Report back that volume was modified */
525			*modified = 1;
526		}
527	}
528	ScavCtrl( &dataArea, scavTerminate, &temp );		//	Note: use a temp var so that real scav error can be returned
529
530	if (fsckContext) {
531		fsckPrint( fsckContext, fsckProgress, 100);	// End each run with 100% message, if desired
532		draw_progress(100);
533		end_progress();
534	}
535	if (exitEarly && majorErrors)
536		err = MAJOREXIT;
537
538	if (msgCounts) {
539		free(msgCounts);
540	}
541
542	return( err );
543}
544
545
546/*------------------------------------------------------------------------------
547
548Function:	ScavCtrl - (Scavenger Control)
549
550Function:	Controls the scavenging process.  Interfaces with the User Interface
551			Layer (written in PASCAL).
552
553Input:		ScavOp		-	scavenging operation to be performed:
554
555								scavInitialize	= start initial volume check
556								scavVerify	= start verify
557								scavRepair	= start repair
558								scavTerminate		= finished scavenge
559
560			GPtr		-	pointer to scavenger global area
561
562
563Output:		ScavRes		-	scavenge result code (R_xxx, or 0 if no error)
564
565------------------------------------------------------------------------------*/
566
567void ScavCtrl( SGlobPtr GPtr, UInt32 ScavOp, short *ScavRes )
568{
569	OSErr			result;
570	unsigned int		stat;
571#if SHOW_ELAPSED_TIMES
572	struct timeval 	myStartTime;
573	struct timeval 	myEndTime;
574	struct timeval 	myElapsedTime;
575	struct timezone zone;
576#endif
577
578	//
579	//	initialize some stuff
580	//
581	result			= noErr;						//	assume good status
582	*ScavRes		= 0;
583	GPtr->ScavRes	= 0;
584
585	//
586	//	dispatch next scavenge operation
587	//
588	switch ( ScavOp )
589	{
590		case scavInitialize:								//	INITIAL VOLUME CHECK
591		{
592			Boolean modified;
593			int clean;
594
595			if ( ( result = ScavSetUp( GPtr ) ) )			//	set up BEFORE CheckForStop
596				break;
597			if ( IsBlueBoxSharedDrive( GPtr->DrvPtr ) )
598				break;
599			if ( ( result = CheckForStop( GPtr ) ) )		//	in order to initialize wrCnt
600				break;
601
602			/* Call for all chkLevel options and check return value only
603			 * for kDirtyCheck for preen option and kNeverCheck for quick option
604			 */
605			clean = CheckForClean(GPtr, kCheckVolume, &modified);
606			if ((GPtr->chkLevel == kDirtyCheck) || (GPtr->chkLevel == kNeverCheck)) {
607				if (clean == 1) {
608					/* volume was unmounted cleanly */
609					GPtr->cleanUnmount = true;
610					break;
611				}
612
613				if (GPtr->chkLevel == kNeverCheck) {
614					if (clean == -1)
615						result = R_BadSig;
616					else if (clean == 0) {
617						/*
618					 	 * We lie for journaled file systems since
619					     * they get cleaned up in mount by replaying
620					 	 * the journal.
621					 	 * Note: CheckIfJournaled will return negative
622					     * if it finds lastMountedVersion = FSK!.
623					     */
624						if (CheckIfJournaled(GPtr, false))
625							GPtr->cleanUnmount = true;
626						else
627							result = R_Dirty;
628					}
629					break;
630				}
631			}
632
633			if (CheckIfJournaled(GPtr, false)
634			    && GPtr->chkLevel != kForceCheck
635			    && !(GPtr->chkLevel == kPartialCheck && GPtr->repairLevel == kForceRepairs)
636				&& !(GPtr->chkLevel == kAlwaysCheck && GPtr->repairLevel == kMajorRepairs)) {
637			    break;
638			}
639
640			if (GPtr->liveVerifyState) {
641				fsckPrint(GPtr->context, hfsLiveVerifyCheck);
642			} else if (GPtr->canWrite == 0 && nflag == 0) {
643				fsckPrint(GPtr->context, hfsVerifyVolWithWrite);
644			}
645
646			/*
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.)
654			 */
655			if ((GPtr->scanCount == 0) &&
656			    (CheckIfJournaled(GPtr, true) == 1) &&
657			    (GPtr->canWrite == 0 || GPtr->writeRef == -1) &&
658			    (lflag == 0)) {
659				fsckJournalInfo_t jnlInfo = { 0 };
660				UInt64 numBlocks;
661				UInt32 blockSize;
662				jnlInfo.jnlfd = -1;
663
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;
669					} else {
670						(void)GetDeviceSize(GPtr->calculatedVCB->vcbDriveNumber, &numBlocks, &blockSize);
671#if 0
672						// For debugging the cache.  WAY to verbose to run with even normal debug
673						if (debug) {
674							printf("Before journal replay\n");
675							dumpCache(&fscache);
676						}
677#endif
678						if (journal_open(jnlInfo.jnlfd,
679								 jnlInfo.jnlOffset,
680								 jnlInfo.jnlSize,
681								 blockSize,
682								 0,
683								 jnlInfo.name,
684								 ^(off_t start, void *data, size_t len) {
685									 Buf_t *buf;
686									 int rv;
687									 rv = CacheRead(&fscache, start, (int)len, &buf);
688									 if (rv != 0)
689										 abort();
690									 memcpy(buf->Buffer, data, len);
691									 rv = CacheWrite(&fscache, buf, 0, kLockWrite);
692									 if (rv != 0)
693										 abort();
694									 return 0;}
695							    ) == -1) {
696							fsckPrint(GPtr->context, E_DirtyJournal);
697							GPtr->JStat |= S_DirtyJournal;
698						} else if (debug) {
699							plog("Journal replay simulation succeeded\n");
700#if 0
701							// Still way too verbose to run
702							dumpCache(&fscache);
703#endif
704						}
705					}
706				} else {
707					if (debug)
708						plog("Journal is empty\n");
709				}
710				if (jnlInfo.jnlfd != -1)
711					close(jnlInfo.jnlfd);
712				if (jnlInfo.name != NULL)
713					free(jnlInfo.name);
714			}
715
716			result = IVChk( GPtr );
717
718			break;
719		}
720
721		case scavVerify:								//	VERIFY
722		{
723
724#if SHOW_ELAPSED_TIMES
725			gettimeofday( &myStartTime, &zone );
726#endif
727
728			/* Initialize volume bitmap structure */
729			if ( BitMapCheckBegin(GPtr) != 0)
730				break;
731
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 );
738#endif
739
740			if ( IsBlueBoxSharedDrive( GPtr->DrvPtr ) )
741				break;
742			if ( ( result = CheckForStop( GPtr ) ) )
743				break;
744
745#if SHOW_ELAPSED_TIMES
746			gettimeofday( &myStartTime, &zone );
747#endif
748
749			/* Create calculated BTree structures */
750			if ( ( result = CreateExtentsBTreeControlBlock( GPtr ) ) )
751				break;
752			if ( ( result = CreateCatalogBTreeControlBlock( GPtr ) ) )
753				break;
754			if ( ( result = CreateAttributesBTreeControlBlock( GPtr ) ) )
755				break;
756			if ( ( result = CreateExtendedAllocationsFCB( GPtr ) ) )
757				break;
758
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 );
765#endif
766
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
770
771			if ( ( result = VLockedChk( GPtr ) ) )
772				break;
773
774			GPtr->itemsProcessed += GPtr->onePercent;	// We do this 4 times as set up in CalculateItemCount() to smooth the scroll
775			fsckPrint(GPtr->context, hfsExtBTCheck);
776
777#if SHOW_ELAPSED_TIMES
778			gettimeofday( &myStartTime, &zone );
779#endif
780
781			/* Verify extent btree structure */
782			if ((result = ExtBTChk(GPtr)))
783				break;
784
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 );
791#endif
792
793			if ((result = CheckForStop(GPtr)))
794				break;
795
796			GPtr->itemsProcessed += GPtr->onePercent;	// We do this 4 times as set up in CalculateItemCount() to smooth the scroll
797
798			/* Check extents of bad block file */
799			if ((result = BadBlockFileExtentCheck(GPtr)))
800				break;
801			if ((result = CheckForStop(GPtr)))
802				break;
803
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);
807
808#if SHOW_ELAPSED_TIMES
809			gettimeofday( &myStartTime, &zone );
810#endif
811
812			if ( GPtr->chkLevel == kPartialCheck )
813			{
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;
819				}
820				if (GPtr->rebuildOptions & REBUILD_EXTENTS) {
821					GPtr->EBTStat |= S_RebuildBTree;
822				}
823				if (GPtr->rebuildOptions & REBUILD_ATTRIBUTE) {
824					GPtr->ABTStat |= S_RebuildBTree;
825				}
826				result = errRebuildBtree;
827				break;
828			}
829
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
833			 */
834			if ((result = CheckCatalogBTree(GPtr)))
835				break;
836
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 );
843#endif
844
845			if ((result = CheckForStop(GPtr)))
846				break;
847
848			if (scanflag == 0) {
849				fsckPrint(GPtr->context, hfsCatHierCheck);
850
851#if SHOW_ELAPSED_TIMES
852				gettimeofday( &myStartTime, &zone );
853#endif
854
855				/* Check catalog hierarchy */
856				if ((result = CatHChk(GPtr)))
857					break;
858
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 );
865#endif
866
867				if ((result = CheckForStop(GPtr)))
868					break;
869
870				if (VolumeObjectIsHFSX(GPtr)) {
871					result = CheckFolderCount(GPtr);
872					if (result)
873						break;
874
875					if ((result=CheckForStop(GPtr)))
876						break;
877				}
878			}
879			/* Check attribute btree.  The function accounts for all extents
880			 * for extended attributes whose values are stored in
881			 * allocation blocks
882			 */
883			if ((result = AttrBTChk(GPtr)))
884				break;
885
886			if ((result = CheckForStop(GPtr)))
887				break;
888
889			/*
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.
897			 */
898			if (GPtr->VIStat & S_OverlappingExtents) {
899				/* Find original files involved in overlapped extents */
900				result = FindOrigOverlapFiles(GPtr);
901				if (result) {
902					break;
903				}
904
905				/* Print all unique overlapping file IDs and paths */
906				(void) PrintOverlapFiles(GPtr);
907			}
908
909			if (scanflag == 0) {
910				/* Directory inodes store first link information in
911				 * an extended attribute.  Therefore start directory
912				 * hard link check after extended attribute checks.
913				 */
914				result = dirhardlink_check(GPtr);
915				/* On error or unrepairable corruption, stop the verification */
916				if ((result != 0) || (GPtr->CatStat & S_LinkErrNoRepair)) {
917					if (result == 0) {
918						result = -1;
919					}
920
921					break;
922				}
923			}
924
925			fsckPrint(GPtr->context, hfsVolBitmapCheck);
926
927#if SHOW_ELAPSED_TIMES
928			gettimeofday( &myStartTime, &zone );
929#endif
930
931			/* Compare in-memory volume bitmap with on-disk bitmap */
932			if ((result = CheckVolumeBitMap(GPtr, false)))
933				break;
934
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 );
941#endif
942
943			if ((result = CheckForStop(GPtr)))
944				break;
945
946			fsckPrint(GPtr->context, hfsVolInfoCheck);
947
948#if SHOW_ELAPSED_TIMES
949			gettimeofday( &myStartTime, &zone );
950#endif
951
952			/* Verify volume level information */
953			if ((result = VInfoChk(GPtr)))
954				break;
955
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 );
962#endif
963
964			stat =	GPtr->VIStat  | GPtr->ABTStat | GPtr->EBTStat | GPtr->CBTStat |
965					GPtr->CatStat | GPtr->JStat;
966
967			if ( stat != 0 )
968			{
969				if ( (GPtr->RepLevel == repairLevelNoProblemsFound) || (GPtr->RepLevel == repairLevelVolumeRecoverable) )
970				{
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;
977					else
978						GPtr->RepLevel =  repairLevelVolumeRecoverable;
979				}
980			}
981			else if ( GPtr->RepLevel == repairLevelNoProblemsFound )
982			{
983			}
984
985			GPtr->itemsProcessed = GPtr->itemsToProcess;
986			result = CheckForStop(GPtr);				//	one last check for modified volume
987			break;
988		}
989
990		case scavRepair:									//	REPAIR
991		{
992			if ( IsBlueBoxSharedDrive( GPtr->DrvPtr ) )
993				break;
994			if ( ( result = CheckForStop(GPtr) ) )
995				break;
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
1002			} else {
1003				fsckPrint(GPtr->context, fsckRepairingVolume);
1004				if (embedded == 1 && debug == 0)
1005					fsckPrint(GPtr->context, fsckLimitedRepairs);
1006			}
1007			result = RepairVolume( GPtr );
1008			break;
1009		}
1010
1011		case scavTerminate:									//	CLEANUP AFTER SCAVENGE
1012		{
1013			result = ScavTerm(GPtr);
1014			break;
1015		}
1016	}													//	end ScavOp switch
1017
1018
1019	//
1020	//	Map internal error codes to scavenger result codes
1021	//
1022	if ( (result < 0) || (result > Max_RCode) )
1023	{
1024		switch ( ScavOp )
1025		{
1026			case scavInitialize:
1027			case scavVerify:
1028				if ( result == ioErr )
1029					result = R_RdErr;
1030				else if ( result == errRebuildBtree )
1031				{
1032					GPtr->RepLevel = repairLevelCatalogBtreeRebuild;
1033					break;
1034				}
1035				else
1036					result = R_VFail;
1037				GPtr->RepLevel = repairLevelUnrepairable;
1038				break;
1039			case scavRepair:
1040				result = R_RFail;
1041				break;
1042			default:
1043				result = R_IntErr;
1044		}
1045	}
1046
1047	GPtr->ScavRes = result;
1048
1049	*ScavRes = result;
1050
1051}	//	end of ScavCtrl
1052
1053
1054
1055/*------------------------------------------------------------------------------
1056
1057Function: 	CheckForStop
1058
1059Function:	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.
1063
1064Input:		GPtr		-  	pointer to scavenger global area
1065
1066Output:		Function result:
1067						0 - ok to continue
1068						R_UInt - STOP button hit
1069						R_Modified - another app has touched the volume
1070-------------------------------------------------------------------------------*/
1071
1072short CheckForStop( SGlob *GPtr )
1073{
1074	OSErr	err			= noErr;						//	Initialize err to noErr
1075	long	ticks		= TickCount();
1076	UInt16	dfaStage	= (UInt16) GetDFAStage();
1077
1078        //plog("%d, %d", dfaStage, kAboutToRepairStage);
1079
1080	//if ( ((ticks - 10) > GPtr->lastTickCount) || (dfaStage == kAboutToRepairStage) )			//	To reduce cursor flicker on fast machines, call through on a timed interval
1081	//{
1082		if ( GPtr->userCancelProc != nil )
1083		{
1084			UInt64	progress = 0;
1085			Boolean	progressChanged;
1086		//	UInt16	elapsedTicks;
1087
1088			if ( dfaStage != kRepairStage )
1089			{
1090				progress = GPtr->itemsProcessed * 100;
1091				progress /= GPtr->itemsToProcess;
1092				progressChanged = ( progress != GPtr->lastProgress );
1093				GPtr->lastProgress = progress;
1094
1095				#if( DisplayTimeRemaining )
1096					if ( (progressChanged) && (progress > 5) )
1097					{
1098						elapsedTicks	=	TickCount() - GPtr->startTicks;
1099						GPtr->secondsRemaining	= ( ( ( 100 * elapsedTicks ) / progress ) - elapsedTicks ) / 60;
1100					}
1101				#endif
1102				err = CallUserCancelProc( GPtr->userCancelProc, (UInt16)progress, (UInt16)GPtr->secondsRemaining, progressChanged, dfaStage, GPtr->context, GPtr->scanCount );
1103			}
1104			else
1105			{
1106				(void) CallUserCancelProc( GPtr->userCancelProc, (UInt16)progress, 0, false, dfaStage, GPtr->context, GPtr->scanCount );
1107			}
1108
1109		}
1110
1111		if ( err != noErr )
1112			err = R_UInt;
1113	#if 0
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
1117	#endif
1118		GPtr->lastTickCount	= ticks;
1119	//}
1120
1121	return ( err );
1122}
1123
1124
1125
1126/*------------------------------------------------------------------------------
1127
1128Function:	ScavSetUp - (Scavenger Set Up)
1129
1130Function:	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.
1134
1135Input:		GPtr		-	pointer to scavenger global area
1136
1137Output:		ScavSetUp	-	function result:
1138								0	= no error
1139								n 	= error code
1140------------------------------------------------------------------------------*/
1141
1142struct ScavStaticStructures {
1143	SVCB				vcb;
1144	SFCB				fcbList[6];
1145	BTreeControlBlock	btcb[4];			// 4 btcb's
1146	SBTPT				btreePath;			// scavenger BTree path table
1147};
1148typedef struct ScavStaticStructures ScavStaticStructures;
1149
1150
1151static int ScavSetUp( SGlob *GPtr)
1152{
1153	OSErr  err;
1154	SVCB * vcb;
1155#if !BSD
1156	DrvQEl	*drvP;
1157	short	ioRefNum;
1158#endif
1159
1160	GPtr->MinorRepairsP = nil;
1161
1162	GPtr->itemsProcessed = 0;
1163	GPtr->lastProgress = 0;
1164	GPtr->startTicks = TickCount();
1165
1166	//
1167	//	allocate the static data structures (VCB, FCB's, BTCB'S, DPT and BTPT)
1168	//
1169 	{
1170		ScavStaticStructures	*pointer;
1171
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) );
1177			}
1178			return( R_NoMem );
1179		}
1180		GPtr->scavStaticPtr = pointer;
1181
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 );
1187			}
1188			return( R_NoMem );
1189		}
1190		GPtr->dirPathCount = CMMaxDepth;
1191
1192		GPtr->calculatedVCB = vcb	= &pointer->vcb;
1193		vcb->vcbGPtr = GPtr;
1194
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];
1202
1203		GPtr->calculatedExtentsBTCB		= &pointer->btcb[0];
1204		GPtr->calculatedCatalogBTCB		= &pointer->btcb[1];
1205		GPtr->calculatedRepairBTCB		= &pointer->btcb[2];
1206		GPtr->calculatedAttributesBTCB	= &pointer->btcb[3];
1207
1208		GPtr->BTPTPtr					= (SBTPT*) &pointer->btreePath;
1209	}
1210
1211
1212	SetDFAStage( kVerifyStage );
1213	SetFCBSPtr( GPtr->FCBAPtr );
1214
1215	//
1216	//	locate the driveQ element for drive being scavenged
1217	//
1218 	GPtr->DrvPtr	= 0;							//	<8> initialize so we can know if drive disappears
1219
1220	//
1221	//	Set up Real structures
1222	//
1223#if !BSD
1224	err = FindDrive( &ioRefNum, &(GPtr->DrvPtr), GPtr->DrvNum );
1225#endif
1226	if ( IsBlueBoxSharedDrive( GPtr->DrvPtr ) )
1227		return noErr;
1228
1229	err = GetVolumeFeatures( GPtr );				//	Sets up GPtr->volumeFeatures and GPtr->realVCB
1230
1231#if !BSD
1232	if ( GPtr->DrvPtr == NULL )						//	<8> drive is no longer there!
1233		return ( R_NoVol );
1234	else
1235		drvP = GPtr->DrvPtr;
1236
1237	//	Save current value of vcbWrCnt, to detect modifications to volume by other apps etc
1238	if ( GPtr->volumeFeatures & volumeIsMountedMask )
1239	{
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
1242	}
1243#endif
1244
1245	//	Finish initializing the VCB
1246
1247	//	The calculated structures
1248#if BSD
1249	InitBlockCache(vcb);
1250	vcb->vcbDriveNumber = GPtr->DrvNum;
1251	vcb->vcbDriverReadRef = GPtr->DrvNum;
1252	vcb->vcbDriverWriteRef = -1;	/* XXX need to get real fd here */
1253#else
1254	vcb->vcbDriveNumber = drvP->dQDrive;
1255	vcb->vcbDriverReadRef = drvP->dQRefNum;
1256	vcb->vcbDriverWriteRef = drvP->dQRefNum;
1257	vcb->vcbFSID = drvP->dQFSID;
1258#endif
1259//	vcb->vcbVRefNum = Vol_RefN;
1260
1261	//
1262	//	finish initializing the FCB's
1263	//
1264	{
1265		SFCB *fcb;
1266
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;
1273
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;
1280
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;
1287
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;
1294
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;
1301	}
1302
1303	//	finish initializing the BTCB's
1304	{
1305		BTreeControlBlock	*btcb;
1306
1307		btcb			= GPtr->calculatedExtentsBTCB;		// calculatedExtentsBTCB
1308		btcb->fcbPtr		= GPtr->calculatedExtentsFCB;
1309		btcb->getBlockProc	= GetFileBlock;
1310		btcb->releaseBlockProc	= ReleaseFileBlock;
1311		btcb->setEndOfForkProc	= SetEndOfForkProc;
1312
1313		btcb			= GPtr->calculatedCatalogBTCB;		// calculatedCatalogBTCB
1314		btcb->fcbPtr		= GPtr->calculatedCatalogFCB;
1315		btcb->getBlockProc	= GetFileBlock;
1316		btcb->releaseBlockProc	= ReleaseFileBlock;
1317		btcb->setEndOfForkProc	= SetEndOfForkProc;
1318
1319		btcb			= GPtr->calculatedAttributesBTCB;	// calculatedAttributesBTCB
1320		btcb->fcbPtr		= GPtr->calculatedAttributesFCB;
1321		btcb->getBlockProc	= GetFileBlock;
1322		btcb->releaseBlockProc	= ReleaseFileBlock;
1323		btcb->setEndOfForkProc	= SetEndOfForkProc;
1324	}
1325
1326
1327	//
1328	//	Initialize some global stuff
1329	//
1330
1331	GPtr->RepLevel			= repairLevelNoProblemsFound;
1332	GPtr->ErrCode			= 0;
1333	GPtr->IntErr			= noErr;
1334	GPtr->VIStat			= 0;
1335	GPtr->ABTStat			= 0;
1336	GPtr->EBTStat			= 0;
1337	GPtr->CBTStat			= 0;
1338	GPtr->CatStat			= 0;
1339	GPtr->VeryMinorErrorsStat	= 0;
1340	GPtr->JStat			= 0;
1341
1342	/* Assume that the volume is dirty unmounted */
1343	GPtr->cleanUnmount		= false;
1344
1345 	//
1346 	// Initialize VolumeObject
1347 	//
1348
1349 	InitializeVolumeObject( GPtr );
1350
1351	/* Check if the volume type of initialized object is valid.  If not, return error */
1352	if (VolumeObjectIsValid() == false) {
1353		return (R_BadSig);
1354	}
1355
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 );
1361		}
1362		return( R_NoMem );
1363	}
1364
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));
1369
1370	return( noErr );
1371
1372} /* end of ScavSetUp */
1373
1374
1375
1376
1377/*------------------------------------------------------------------------------
1378
1379Function:	ScavTerm - (Scavenge Termination))
1380
1381Function:	Terminates the current scavenging operation.  Memory for the
1382			VCB, FCBs, BTCBs, volume bit map, and BTree bit maps is
1383			released.
1384
1385Input:		GPtr		-	pointer to scavenger global area
1386
1387Output:		ScavTerm	-	function result:
1388								0	= no error
1389								n 	= error code
1390------------------------------------------------------------------------------*/
1391
1392static int ScavTerm( SGlobPtr GPtr )
1393{
1394	SFCB			*fcbP;
1395	BTreeControlBlock	*btcbP;
1396	RepairOrderPtr		rP;
1397	OSErr			err;
1398	ExtentsTable 		**extentsTableH;
1399	ExtentInfo		*curExtentInfo;
1400	int			i;
1401
1402	(void) BitMapCheckEnd();
1403
1404	while( (rP = GPtr->MinorRepairsP) != nil )		//	loop freeing leftover (undone) repair orders
1405	{
1406		GPtr->MinorRepairsP = rP->link;				//	(in case repairs were not made)
1407		DisposeMemory(rP);
1408		err = MemError();
1409	}
1410
1411	if( GPtr->validFilesList != nil )
1412		DisposeHandle( (Handle) GPtr->validFilesList );
1413
1414	if( GPtr->overlappedExtents != nil ) {
1415 		extentsTableH = GPtr->overlappedExtents;
1416
1417		/* Overlapped extents list also allocated memory for attribute name */
1418		for (i=0; i<(**extentsTableH).count; i++) {
1419			curExtentInfo = &((**extentsTableH).extentInfo[i]);
1420
1421			/* Deallocate memory for attribute name, if any */
1422			if (curExtentInfo->attrname) {
1423				free(curExtentInfo->attrname);
1424			}
1425		}
1426
1427		DisposeHandle( (Handle) GPtr->overlappedExtents );
1428	}
1429
1430	if( GPtr->fileIdentifierTable != nil )
1431		DisposeHandle( (Handle) GPtr->fileIdentifierTable );
1432
1433	if( GPtr->calculatedVCB == nil )								//	already freed?
1434		return( noErr );
1435
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
1438	if ( fcbP != nil )
1439	{
1440		btcbP = (BTreeControlBlock*)fcbP->fcbBtree;
1441		if ( btcbP != nil)
1442		{
1443			if( btcbP->refCon != nil )
1444			{
1445				if(((BTreeExtensionsRec*)btcbP->refCon)->BTCBMPtr != nil)
1446				{
1447					DisposeMemory(((BTreeExtensionsRec*)btcbP->refCon)->BTCBMPtr);
1448					err = MemError();
1449				}
1450				DisposeMemory( (Ptr)btcbP->refCon );
1451				err = MemError();
1452				btcbP->refCon = nil;
1453			}
1454
1455			fcbP = GPtr->calculatedCatalogFCB;	//	release catalog BTree bit map
1456			btcbP = (BTreeControlBlock*)fcbP->fcbBtree;
1457
1458			if( btcbP->refCon != nil )
1459			{
1460				if(((BTreeExtensionsRec*)btcbP->refCon)->BTCBMPtr != nil)
1461				{
1462					DisposeMemory(((BTreeExtensionsRec*)btcbP->refCon)->BTCBMPtr);
1463					err = MemError();
1464				}
1465				DisposeMemory( (Ptr)btcbP->refCon );
1466				err = MemError();
1467				btcbP->refCon = nil;
1468			}
1469		}
1470	}
1471
1472	DisposeMemory(GPtr->DirPTPtr);
1473	DisposeMemory((ScavStaticStructures *)GPtr->scavStaticPtr);
1474	GPtr->scavStaticPtr = nil;
1475	GPtr->calculatedVCB = nil;
1476
1477	return( noErr );
1478}
1479
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"
1483
1484/*------------------------------------------------------------------------------
1485
1486Routine:	IsBlueBoxSharedDrive
1487
1488Function: 	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.
1491
1492Input:		Arg 1	- DQE pointer
1493
1494Output:		D0.L -	0 if drive not to be used
1495					1 otherwise
1496------------------------------------------------------------------------------*/
1497
1498struct IconAndStringRec {
1499	char		icon[ 256 ];
1500	Str255		string;
1501};
1502typedef struct IconAndStringRec IconAndStringRec, * IconAndStringRecPtr;
1503
1504
1505Boolean IsBlueBoxSharedDrive ( DrvQElPtr dqPtr )
1506{
1507#if 0
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;
1515
1516	if ( dqPtr == NULL )
1517		return false;
1518
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))
1523	{
1524		if (((driverDCtlPtr->dCtlFlags) & Is_Ram_Based_Mask) == 0)
1525		{
1526			drvrHeaderPtr = (DRVRHeaderPtr)driverDCtlPtr->dCtlDriver;
1527		}
1528		else
1529		{
1530			//��� bek - lock w/o unlock/restore?  should be getstate/setstate?
1531			HLock((Handle)(driverDCtlPtr)->dCtlDriver);
1532			drvrHeaderPtr = (DRVRHeaderPtr)*((Handle)(driverDCtlPtr->dCtlDriver));
1533
1534		}
1535		driverName = (StringPtr)&(drvrHeaderPtr->drvrName);
1536		if (!(IdenticalString(driverName,blueBoxSharedDriverName,nil)))
1537		{
1538			return( true );
1539		}
1540
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)))
1544		{
1545			CntrlParam			paramBlock;
1546
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
1552
1553			// If PBControl(kDriveIcon) returns an error then the driver is not the Blue Box driver.
1554			if ( noErr == PBControlSync( (ParmBlkPtr) &paramBlock ) )
1555			{
1556				IconAndStringRecPtr		iconAndStringRecPtr;
1557				StringPtr				whereStringPtr;
1558
1559				iconAndStringRecPtr = * (IconAndStringRecPtr*) & paramBlock.csParam;
1560				whereStringPtr = (StringPtr) & iconAndStringRecPtr->string;
1561				if (!(IdenticalString(whereStringPtr,blueBoxFloppyWhereString,nil)))
1562				{
1563					return( true );
1564				}
1565			}
1566		}
1567	}
1568#endif
1569
1570	return false;
1571}
1572
1573
1574
1575
1576/*------------------------------------------------------------------------------
1577
1578Function:	printVerifyStatus - (Print Verify Status)
1579
1580Function:	Prints out the Verify Status words.
1581
1582Input:		GPtr	-	pointer to scavenger global area
1583
1584Output:		None.
1585------------------------------------------------------------------------------*/
1586static
1587void printVerifyStatus(SGlobPtr GPtr)
1588{
1589    UInt32 stat;
1590
1591    stat = GPtr->VIStat | GPtr->ABTStat | GPtr->EBTStat | GPtr->CBTStat | GPtr->CatStat;
1592
1593    if ( stat != 0 ) {
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);
1598    }
1599}
1600