]> git.saurik.com Git - apple/hfs.git/blob - fsck_hfs/dfalib/SControl.c
hfs-226.1.1.tar.gz
[apple/hfs.git] / fsck_hfs / dfalib / SControl.c
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 */
62 extern const unsigned char fsck_hfsVersionString[];
63
64 int gGUIControl;
65 extern char lflag;
66
67
68 // Static function prototypes
69
70 static void printVerifyStatus( SGlobPtr GPtr );
71 static Boolean IsBlueBoxSharedDrive ( DrvQElPtr dqPtr );
72 static int ScavSetUp( SGlobPtr GPtr );
73 static 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
77 int 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
101 static 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 */
109 static int
110 isMinorError(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
209 External
210 Routines: CheckHFS - Controls the scavenging process.
211
212 ------------------------------------------------------------------------------*/
213
214 static jmp_buf envBuf;
215 int
216 CheckHFS( const char *rdevnode, int fsReadRef, int fsWriteRef, int checkLevel,
217 int repairLevel, fsck_ctx_t fsckContext, int lostAndFoundMode,
218 int canWrite, int *modified, int liveMode, int rebuildOptions )
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 }
303 DoAgain:
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
370 EarlyExitLabel:
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
482 termScav:
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
534 Function: ScavCtrl - (Scavenger Control)
535
536 Function: Controls the scavenging process. Interfaces with the User Interface
537 Layer (written in PASCAL).
538
539 Input: 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
549 Output: ScavRes - scavenge result code (R_xxx, or 0 if no error)
550
551 ------------------------------------------------------------------------------*/
552
553 void 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
1043 Function: CheckForStop
1044
1045 Function: Checks for the user hitting the "STOP" button during a scavenge,
1046 which interrupts the operation. Additionally, we monitor the write
1047 count of a mounted volume, to be sure that the volume is not
1048 modified by another app while we scavenge.
1049
1050 Input: GPtr - pointer to scavenger global area
1051
1052 Output: Function result:
1053 0 - ok to continue
1054 R_UInt - STOP button hit
1055 R_Modified - another app has touched the volume
1056 -------------------------------------------------------------------------------*/
1057
1058 short 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
1114 Function: ScavSetUp - (Scavenger Set Up)
1115
1116 Function: Sets up scavenger globals for a new scavenge operation. Memory is
1117 allocated for the Scavenger's static data structures (VCB, FCBs,
1118 BTCBs, and TPTs). The contents of the data structures are
1119 initialized to zero.
1120
1121 Input: GPtr - pointer to scavenger global area
1122
1123 Output: ScavSetUp - function result:
1124 0 = no error
1125 n = error code
1126 ------------------------------------------------------------------------------*/
1127
1128 struct ScavStaticStructures {
1129 SVCB vcb;
1130 SFCB fcbList[6];
1131 BTreeControlBlock btcb[4]; // 4 btcb's
1132 SBTPT btreePath; // scavenger BTree path table
1133 };
1134 typedef struct ScavStaticStructures ScavStaticStructures;
1135
1136
1137 static 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
1365 Function: ScavTerm - (Scavenge Termination))
1366
1367 Function: Terminates the current scavenging operation. Memory for the
1368 VCB, FCBs, BTCBs, volume bit map, and BTree bit maps is
1369 released.
1370
1371 Input: GPtr - pointer to scavenger global area
1372
1373 Output: ScavTerm - function result:
1374 0 = no error
1375 n = error code
1376 ------------------------------------------------------------------------------*/
1377
1378 static 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
1472 Routine: IsBlueBoxSharedDrive
1473
1474 Function: Given a DQE address, return a boolean that determines whether
1475 or not a drive is a Blue Box disk being accessed via Shared mode.
1476 Such drives do not support i/o and cannot be scavenged.
1477
1478 Input: Arg 1 - DQE pointer
1479
1480 Output: D0.L - 0 if drive not to be used
1481 1 otherwise
1482 ------------------------------------------------------------------------------*/
1483
1484 struct IconAndStringRec {
1485 char icon[ 256 ];
1486 Str255 string;
1487 };
1488 typedef struct IconAndStringRec IconAndStringRec, * IconAndStringRecPtr;
1489
1490
1491 Boolean 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
1564 Function: printVerifyStatus - (Print Verify Status)
1565
1566 Function: Prints out the Verify Status words.
1567
1568 Input: GPtr - pointer to scavenger global area
1569
1570 Output: None.
1571 ------------------------------------------------------------------------------*/
1572 static
1573 void 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 }