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
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)) {
501		Boolean update;
502		if (scavError) {
503			// Mark volume dirty
504			CheckForClean(&dataArea, kMarkVolumeDirty, &update);
505		} else {
506			// Mark volume clean
507			CheckForClean(&dataArea, kMarkVolumeClean, &update);
508		}
509		if (update) {
510			/* Report back that volume was modified */
511			*modified = 1;
512		}
513	}
514	ScavCtrl( &dataArea, scavTerminate, &temp );		//	Note: use a temp var so that real scav error can be returned
515
516	if (fsckContext) {
517		fsckPrint( fsckContext, fsckProgress, 100);	// End each run with 100% message, if desired
518		draw_progress(100);
519		end_progress();
520	}
521	if (exitEarly && majorErrors)
522		err = MAJOREXIT;
523
524	if (msgCounts) {
525		free(msgCounts);
526	}
527
528	return( err );
529}
530
531
532/*------------------------------------------------------------------------------
533
534Function:	ScavCtrl - (Scavenger Control)
535
536Function:	Controls the scavenging process.  Interfaces with the User Interface
537			Layer (written in PASCAL).
538
539Input:		ScavOp		-	scavenging operation to be performed:
540
541								scavInitialize	= start initial volume check
542								scavVerify	= start verify
543								scavRepair	= start repair
544								scavTerminate		= finished scavenge
545
546			GPtr		-	pointer to scavenger global area
547
548
549Output:		ScavRes		-	scavenge result code (R_xxx, or 0 if no error)
550
551------------------------------------------------------------------------------*/
552
553void ScavCtrl( SGlobPtr GPtr, UInt32 ScavOp, short *ScavRes )
554{
555	OSErr			result;
556	unsigned int		stat;
557#if SHOW_ELAPSED_TIMES
558	struct timeval 	myStartTime;
559	struct timeval 	myEndTime;
560	struct timeval 	myElapsedTime;
561	struct timezone zone;
562#endif
563
564	//
565	//	initialize some stuff
566	//
567	result			= noErr;						//	assume good status
568	*ScavRes		= 0;
569	GPtr->ScavRes	= 0;
570
571	//
572	//	dispatch next scavenge operation
573	//
574	switch ( ScavOp )
575	{
576		case scavInitialize:								//	INITIAL VOLUME CHECK
577		{
578			Boolean modified;
579			int clean;
580
581			if ( ( result = ScavSetUp( GPtr ) ) )			//	set up BEFORE CheckForStop
582				break;
583			if ( IsBlueBoxSharedDrive( GPtr->DrvPtr ) )
584				break;
585			if ( ( result = CheckForStop( GPtr ) ) )		//	in order to initialize wrCnt
586				break;
587
588			/* Call for all chkLevel options and check return value only
589			 * for kDirtyCheck for preen option and kNeverCheck for quick option
590			 */
591			clean = CheckForClean(GPtr, kCheckVolume, &modified);
592			if ((GPtr->chkLevel == kDirtyCheck) || (GPtr->chkLevel == kNeverCheck)) {
593				if (clean == 1) {
594					/* volume was unmounted cleanly */
595					GPtr->cleanUnmount = true;
596					break;
597				}
598
599				if (GPtr->chkLevel == kNeverCheck) {
600					if (clean == -1)
601						result = R_BadSig;
602					else if (clean == 0) {
603						/*
604					 	 * We lie for journaled file systems since
605					     * they get cleaned up in mount by replaying
606					 	 * the journal.
607					 	 * Note: CheckIfJournaled will return negative
608					     * if it finds lastMountedVersion = FSK!.
609					     */
610						if (CheckIfJournaled(GPtr, false))
611							GPtr->cleanUnmount = true;
612						else
613							result = R_Dirty;
614					}
615					break;
616				}
617			}
618
619			if (CheckIfJournaled(GPtr, false)
620			    && GPtr->chkLevel != kForceCheck
621			    && !(GPtr->chkLevel == kPartialCheck && GPtr->repairLevel == kForceRepairs)
622				&& !(GPtr->chkLevel == kAlwaysCheck && GPtr->repairLevel == kMajorRepairs)) {
623			    break;
624			}
625
626			if (GPtr->liveVerifyState) {
627				fsckPrint(GPtr->context, hfsLiveVerifyCheck);
628			} else if (GPtr->canWrite == 0 && nflag == 0) {
629				fsckPrint(GPtr->context, hfsVerifyVolWithWrite);
630			}
631
632			/*
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.)
640			 */
641			if ((GPtr->scanCount == 0) &&
642			    (CheckIfJournaled(GPtr, true) == 1) &&
643			    (GPtr->canWrite == 0 || GPtr->writeRef == -1) &&
644			    (lflag == 0)) {
645				fsckJournalInfo_t jnlInfo = { 0 };
646				UInt64 numBlocks;
647				UInt32 blockSize;
648				jnlInfo.jnlfd = -1;
649
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;
655					} else {
656						(void)GetDeviceSize(GPtr->calculatedVCB->vcbDriveNumber, &numBlocks, &blockSize);
657#if 0
658						// For debugging the cache.  WAY to verbose to run with even normal debug
659						if (debug) {
660							printf("Before journal replay\n");
661							dumpCache(&fscache);
662						}
663#endif
664						if (journal_open(jnlInfo.jnlfd,
665								 jnlInfo.jnlOffset,
666								 jnlInfo.jnlSize,
667								 blockSize,
668								 0,
669								 jnlInfo.name,
670								 ^(off_t start, void *data, size_t len) {
671									 Buf_t *buf;
672									 int rv;
673									 rv = CacheRead(&fscache, start, (int)len, &buf);
674									 if (rv != 0)
675										 abort();
676									 memcpy(buf->Buffer, data, len);
677									 rv = CacheWrite(&fscache, buf, 0, kLockWrite);
678									 if (rv != 0)
679										 abort();
680									 return 0;}
681							    ) == -1) {
682							fsckPrint(GPtr->context, E_DirtyJournal);
683							GPtr->JStat |= S_DirtyJournal;
684						} else if (debug) {
685							plog("Journal replay simulation succeeded\n");
686#if 0
687							// Still way too verbose to run
688							dumpCache(&fscache);
689#endif
690						}
691					}
692				} else {
693					if (debug)
694						plog("Journal is empty\n");
695				}
696				if (jnlInfo.jnlfd != -1)
697					close(jnlInfo.jnlfd);
698				if (jnlInfo.name != NULL)
699					free(jnlInfo.name);
700			}
701
702			result = IVChk( GPtr );
703
704			break;
705		}
706
707		case scavVerify:								//	VERIFY
708		{
709
710#if SHOW_ELAPSED_TIMES
711			gettimeofday( &myStartTime, &zone );
712#endif
713
714			/* Initialize volume bitmap structure */
715			if ( BitMapCheckBegin(GPtr) != 0)
716				break;
717
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 );
724#endif
725
726			if ( IsBlueBoxSharedDrive( GPtr->DrvPtr ) )
727				break;
728			if ( ( result = CheckForStop( GPtr ) ) )
729				break;
730
731#if SHOW_ELAPSED_TIMES
732			gettimeofday( &myStartTime, &zone );
733#endif
734
735			/* Create calculated BTree structures */
736			if ( ( result = CreateExtentsBTreeControlBlock( GPtr ) ) )
737				break;
738			if ( ( result = CreateCatalogBTreeControlBlock( GPtr ) ) )
739				break;
740			if ( ( result = CreateAttributesBTreeControlBlock( GPtr ) ) )
741				break;
742			if ( ( result = CreateExtendedAllocationsFCB( GPtr ) ) )
743				break;
744
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 );
751#endif
752
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
756
757			if ( ( result = VLockedChk( GPtr ) ) )
758				break;
759
760			GPtr->itemsProcessed += GPtr->onePercent;	// We do this 4 times as set up in CalculateItemCount() to smooth the scroll
761			fsckPrint(GPtr->context, hfsExtBTCheck);
762
763#if SHOW_ELAPSED_TIMES
764			gettimeofday( &myStartTime, &zone );
765#endif
766
767			/* Verify extent btree structure */
768			if ((result = ExtBTChk(GPtr)))
769				break;
770
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 );
777#endif
778
779			if ((result = CheckForStop(GPtr)))
780				break;
781
782			GPtr->itemsProcessed += GPtr->onePercent;	// We do this 4 times as set up in CalculateItemCount() to smooth the scroll
783
784			/* Check extents of bad block file */
785			if ((result = BadBlockFileExtentCheck(GPtr)))
786				break;
787			if ((result = CheckForStop(GPtr)))
788				break;
789
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);
793
794#if SHOW_ELAPSED_TIMES
795			gettimeofday( &myStartTime, &zone );
796#endif
797
798			if ( GPtr->chkLevel == kPartialCheck )
799			{
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;
805				}
806				if (GPtr->rebuildOptions & REBUILD_EXTENTS) {
807					GPtr->EBTStat |= S_RebuildBTree;
808				}
809				if (GPtr->rebuildOptions & REBUILD_ATTRIBUTE) {
810					GPtr->ABTStat |= S_RebuildBTree;
811				}
812				result = errRebuildBtree;
813				break;
814			}
815
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
819			 */
820			if ((result = CheckCatalogBTree(GPtr)))
821				break;
822
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 );
829#endif
830
831			if ((result = CheckForStop(GPtr)))
832				break;
833
834			if (scanflag == 0) {
835				fsckPrint(GPtr->context, hfsCatHierCheck);
836
837#if SHOW_ELAPSED_TIMES
838				gettimeofday( &myStartTime, &zone );
839#endif
840
841				/* Check catalog hierarchy */
842				if ((result = CatHChk(GPtr)))
843					break;
844
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 );
851#endif
852
853				if ((result = CheckForStop(GPtr)))
854					break;
855
856				if (VolumeObjectIsHFSX(GPtr)) {
857					result = CheckFolderCount(GPtr);
858					if (result)
859						break;
860
861					if ((result=CheckForStop(GPtr)))
862						break;
863				}
864			}
865			/* Check attribute btree.  The function accounts for all extents
866			 * for extended attributes whose values are stored in
867			 * allocation blocks
868			 */
869			if ((result = AttrBTChk(GPtr)))
870				break;
871
872			if ((result = CheckForStop(GPtr)))
873				break;
874
875			/*
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.
883			 */
884			if (GPtr->VIStat & S_OverlappingExtents) {
885				/* Find original files involved in overlapped extents */
886				result = FindOrigOverlapFiles(GPtr);
887				if (result) {
888					break;
889				}
890
891				/* Print all unique overlapping file IDs and paths */
892				(void) PrintOverlapFiles(GPtr);
893			}
894
895			if (scanflag == 0) {
896				/* Directory inodes store first link information in
897				 * an extended attribute.  Therefore start directory
898				 * hard link check after extended attribute checks.
899				 */
900				result = dirhardlink_check(GPtr);
901				/* On error or unrepairable corruption, stop the verification */
902				if ((result != 0) || (GPtr->CatStat & S_LinkErrNoRepair)) {
903					if (result == 0) {
904						result = -1;
905					}
906
907					break;
908				}
909			}
910
911			fsckPrint(GPtr->context, hfsVolBitmapCheck);
912
913#if SHOW_ELAPSED_TIMES
914			gettimeofday( &myStartTime, &zone );
915#endif
916
917			/* Compare in-memory volume bitmap with on-disk bitmap */
918			if ((result = CheckVolumeBitMap(GPtr, false)))
919				break;
920
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 );
927#endif
928
929			if ((result = CheckForStop(GPtr)))
930				break;
931
932			fsckPrint(GPtr->context, hfsVolInfoCheck);
933
934#if SHOW_ELAPSED_TIMES
935			gettimeofday( &myStartTime, &zone );
936#endif
937
938			/* Verify volume level information */
939			if ((result = VInfoChk(GPtr)))
940				break;
941
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 );
948#endif
949
950			stat =	GPtr->VIStat  | GPtr->ABTStat | GPtr->EBTStat | GPtr->CBTStat |
951					GPtr->CatStat | GPtr->JStat;
952
953			if ( stat != 0 )
954			{
955				if ( (GPtr->RepLevel == repairLevelNoProblemsFound) || (GPtr->RepLevel == repairLevelVolumeRecoverable) )
956				{
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;
963					else
964						GPtr->RepLevel =  repairLevelVolumeRecoverable;
965				}
966			}
967			else if ( GPtr->RepLevel == repairLevelNoProblemsFound )
968			{
969			}
970
971			GPtr->itemsProcessed = GPtr->itemsToProcess;
972			result = CheckForStop(GPtr);				//	one last check for modified volume
973			break;
974		}
975
976		case scavRepair:									//	REPAIR
977		{
978			if ( IsBlueBoxSharedDrive( GPtr->DrvPtr ) )
979				break;
980			if ( ( result = CheckForStop(GPtr) ) )
981				break;
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
988			} else {
989				fsckPrint(GPtr->context, fsckRepairingVolume);
990				if (embedded == 1 && debug == 0)
991					fsckPrint(GPtr->context, fsckLimitedRepairs);
992			}
993			result = RepairVolume( GPtr );
994			break;
995		}
996
997		case scavTerminate:									//	CLEANUP AFTER SCAVENGE
998		{
999			result = ScavTerm(GPtr);
1000			break;
1001		}
1002	}													//	end ScavOp switch
1003
1004
1005	//
1006	//	Map internal error codes to scavenger result codes
1007	//
1008	if ( (result < 0) || (result > Max_RCode) )
1009	{
1010		switch ( ScavOp )
1011		{
1012			case scavInitialize:
1013			case scavVerify:
1014				if ( result == ioErr )
1015					result = R_RdErr;
1016				else if ( result == errRebuildBtree )
1017				{
1018					GPtr->RepLevel = repairLevelCatalogBtreeRebuild;
1019					break;
1020				}
1021				else
1022					result = R_VFail;
1023				GPtr->RepLevel = repairLevelUnrepairable;
1024				break;
1025			case scavRepair:
1026				result = R_RFail;
1027				break;
1028			default:
1029				result = R_IntErr;
1030		}
1031	}
1032
1033	GPtr->ScavRes = result;
1034
1035	*ScavRes = result;
1036
1037}	//	end of ScavCtrl
1038
1039
1040
1041/*------------------------------------------------------------------------------
1042
1043Function: 	CheckForStop
1044
1045Function:	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.
1049
1050Input:		GPtr		-  	pointer to scavenger global area
1051
1052Output:		Function result:
1053						0 - ok to continue
1054						R_UInt - STOP button hit
1055						R_Modified - another app has touched the volume
1056-------------------------------------------------------------------------------*/
1057
1058short CheckForStop( SGlob *GPtr )
1059{
1060	OSErr	err			= noErr;						//	Initialize err to noErr
1061	long	ticks		= TickCount();
1062	UInt16	dfaStage	= (UInt16) GetDFAStage();
1063
1064        //plog("%d, %d", dfaStage, kAboutToRepairStage);
1065
1066	//if ( ((ticks - 10) > GPtr->lastTickCount) || (dfaStage == kAboutToRepairStage) )			//	To reduce cursor flicker on fast machines, call through on a timed interval
1067	//{
1068		if ( GPtr->userCancelProc != nil )
1069		{
1070			UInt64	progress = 0;
1071			Boolean	progressChanged;
1072		//	UInt16	elapsedTicks;
1073
1074			if ( dfaStage != kRepairStage )
1075			{
1076				progress = GPtr->itemsProcessed * 100;
1077				progress /= GPtr->itemsToProcess;
1078				progressChanged = ( progress != GPtr->lastProgress );
1079				GPtr->lastProgress = progress;
1080
1081				#if( DisplayTimeRemaining )
1082					if ( (progressChanged) && (progress > 5) )
1083					{
1084						elapsedTicks	=	TickCount() - GPtr->startTicks;
1085						GPtr->secondsRemaining	= ( ( ( 100 * elapsedTicks ) / progress ) - elapsedTicks ) / 60;
1086					}
1087				#endif
1088				err = CallUserCancelProc( GPtr->userCancelProc, (UInt16)progress, (UInt16)GPtr->secondsRemaining, progressChanged, dfaStage, GPtr->context, GPtr->scanCount );
1089			}
1090			else
1091			{
1092				(void) CallUserCancelProc( GPtr->userCancelProc, (UInt16)progress, 0, false, dfaStage, GPtr->context, GPtr->scanCount );
1093			}
1094
1095		}
1096
1097		if ( err != noErr )
1098			err = R_UInt;
1099	#if 0
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
1103	#endif
1104		GPtr->lastTickCount	= ticks;
1105	//}
1106
1107	return ( err );
1108}
1109
1110
1111
1112/*------------------------------------------------------------------------------
1113
1114Function:	ScavSetUp - (Scavenger Set Up)
1115
1116Function:	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.
1120
1121Input:		GPtr		-	pointer to scavenger global area
1122
1123Output:		ScavSetUp	-	function result:
1124								0	= no error
1125								n 	= error code
1126------------------------------------------------------------------------------*/
1127
1128struct ScavStaticStructures {
1129	SVCB				vcb;
1130	SFCB				fcbList[6];
1131	BTreeControlBlock	btcb[4];			// 4 btcb's
1132	SBTPT				btreePath;			// scavenger BTree path table
1133};
1134typedef struct ScavStaticStructures ScavStaticStructures;
1135
1136
1137static int ScavSetUp( SGlob *GPtr)
1138{
1139	OSErr  err;
1140	SVCB * vcb;
1141#if !BSD
1142	DrvQEl	*drvP;
1143	short	ioRefNum;
1144#endif
1145
1146	GPtr->MinorRepairsP = nil;
1147
1148	GPtr->itemsProcessed = 0;
1149	GPtr->lastProgress = 0;
1150	GPtr->startTicks = TickCount();
1151
1152	//
1153	//	allocate the static data structures (VCB, FCB's, BTCB'S, DPT and BTPT)
1154	//
1155 	{
1156		ScavStaticStructures	*pointer;
1157
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) );
1163			}
1164			return( R_NoMem );
1165		}
1166		GPtr->scavStaticPtr = pointer;
1167
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 );
1173			}
1174			return( R_NoMem );
1175		}
1176		GPtr->dirPathCount = CMMaxDepth;
1177
1178		GPtr->calculatedVCB = vcb	= &pointer->vcb;
1179		vcb->vcbGPtr = GPtr;
1180
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];
1188
1189		GPtr->calculatedExtentsBTCB		= &pointer->btcb[0];
1190		GPtr->calculatedCatalogBTCB		= &pointer->btcb[1];
1191		GPtr->calculatedRepairBTCB		= &pointer->btcb[2];
1192		GPtr->calculatedAttributesBTCB	= &pointer->btcb[3];
1193
1194		GPtr->BTPTPtr					= (SBTPT*) &pointer->btreePath;
1195	}
1196
1197
1198	SetDFAStage( kVerifyStage );
1199	SetFCBSPtr( GPtr->FCBAPtr );
1200
1201	//
1202	//	locate the driveQ element for drive being scavenged
1203	//
1204 	GPtr->DrvPtr	= 0;							//	<8> initialize so we can know if drive disappears
1205
1206	//
1207	//	Set up Real structures
1208	//
1209#if !BSD
1210	err = FindDrive( &ioRefNum, &(GPtr->DrvPtr), GPtr->DrvNum );
1211#endif
1212	if ( IsBlueBoxSharedDrive( GPtr->DrvPtr ) )
1213		return noErr;
1214
1215	err = GetVolumeFeatures( GPtr );				//	Sets up GPtr->volumeFeatures and GPtr->realVCB
1216
1217#if !BSD
1218	if ( GPtr->DrvPtr == NULL )						//	<8> drive is no longer there!
1219		return ( R_NoVol );
1220	else
1221		drvP = GPtr->DrvPtr;
1222
1223	//	Save current value of vcbWrCnt, to detect modifications to volume by other apps etc
1224	if ( GPtr->volumeFeatures & volumeIsMountedMask )
1225	{
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
1228	}
1229#endif
1230
1231	//	Finish initializing the VCB
1232
1233	//	The calculated structures
1234#if BSD
1235	InitBlockCache(vcb);
1236	vcb->vcbDriveNumber = GPtr->DrvNum;
1237	vcb->vcbDriverReadRef = GPtr->DrvNum;
1238	vcb->vcbDriverWriteRef = -1;	/* XXX need to get real fd here */
1239#else
1240	vcb->vcbDriveNumber = drvP->dQDrive;
1241	vcb->vcbDriverReadRef = drvP->dQRefNum;
1242	vcb->vcbDriverWriteRef = drvP->dQRefNum;
1243	vcb->vcbFSID = drvP->dQFSID;
1244#endif
1245//	vcb->vcbVRefNum = Vol_RefN;
1246
1247	//
1248	//	finish initializing the FCB's
1249	//
1250	{
1251		SFCB *fcb;
1252
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;
1259
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;
1266
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;
1273
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;
1280
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;
1287	}
1288
1289	//	finish initializing the BTCB's
1290	{
1291		BTreeControlBlock	*btcb;
1292
1293		btcb			= GPtr->calculatedExtentsBTCB;		// calculatedExtentsBTCB
1294		btcb->fcbPtr		= GPtr->calculatedExtentsFCB;
1295		btcb->getBlockProc	= GetFileBlock;
1296		btcb->releaseBlockProc	= ReleaseFileBlock;
1297		btcb->setEndOfForkProc	= SetEndOfForkProc;
1298
1299		btcb			= GPtr->calculatedCatalogBTCB;		// calculatedCatalogBTCB
1300		btcb->fcbPtr		= GPtr->calculatedCatalogFCB;
1301		btcb->getBlockProc	= GetFileBlock;
1302		btcb->releaseBlockProc	= ReleaseFileBlock;
1303		btcb->setEndOfForkProc	= SetEndOfForkProc;
1304
1305		btcb			= GPtr->calculatedAttributesBTCB;	// calculatedAttributesBTCB
1306		btcb->fcbPtr		= GPtr->calculatedAttributesFCB;
1307		btcb->getBlockProc	= GetFileBlock;
1308		btcb->releaseBlockProc	= ReleaseFileBlock;
1309		btcb->setEndOfForkProc	= SetEndOfForkProc;
1310	}
1311
1312
1313	//
1314	//	Initialize some global stuff
1315	//
1316
1317	GPtr->RepLevel			= repairLevelNoProblemsFound;
1318	GPtr->ErrCode			= 0;
1319	GPtr->IntErr			= noErr;
1320	GPtr->VIStat			= 0;
1321	GPtr->ABTStat			= 0;
1322	GPtr->EBTStat			= 0;
1323	GPtr->CBTStat			= 0;
1324	GPtr->CatStat			= 0;
1325	GPtr->VeryMinorErrorsStat	= 0;
1326	GPtr->JStat			= 0;
1327
1328	/* Assume that the volume is dirty unmounted */
1329	GPtr->cleanUnmount		= false;
1330
1331 	//
1332 	// Initialize VolumeObject
1333 	//
1334
1335 	InitializeVolumeObject( GPtr );
1336
1337	/* Check if the volume type of initialized object is valid.  If not, return error */
1338	if (VolumeObjectIsValid() == false) {
1339		return (R_BadSig);
1340	}
1341
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 );
1347		}
1348		return( R_NoMem );
1349	}
1350
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));
1355
1356	return( noErr );
1357
1358} /* end of ScavSetUp */
1359
1360
1361
1362
1363/*------------------------------------------------------------------------------
1364
1365Function:	ScavTerm - (Scavenge Termination))
1366
1367Function:	Terminates the current scavenging operation.  Memory for the
1368			VCB, FCBs, BTCBs, volume bit map, and BTree bit maps is
1369			released.
1370
1371Input:		GPtr		-	pointer to scavenger global area
1372
1373Output:		ScavTerm	-	function result:
1374								0	= no error
1375								n 	= error code
1376------------------------------------------------------------------------------*/
1377
1378static int ScavTerm( SGlobPtr GPtr )
1379{
1380	SFCB			*fcbP;
1381	BTreeControlBlock	*btcbP;
1382	RepairOrderPtr		rP;
1383	OSErr			err;
1384	ExtentsTable 		**extentsTableH;
1385	ExtentInfo		*curExtentInfo;
1386	int			i;
1387
1388	(void) BitMapCheckEnd();
1389
1390	while( (rP = GPtr->MinorRepairsP) != nil )		//	loop freeing leftover (undone) repair orders
1391	{
1392		GPtr->MinorRepairsP = rP->link;				//	(in case repairs were not made)
1393		DisposeMemory(rP);
1394		err = MemError();
1395	}
1396
1397	if( GPtr->validFilesList != nil )
1398		DisposeHandle( (Handle) GPtr->validFilesList );
1399
1400	if( GPtr->overlappedExtents != nil ) {
1401 		extentsTableH = GPtr->overlappedExtents;
1402
1403		/* Overlapped extents list also allocated memory for attribute name */
1404		for (i=0; i<(**extentsTableH).count; i++) {
1405			curExtentInfo = &((**extentsTableH).extentInfo[i]);
1406
1407			/* Deallocate memory for attribute name, if any */
1408			if (curExtentInfo->attrname) {
1409				free(curExtentInfo->attrname);
1410			}
1411		}
1412
1413		DisposeHandle( (Handle) GPtr->overlappedExtents );
1414	}
1415
1416	if( GPtr->fileIdentifierTable != nil )
1417		DisposeHandle( (Handle) GPtr->fileIdentifierTable );
1418
1419	if( GPtr->calculatedVCB == nil )								//	already freed?
1420		return( noErr );
1421
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
1424	if ( fcbP != nil )
1425	{
1426		btcbP = (BTreeControlBlock*)fcbP->fcbBtree;
1427		if ( btcbP != nil)
1428		{
1429			if( btcbP->refCon != nil )
1430			{
1431				if(((BTreeExtensionsRec*)btcbP->refCon)->BTCBMPtr != nil)
1432				{
1433					DisposeMemory(((BTreeExtensionsRec*)btcbP->refCon)->BTCBMPtr);
1434					err = MemError();
1435				}
1436				DisposeMemory( (Ptr)btcbP->refCon );
1437				err = MemError();
1438				btcbP->refCon = nil;
1439			}
1440
1441			fcbP = GPtr->calculatedCatalogFCB;	//	release catalog BTree bit map
1442			btcbP = (BTreeControlBlock*)fcbP->fcbBtree;
1443
1444			if( btcbP->refCon != nil )
1445			{
1446				if(((BTreeExtensionsRec*)btcbP->refCon)->BTCBMPtr != nil)
1447				{
1448					DisposeMemory(((BTreeExtensionsRec*)btcbP->refCon)->BTCBMPtr);
1449					err = MemError();
1450				}
1451				DisposeMemory( (Ptr)btcbP->refCon );
1452				err = MemError();
1453				btcbP->refCon = nil;
1454			}
1455		}
1456	}
1457
1458	DisposeMemory(GPtr->DirPTPtr);
1459	DisposeMemory((ScavStaticStructures *)GPtr->scavStaticPtr);
1460	GPtr->scavStaticPtr = nil;
1461	GPtr->calculatedVCB = nil;
1462
1463	return( noErr );
1464}
1465
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"
1469
1470/*------------------------------------------------------------------------------
1471
1472Routine:	IsBlueBoxSharedDrive
1473
1474Function: 	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.
1477
1478Input:		Arg 1	- DQE pointer
1479
1480Output:		D0.L -	0 if drive not to be used
1481					1 otherwise
1482------------------------------------------------------------------------------*/
1483
1484struct IconAndStringRec {
1485	char		icon[ 256 ];
1486	Str255		string;
1487};
1488typedef struct IconAndStringRec IconAndStringRec, * IconAndStringRecPtr;
1489
1490
1491Boolean IsBlueBoxSharedDrive ( DrvQElPtr dqPtr )
1492{
1493#if 0
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;
1501
1502	if ( dqPtr == NULL )
1503		return false;
1504
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))
1509	{
1510		if (((driverDCtlPtr->dCtlFlags) & Is_Ram_Based_Mask) == 0)
1511		{
1512			drvrHeaderPtr = (DRVRHeaderPtr)driverDCtlPtr->dCtlDriver;
1513		}
1514		else
1515		{
1516			//��� bek - lock w/o unlock/restore?  should be getstate/setstate?
1517			HLock((Handle)(driverDCtlPtr)->dCtlDriver);
1518			drvrHeaderPtr = (DRVRHeaderPtr)*((Handle)(driverDCtlPtr->dCtlDriver));
1519
1520		}
1521		driverName = (StringPtr)&(drvrHeaderPtr->drvrName);
1522		if (!(IdenticalString(driverName,blueBoxSharedDriverName,nil)))
1523		{
1524			return( true );
1525		}
1526
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)))
1530		{
1531			CntrlParam			paramBlock;
1532
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
1538
1539			// If PBControl(kDriveIcon) returns an error then the driver is not the Blue Box driver.
1540			if ( noErr == PBControlSync( (ParmBlkPtr) &paramBlock ) )
1541			{
1542				IconAndStringRecPtr		iconAndStringRecPtr;
1543				StringPtr				whereStringPtr;
1544
1545				iconAndStringRecPtr = * (IconAndStringRecPtr*) & paramBlock.csParam;
1546				whereStringPtr = (StringPtr) & iconAndStringRecPtr->string;
1547				if (!(IdenticalString(whereStringPtr,blueBoxFloppyWhereString,nil)))
1548				{
1549					return( true );
1550				}
1551			}
1552		}
1553	}
1554#endif
1555
1556	return false;
1557}
1558
1559
1560
1561
1562/*------------------------------------------------------------------------------
1563
1564Function:	printVerifyStatus - (Print Verify Status)
1565
1566Function:	Prints out the Verify Status words.
1567
1568Input:		GPtr	-	pointer to scavenger global area
1569
1570Output:		None.
1571------------------------------------------------------------------------------*/
1572static
1573void printVerifyStatus(SGlobPtr GPtr)
1574{
1575    UInt32 stat;
1576
1577    stat = GPtr->VIStat | GPtr->ABTStat | GPtr->EBTStat | GPtr->CBTStat | GPtr->CatStat;
1578
1579    if ( stat != 0 ) {
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);
1584    }
1585}
1586