]> git.saurik.com Git - apple/hfs.git/blob - fsck_hfs/dfalib/SControl.c
hfs-366.30.3.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 if (err != 0 && embedded == 1) {
498 Buf_t *buf = NULL;
499 off_t offset = 1024;
500 uint32_t len = 512; // the size of an HFS+ volume header
501 int rv = CacheRead(&fscache, offset, len, &buf);
502 if (rv == 0) {
503 fprintf(stderr, "Offset %llu length %u:\n", offset, len);
504 DumpData(buf->Buffer, len);
505 CacheRelease(&fscache, buf, 0);
506 } else {
507 fprintf(stderr, "%s(%d): rv = %d\n", __FUNCTION__, __LINE__, rv);
508 }
509 fflush(stderr);
510 }
511
512 // If we have write access on volume and we are allowed to write,
513 // mark the volume clean/dirty
514 if ((fsWriteRef != -1) && (dataArea.canWrite != 0)) {
515 Boolean update;
516 if (scavError) {
517 // Mark volume dirty
518 CheckForClean(&dataArea, kMarkVolumeDirty, &update);
519 } else {
520 // Mark volume clean
521 CheckForClean(&dataArea, kMarkVolumeClean, &update);
522 }
523 if (update) {
524 /* Report back that volume was modified */
525 *modified = 1;
526 }
527 }
528 ScavCtrl( &dataArea, scavTerminate, &temp ); // Note: use a temp var so that real scav error can be returned
529
530 if (fsckContext) {
531 fsckPrint( fsckContext, fsckProgress, 100); // End each run with 100% message, if desired
532 draw_progress(100);
533 end_progress();
534 }
535 if (exitEarly && majorErrors)
536 err = MAJOREXIT;
537
538 if (msgCounts) {
539 free(msgCounts);
540 }
541
542 return( err );
543 }
544
545
546 /*------------------------------------------------------------------------------
547
548 Function: ScavCtrl - (Scavenger Control)
549
550 Function: Controls the scavenging process. Interfaces with the User Interface
551 Layer (written in PASCAL).
552
553 Input: ScavOp - scavenging operation to be performed:
554
555 scavInitialize = start initial volume check
556 scavVerify = start verify
557 scavRepair = start repair
558 scavTerminate = finished scavenge
559
560 GPtr - pointer to scavenger global area
561
562
563 Output: ScavRes - scavenge result code (R_xxx, or 0 if no error)
564
565 ------------------------------------------------------------------------------*/
566
567 void ScavCtrl( SGlobPtr GPtr, UInt32 ScavOp, short *ScavRes )
568 {
569 OSErr result;
570 unsigned int stat;
571 #if SHOW_ELAPSED_TIMES
572 struct timeval myStartTime;
573 struct timeval myEndTime;
574 struct timeval myElapsedTime;
575 struct timezone zone;
576 #endif
577
578 //
579 // initialize some stuff
580 //
581 result = noErr; // assume good status
582 *ScavRes = 0;
583 GPtr->ScavRes = 0;
584
585 //
586 // dispatch next scavenge operation
587 //
588 switch ( ScavOp )
589 {
590 case scavInitialize: // INITIAL VOLUME CHECK
591 {
592 Boolean modified;
593 int clean;
594
595 if ( ( result = ScavSetUp( GPtr ) ) ) // set up BEFORE CheckForStop
596 break;
597 if ( IsBlueBoxSharedDrive( GPtr->DrvPtr ) )
598 break;
599 if ( ( result = CheckForStop( GPtr ) ) ) // in order to initialize wrCnt
600 break;
601
602 /* Call for all chkLevel options and check return value only
603 * for kDirtyCheck for preen option and kNeverCheck for quick option
604 */
605 clean = CheckForClean(GPtr, kCheckVolume, &modified);
606 if ((GPtr->chkLevel == kDirtyCheck) || (GPtr->chkLevel == kNeverCheck)) {
607 if (clean == 1) {
608 /* volume was unmounted cleanly */
609 GPtr->cleanUnmount = true;
610 break;
611 }
612
613 if (GPtr->chkLevel == kNeverCheck) {
614 if (clean == -1)
615 result = R_BadSig;
616 else if (clean == 0) {
617 /*
618 * We lie for journaled file systems since
619 * they get cleaned up in mount by replaying
620 * the journal.
621 * Note: CheckIfJournaled will return negative
622 * if it finds lastMountedVersion = FSK!.
623 */
624 if (CheckIfJournaled(GPtr, false))
625 GPtr->cleanUnmount = true;
626 else
627 result = R_Dirty;
628 }
629 break;
630 }
631 }
632
633 if (CheckIfJournaled(GPtr, false)
634 && GPtr->chkLevel != kForceCheck
635 && !(GPtr->chkLevel == kPartialCheck && GPtr->repairLevel == kForceRepairs)
636 && !(GPtr->chkLevel == kAlwaysCheck && GPtr->repairLevel == kMajorRepairs)) {
637 break;
638 }
639
640 if (GPtr->liveVerifyState) {
641 fsckPrint(GPtr->context, hfsLiveVerifyCheck);
642 } else if (GPtr->canWrite == 0 && nflag == 0) {
643 fsckPrint(GPtr->context, hfsVerifyVolWithWrite);
644 }
645
646 /*
647 * In the first pass, if fsck_hfs is verifying a
648 * journaled volume, and it's not a live verification,
649 * check to see if the journal is empty. If it is not,
650 * flag it as a journal error, and print a message.
651 * (A live verify will almost certainly have a non-empty
652 * journal, but that should be safe in this case due
653 * to the freeze command flushing everything.)
654 */
655 if ((GPtr->scanCount == 0) &&
656 (CheckIfJournaled(GPtr, true) == 1) &&
657 (GPtr->canWrite == 0 || GPtr->writeRef == -1) &&
658 (lflag == 0)) {
659 fsckJournalInfo_t jnlInfo = { 0 };
660 UInt64 numBlocks;
661 UInt32 blockSize;
662 jnlInfo.jnlfd = -1;
663
664 if (IsJournalEmpty(GPtr, &jnlInfo) == 0) {
665 // disable_journal can currently only be set with debug enabled
666 if (disable_journal) {
667 fsckPrint(GPtr->context, E_DirtyJournal);
668 GPtr->JStat |= S_DirtyJournal;
669 } else {
670 (void)GetDeviceSize(GPtr->calculatedVCB->vcbDriveNumber, &numBlocks, &blockSize);
671 #if 0
672 // For debugging the cache. WAY to verbose to run with even normal debug
673 if (debug) {
674 printf("Before journal replay\n");
675 dumpCache(&fscache);
676 }
677 #endif
678 if (journal_open(jnlInfo.jnlfd,
679 jnlInfo.jnlOffset,
680 jnlInfo.jnlSize,
681 blockSize,
682 0,
683 jnlInfo.name,
684 ^(off_t start, void *data, size_t len) {
685 Buf_t *buf;
686 int rv;
687 rv = CacheRead(&fscache, start, (int)len, &buf);
688 if (rv != 0)
689 abort();
690 memcpy(buf->Buffer, data, len);
691 rv = CacheWrite(&fscache, buf, 0, kLockWrite);
692 if (rv != 0)
693 abort();
694 return 0;}
695 ) == -1) {
696 fsckPrint(GPtr->context, E_DirtyJournal);
697 GPtr->JStat |= S_DirtyJournal;
698 } else if (debug) {
699 plog("Journal replay simulation succeeded\n");
700 #if 0
701 // Still way too verbose to run
702 dumpCache(&fscache);
703 #endif
704 }
705 }
706 } else {
707 if (debug)
708 plog("Journal is empty\n");
709 }
710 if (jnlInfo.jnlfd != -1)
711 close(jnlInfo.jnlfd);
712 if (jnlInfo.name != NULL)
713 free(jnlInfo.name);
714 }
715
716 result = IVChk( GPtr );
717
718 break;
719 }
720
721 case scavVerify: // VERIFY
722 {
723
724 #if SHOW_ELAPSED_TIMES
725 gettimeofday( &myStartTime, &zone );
726 #endif
727
728 /* Initialize volume bitmap structure */
729 if ( BitMapCheckBegin(GPtr) != 0)
730 break;
731
732 #if SHOW_ELAPSED_TIMES
733 gettimeofday( &myEndTime, &zone );
734 timersub( &myEndTime, &myStartTime, &myElapsedTime );
735 plog( "\n%s - BitMapCheck elapsed time \n", __FUNCTION__ );
736 plog( "########## secs %d msecs %d \n\n",
737 myElapsedTime.tv_sec, myElapsedTime.tv_usec );
738 #endif
739
740 if ( IsBlueBoxSharedDrive( GPtr->DrvPtr ) )
741 break;
742 if ( ( result = CheckForStop( GPtr ) ) )
743 break;
744
745 #if SHOW_ELAPSED_TIMES
746 gettimeofday( &myStartTime, &zone );
747 #endif
748
749 /* Create calculated BTree structures */
750 if ( ( result = CreateExtentsBTreeControlBlock( GPtr ) ) )
751 break;
752 if ( ( result = CreateCatalogBTreeControlBlock( GPtr ) ) )
753 break;
754 if ( ( result = CreateAttributesBTreeControlBlock( GPtr ) ) )
755 break;
756 if ( ( result = CreateExtendedAllocationsFCB( GPtr ) ) )
757 break;
758
759 #if SHOW_ELAPSED_TIMES
760 gettimeofday( &myEndTime, &zone );
761 timersub( &myEndTime, &myStartTime, &myElapsedTime );
762 plog( "\n%s - create control blocks elapsed time \n", __FUNCTION__ );
763 plog( ">>>>>>>>>>>>> secs %d msecs %d \n\n",
764 myElapsedTime.tv_sec, myElapsedTime.tv_usec );
765 #endif
766
767 // Now that preflight of the BTree structures is calculated, compute the CheckDisk items
768 CalculateItemCount( GPtr, &GPtr->itemsToProcess, &GPtr->onePercent );
769 GPtr->itemsProcessed += GPtr->onePercent; // We do this 4 times as set up in CalculateItemCount() to smooth the scroll
770
771 if ( ( result = VLockedChk( GPtr ) ) )
772 break;
773
774 GPtr->itemsProcessed += GPtr->onePercent; // We do this 4 times as set up in CalculateItemCount() to smooth the scroll
775 fsckPrint(GPtr->context, hfsExtBTCheck);
776
777 #if SHOW_ELAPSED_TIMES
778 gettimeofday( &myStartTime, &zone );
779 #endif
780
781 /* Verify extent btree structure */
782 if ((result = ExtBTChk(GPtr)))
783 break;
784
785 #if SHOW_ELAPSED_TIMES
786 gettimeofday( &myEndTime, &zone );
787 timersub( &myEndTime, &myStartTime, &myElapsedTime );
788 plog( "\n%s - ExtBTChk elapsed time \n", __FUNCTION__ );
789 plog( ">>>>>>>>>>>>> secs %d msecs %d \n\n",
790 myElapsedTime.tv_sec, myElapsedTime.tv_usec );
791 #endif
792
793 if ((result = CheckForStop(GPtr)))
794 break;
795
796 GPtr->itemsProcessed += GPtr->onePercent; // We do this 4 times as set up in CalculateItemCount() to smooth the scroll
797
798 /* Check extents of bad block file */
799 if ((result = BadBlockFileExtentCheck(GPtr)))
800 break;
801 if ((result = CheckForStop(GPtr)))
802 break;
803
804 GPtr->itemsProcessed += GPtr->onePercent; // We do this 4 times as set up in CalculateItemCount() to smooth the scroll
805 GPtr->itemsProcessed += GPtr->onePercent;
806 fsckPrint(GPtr->context, hfsCatBTCheck);
807
808 #if SHOW_ELAPSED_TIMES
809 gettimeofday( &myStartTime, &zone );
810 #endif
811
812 if ( GPtr->chkLevel == kPartialCheck )
813 {
814 /* skip the rest of the verify code path the first time */
815 /* through when we are rebuilding the catalog B-Tree file. */
816 /* we will be back here after the rebuild. */
817 if (GPtr->rebuildOptions & REBUILD_CATALOG) {
818 GPtr->CBTStat |= S_RebuildBTree;
819 }
820 if (GPtr->rebuildOptions & REBUILD_EXTENTS) {
821 GPtr->EBTStat |= S_RebuildBTree;
822 }
823 if (GPtr->rebuildOptions & REBUILD_ATTRIBUTE) {
824 GPtr->ABTStat |= S_RebuildBTree;
825 }
826 result = errRebuildBtree;
827 break;
828 }
829
830 /* Check catalog btree. For given fileID, the function accounts
831 * for all extents existing in catalog record as well as in
832 * overflow extent btree
833 */
834 if ((result = CheckCatalogBTree(GPtr)))
835 break;
836
837 #if SHOW_ELAPSED_TIMES
838 gettimeofday( &myEndTime, &zone );
839 timersub( &myEndTime, &myStartTime, &myElapsedTime );
840 plog( "\n%s - CheckCatalogBTree elapsed time \n", __FUNCTION__ );
841 plog( ">>>>>>>>>>>>> secs %d msecs %d \n\n",
842 myElapsedTime.tv_sec, myElapsedTime.tv_usec );
843 #endif
844
845 if ((result = CheckForStop(GPtr)))
846 break;
847
848 if (scanflag == 0) {
849 fsckPrint(GPtr->context, hfsCatHierCheck);
850
851 #if SHOW_ELAPSED_TIMES
852 gettimeofday( &myStartTime, &zone );
853 #endif
854
855 /* Check catalog hierarchy */
856 if ((result = CatHChk(GPtr)))
857 break;
858
859 #if SHOW_ELAPSED_TIMES
860 gettimeofday( &myEndTime, &zone );
861 timersub( &myEndTime, &myStartTime, &myElapsedTime );
862 plog( "\n%s - CatHChk elapsed time \n", __FUNCTION__ );
863 plog( ">>>>>>>>>>>>> secs %d msecs %d \n\n",
864 myElapsedTime.tv_sec, myElapsedTime.tv_usec );
865 #endif
866
867 if ((result = CheckForStop(GPtr)))
868 break;
869
870 if (VolumeObjectIsHFSX(GPtr)) {
871 result = CheckFolderCount(GPtr);
872 if (result)
873 break;
874
875 if ((result=CheckForStop(GPtr)))
876 break;
877 }
878 }
879 /* Check attribute btree. The function accounts for all extents
880 * for extended attributes whose values are stored in
881 * allocation blocks
882 */
883 if ((result = AttrBTChk(GPtr)))
884 break;
885
886 if ((result = CheckForStop(GPtr)))
887 break;
888
889 /*
890 * fsck_hfs has accounted for all valid allocation blocks by
891 * traversing all catalog records and attribute records.
892 * These traversals may have found overlapping extents. Note
893 * that the overlapping extents are detected in CaptureBitmapBits
894 * when it tries to set a bit corresponding to allocation block
895 * and finds that it is already set. Therefore fsck_hfs does not
896 * know the orignal file involved overlapped extents.
897 */
898 if (GPtr->VIStat & S_OverlappingExtents) {
899 /* Find original files involved in overlapped extents */
900 result = FindOrigOverlapFiles(GPtr);
901 if (result) {
902 break;
903 }
904
905 /* Print all unique overlapping file IDs and paths */
906 (void) PrintOverlapFiles(GPtr);
907 }
908
909 if (scanflag == 0) {
910 /* Directory inodes store first link information in
911 * an extended attribute. Therefore start directory
912 * hard link check after extended attribute checks.
913 */
914 result = dirhardlink_check(GPtr);
915 /* On error or unrepairable corruption, stop the verification */
916 if ((result != 0) || (GPtr->CatStat & S_LinkErrNoRepair)) {
917 if (result == 0) {
918 result = -1;
919 }
920
921 break;
922 }
923 }
924
925 fsckPrint(GPtr->context, hfsVolBitmapCheck);
926
927 #if SHOW_ELAPSED_TIMES
928 gettimeofday( &myStartTime, &zone );
929 #endif
930
931 /* Compare in-memory volume bitmap with on-disk bitmap */
932 if ((result = CheckVolumeBitMap(GPtr, false)))
933 break;
934
935 #if SHOW_ELAPSED_TIMES
936 gettimeofday( &myEndTime, &zone );
937 timersub( &myEndTime, &myStartTime, &myElapsedTime );
938 plog( "\n%s - CheckVolumeBitMap elapsed time \n", __FUNCTION__ );
939 plog( ">>>>>>>>>>>>> secs %d msecs %d \n\n",
940 myElapsedTime.tv_sec, myElapsedTime.tv_usec );
941 #endif
942
943 if ((result = CheckForStop(GPtr)))
944 break;
945
946 fsckPrint(GPtr->context, hfsVolInfoCheck);
947
948 #if SHOW_ELAPSED_TIMES
949 gettimeofday( &myStartTime, &zone );
950 #endif
951
952 /* Verify volume level information */
953 if ((result = VInfoChk(GPtr)))
954 break;
955
956 #if SHOW_ELAPSED_TIMES
957 gettimeofday( &myEndTime, &zone );
958 timersub( &myEndTime, &myStartTime, &myElapsedTime );
959 plog( "\n%s - VInfoChk elapsed time \n", __FUNCTION__ );
960 plog( ">>>>>>>>>>>>> secs %d msecs %d \n\n",
961 myElapsedTime.tv_sec, myElapsedTime.tv_usec );
962 #endif
963
964 stat = GPtr->VIStat | GPtr->ABTStat | GPtr->EBTStat | GPtr->CBTStat |
965 GPtr->CatStat | GPtr->JStat;
966
967 if ( stat != 0 )
968 {
969 if ( (GPtr->RepLevel == repairLevelNoProblemsFound) || (GPtr->RepLevel == repairLevelVolumeRecoverable) )
970 {
971 // 2200106, We isolate very minor errors so that if the volume cannot be unmounted
972 // CheckDisk will just return noErr
973 unsigned int minorErrors = (GPtr->CatStat & ~S_LockedDirName) |
974 GPtr->VIStat | GPtr->ABTStat | GPtr->EBTStat | GPtr->CBTStat | GPtr->JStat;
975 if ( minorErrors == 0 )
976 GPtr->RepLevel = repairLevelVeryMinorErrors;
977 else
978 GPtr->RepLevel = repairLevelVolumeRecoverable;
979 }
980 }
981 else if ( GPtr->RepLevel == repairLevelNoProblemsFound )
982 {
983 }
984
985 GPtr->itemsProcessed = GPtr->itemsToProcess;
986 result = CheckForStop(GPtr); // one last check for modified volume
987 break;
988 }
989
990 case scavRepair: // REPAIR
991 {
992 if ( IsBlueBoxSharedDrive( GPtr->DrvPtr ) )
993 break;
994 if ( ( result = CheckForStop(GPtr) ) )
995 break;
996 if ( GPtr->CBTStat & S_RebuildBTree
997 || GPtr->EBTStat & S_RebuildBTree
998 || GPtr->ABTStat & S_RebuildBTree) {
999 // fsckPrint(GPtr->context, hfsRebuildCatalogBTree);
1000 // fsckPrint(GPtr->context, hfsRebuildAttrBTree);
1001 // actually print nothing yet -- we print out when we are rebuilding the trees
1002 } else {
1003 fsckPrint(GPtr->context, fsckRepairingVolume);
1004 if (embedded == 1 && debug == 0)
1005 fsckPrint(GPtr->context, fsckLimitedRepairs);
1006 }
1007 result = RepairVolume( GPtr );
1008 break;
1009 }
1010
1011 case scavTerminate: // CLEANUP AFTER SCAVENGE
1012 {
1013 result = ScavTerm(GPtr);
1014 break;
1015 }
1016 } // end ScavOp switch
1017
1018
1019 //
1020 // Map internal error codes to scavenger result codes
1021 //
1022 if ( (result < 0) || (result > Max_RCode) )
1023 {
1024 switch ( ScavOp )
1025 {
1026 case scavInitialize:
1027 case scavVerify:
1028 if ( result == ioErr )
1029 result = R_RdErr;
1030 else if ( result == errRebuildBtree )
1031 {
1032 GPtr->RepLevel = repairLevelCatalogBtreeRebuild;
1033 break;
1034 }
1035 else
1036 result = R_VFail;
1037 GPtr->RepLevel = repairLevelUnrepairable;
1038 break;
1039 case scavRepair:
1040 result = R_RFail;
1041 break;
1042 default:
1043 result = R_IntErr;
1044 }
1045 }
1046
1047 GPtr->ScavRes = result;
1048
1049 *ScavRes = result;
1050
1051 } // end of ScavCtrl
1052
1053
1054
1055 /*------------------------------------------------------------------------------
1056
1057 Function: CheckForStop
1058
1059 Function: Checks for the user hitting the "STOP" button during a scavenge,
1060 which interrupts the operation. Additionally, we monitor the write
1061 count of a mounted volume, to be sure that the volume is not
1062 modified by another app while we scavenge.
1063
1064 Input: GPtr - pointer to scavenger global area
1065
1066 Output: Function result:
1067 0 - ok to continue
1068 R_UInt - STOP button hit
1069 R_Modified - another app has touched the volume
1070 -------------------------------------------------------------------------------*/
1071
1072 short CheckForStop( SGlob *GPtr )
1073 {
1074 OSErr err = noErr; // Initialize err to noErr
1075 long ticks = TickCount();
1076 UInt16 dfaStage = (UInt16) GetDFAStage();
1077
1078 //plog("%d, %d", dfaStage, kAboutToRepairStage);
1079
1080 //if ( ((ticks - 10) > GPtr->lastTickCount) || (dfaStage == kAboutToRepairStage) ) // To reduce cursor flicker on fast machines, call through on a timed interval
1081 //{
1082 if ( GPtr->userCancelProc != nil )
1083 {
1084 UInt64 progress = 0;
1085 Boolean progressChanged;
1086 // UInt16 elapsedTicks;
1087
1088 if ( dfaStage != kRepairStage )
1089 {
1090 progress = GPtr->itemsProcessed * 100;
1091 progress /= GPtr->itemsToProcess;
1092 progressChanged = ( progress != GPtr->lastProgress );
1093 GPtr->lastProgress = progress;
1094
1095 #if( DisplayTimeRemaining )
1096 if ( (progressChanged) && (progress > 5) )
1097 {
1098 elapsedTicks = TickCount() - GPtr->startTicks;
1099 GPtr->secondsRemaining = ( ( ( 100 * elapsedTicks ) / progress ) - elapsedTicks ) / 60;
1100 }
1101 #endif
1102 err = CallUserCancelProc( GPtr->userCancelProc, (UInt16)progress, (UInt16)GPtr->secondsRemaining, progressChanged, dfaStage, GPtr->context, GPtr->scanCount );
1103 }
1104 else
1105 {
1106 (void) CallUserCancelProc( GPtr->userCancelProc, (UInt16)progress, 0, false, dfaStage, GPtr->context, GPtr->scanCount );
1107 }
1108
1109 }
1110
1111 if ( err != noErr )
1112 err = R_UInt;
1113 #if 0
1114 if ( GPtr->realVCB ) // If the volume is mounted
1115 if ( GPtr->realVCB->vcbWrCnt != GPtr->wrCnt )
1116 err = R_Modified; // Its been modified behind our back
1117 #endif
1118 GPtr->lastTickCount = ticks;
1119 //}
1120
1121 return ( err );
1122 }
1123
1124
1125
1126 /*------------------------------------------------------------------------------
1127
1128 Function: ScavSetUp - (Scavenger Set Up)
1129
1130 Function: Sets up scavenger globals for a new scavenge operation. Memory is
1131 allocated for the Scavenger's static data structures (VCB, FCBs,
1132 BTCBs, and TPTs). The contents of the data structures are
1133 initialized to zero.
1134
1135 Input: GPtr - pointer to scavenger global area
1136
1137 Output: ScavSetUp - function result:
1138 0 = no error
1139 n = error code
1140 ------------------------------------------------------------------------------*/
1141
1142 struct ScavStaticStructures {
1143 SVCB vcb;
1144 SFCB fcbList[6];
1145 BTreeControlBlock btcb[4]; // 4 btcb's
1146 SBTPT btreePath; // scavenger BTree path table
1147 };
1148 typedef struct ScavStaticStructures ScavStaticStructures;
1149
1150
1151 static int ScavSetUp( SGlob *GPtr)
1152 {
1153 OSErr err;
1154 SVCB * vcb;
1155 #if !BSD
1156 DrvQEl *drvP;
1157 short ioRefNum;
1158 #endif
1159
1160 GPtr->MinorRepairsP = nil;
1161
1162 GPtr->itemsProcessed = 0;
1163 GPtr->lastProgress = 0;
1164 GPtr->startTicks = TickCount();
1165
1166 //
1167 // allocate the static data structures (VCB, FCB's, BTCB'S, DPT and BTPT)
1168 //
1169 {
1170 ScavStaticStructures *pointer;
1171
1172 pointer = (ScavStaticStructures *) AllocateClearMemory( sizeof(ScavStaticStructures) );
1173 if ( pointer == nil ) {
1174 if ( fsckGetVerbosity(GPtr->context) >= kDebugLog ) {
1175 plog( "\t error %d - could not allocate %ld bytes of memory \n",
1176 R_NoMem, sizeof(ScavStaticStructures) );
1177 }
1178 return( R_NoMem );
1179 }
1180 GPtr->scavStaticPtr = pointer;
1181
1182 GPtr->DirPTPtr = AllocateClearMemory(sizeof(SDPR) * CMMaxDepth);
1183 if ( GPtr->DirPTPtr == nil ) {
1184 if ( fsckGetVerbosity(GPtr->context) >= kDebugLog ) {
1185 plog( "\t error %d - could not allocate %ld bytes of memory \n",
1186 R_NoMem, sizeof(SDPR) * CMMaxDepth );
1187 }
1188 return( R_NoMem );
1189 }
1190 GPtr->dirPathCount = CMMaxDepth;
1191
1192 GPtr->calculatedVCB = vcb = &pointer->vcb;
1193 vcb->vcbGPtr = GPtr;
1194
1195 GPtr->FCBAPtr = (Ptr) &pointer->fcbList;
1196 GPtr->calculatedExtentsFCB = &pointer->fcbList[0];
1197 GPtr->calculatedCatalogFCB = &pointer->fcbList[1];
1198 GPtr->calculatedAllocationsFCB = &pointer->fcbList[2];
1199 GPtr->calculatedAttributesFCB = &pointer->fcbList[3];
1200 GPtr->calculatedStartupFCB = &pointer->fcbList[4];
1201 GPtr->calculatedRepairFCB = &pointer->fcbList[5];
1202
1203 GPtr->calculatedExtentsBTCB = &pointer->btcb[0];
1204 GPtr->calculatedCatalogBTCB = &pointer->btcb[1];
1205 GPtr->calculatedRepairBTCB = &pointer->btcb[2];
1206 GPtr->calculatedAttributesBTCB = &pointer->btcb[3];
1207
1208 GPtr->BTPTPtr = (SBTPT*) &pointer->btreePath;
1209 }
1210
1211
1212 SetDFAStage( kVerifyStage );
1213 SetFCBSPtr( GPtr->FCBAPtr );
1214
1215 //
1216 // locate the driveQ element for drive being scavenged
1217 //
1218 GPtr->DrvPtr = 0; // <8> initialize so we can know if drive disappears
1219
1220 //
1221 // Set up Real structures
1222 //
1223 #if !BSD
1224 err = FindDrive( &ioRefNum, &(GPtr->DrvPtr), GPtr->DrvNum );
1225 #endif
1226 if ( IsBlueBoxSharedDrive( GPtr->DrvPtr ) )
1227 return noErr;
1228
1229 err = GetVolumeFeatures( GPtr ); // Sets up GPtr->volumeFeatures and GPtr->realVCB
1230
1231 #if !BSD
1232 if ( GPtr->DrvPtr == NULL ) // <8> drive is no longer there!
1233 return ( R_NoVol );
1234 else
1235 drvP = GPtr->DrvPtr;
1236
1237 // Save current value of vcbWrCnt, to detect modifications to volume by other apps etc
1238 if ( GPtr->volumeFeatures & volumeIsMountedMask )
1239 {
1240 FlushVol( nil, GPtr->realVCB->vcbVRefNum ); // Ask HFS to update all changes to disk
1241 GPtr->wrCnt = GPtr->realVCB->vcbWrCnt; // Remember write count after writing changes
1242 }
1243 #endif
1244
1245 // Finish initializing the VCB
1246
1247 // The calculated structures
1248 #if BSD
1249 InitBlockCache(vcb);
1250 vcb->vcbDriveNumber = GPtr->DrvNum;
1251 vcb->vcbDriverReadRef = GPtr->DrvNum;
1252 vcb->vcbDriverWriteRef = -1; /* XXX need to get real fd here */
1253 #else
1254 vcb->vcbDriveNumber = drvP->dQDrive;
1255 vcb->vcbDriverReadRef = drvP->dQRefNum;
1256 vcb->vcbDriverWriteRef = drvP->dQRefNum;
1257 vcb->vcbFSID = drvP->dQFSID;
1258 #endif
1259 // vcb->vcbVRefNum = Vol_RefN;
1260
1261 //
1262 // finish initializing the FCB's
1263 //
1264 {
1265 SFCB *fcb;
1266
1267 // Create Calculated Extents FCB
1268 fcb = GPtr->calculatedExtentsFCB;
1269 fcb->fcbFileID = kHFSExtentsFileID;
1270 fcb->fcbVolume = vcb;
1271 fcb->fcbBtree = GPtr->calculatedExtentsBTCB;
1272 vcb->vcbExtentsFile = fcb;
1273
1274 // Create Calculated Catalog FCB
1275 fcb = GPtr->calculatedCatalogFCB;
1276 fcb->fcbFileID = kHFSCatalogFileID;
1277 fcb->fcbVolume = vcb;
1278 fcb->fcbBtree = GPtr->calculatedCatalogBTCB;
1279 vcb->vcbCatalogFile = fcb;
1280
1281 // Create Calculated Allocations FCB
1282 fcb = GPtr->calculatedAllocationsFCB;
1283 fcb->fcbFileID = kHFSAllocationFileID;
1284 fcb->fcbVolume = vcb;
1285 fcb->fcbBtree = NULL; // no BitMap B-Tree
1286 vcb->vcbAllocationFile = fcb;
1287
1288 // Create Calculated Attributes FCB
1289 fcb = GPtr->calculatedAttributesFCB;
1290 fcb->fcbFileID = kHFSAttributesFileID;
1291 fcb->fcbVolume = vcb;
1292 fcb->fcbBtree = GPtr->calculatedAttributesBTCB;
1293 vcb->vcbAttributesFile = fcb;
1294
1295 /* Create Calculated Startup FCB */
1296 fcb = GPtr->calculatedStartupFCB;
1297 fcb->fcbFileID = kHFSStartupFileID;
1298 fcb->fcbVolume = vcb;
1299 fcb->fcbBtree = NULL;
1300 vcb->vcbStartupFile = fcb;
1301 }
1302
1303 // finish initializing the BTCB's
1304 {
1305 BTreeControlBlock *btcb;
1306
1307 btcb = GPtr->calculatedExtentsBTCB; // calculatedExtentsBTCB
1308 btcb->fcbPtr = GPtr->calculatedExtentsFCB;
1309 btcb->getBlockProc = GetFileBlock;
1310 btcb->releaseBlockProc = ReleaseFileBlock;
1311 btcb->setEndOfForkProc = SetEndOfForkProc;
1312
1313 btcb = GPtr->calculatedCatalogBTCB; // calculatedCatalogBTCB
1314 btcb->fcbPtr = GPtr->calculatedCatalogFCB;
1315 btcb->getBlockProc = GetFileBlock;
1316 btcb->releaseBlockProc = ReleaseFileBlock;
1317 btcb->setEndOfForkProc = SetEndOfForkProc;
1318
1319 btcb = GPtr->calculatedAttributesBTCB; // calculatedAttributesBTCB
1320 btcb->fcbPtr = GPtr->calculatedAttributesFCB;
1321 btcb->getBlockProc = GetFileBlock;
1322 btcb->releaseBlockProc = ReleaseFileBlock;
1323 btcb->setEndOfForkProc = SetEndOfForkProc;
1324 }
1325
1326
1327 //
1328 // Initialize some global stuff
1329 //
1330
1331 GPtr->RepLevel = repairLevelNoProblemsFound;
1332 GPtr->ErrCode = 0;
1333 GPtr->IntErr = noErr;
1334 GPtr->VIStat = 0;
1335 GPtr->ABTStat = 0;
1336 GPtr->EBTStat = 0;
1337 GPtr->CBTStat = 0;
1338 GPtr->CatStat = 0;
1339 GPtr->VeryMinorErrorsStat = 0;
1340 GPtr->JStat = 0;
1341
1342 /* Assume that the volume is dirty unmounted */
1343 GPtr->cleanUnmount = false;
1344
1345 //
1346 // Initialize VolumeObject
1347 //
1348
1349 InitializeVolumeObject( GPtr );
1350
1351 /* Check if the volume type of initialized object is valid. If not, return error */
1352 if (VolumeObjectIsValid() == false) {
1353 return (R_BadSig);
1354 }
1355
1356 // Keep a valid file id list for HFS volumes
1357 GPtr->validFilesList = (UInt32**)NewHandle( 0 );
1358 if ( GPtr->validFilesList == nil ) {
1359 if ( fsckGetVerbosity(GPtr->context) >= kDebugLog ) {
1360 plog( "\t error %d - could not allocate file ID list \n", R_NoMem );
1361 }
1362 return( R_NoMem );
1363 }
1364
1365 // Convert the security attribute name from utf8 to utf16. This will
1366 // avoid repeated conversion of all extended attributes to compare with
1367 // security attribute name
1368 (void) utf_decodestr((unsigned char *)KAUTH_FILESEC_XATTR, strlen(KAUTH_FILESEC_XATTR), GPtr->securityAttrName, &GPtr->securityAttrLen, sizeof(GPtr->securityAttrName));
1369
1370 return( noErr );
1371
1372 } /* end of ScavSetUp */
1373
1374
1375
1376
1377 /*------------------------------------------------------------------------------
1378
1379 Function: ScavTerm - (Scavenge Termination))
1380
1381 Function: Terminates the current scavenging operation. Memory for the
1382 VCB, FCBs, BTCBs, volume bit map, and BTree bit maps is
1383 released.
1384
1385 Input: GPtr - pointer to scavenger global area
1386
1387 Output: ScavTerm - function result:
1388 0 = no error
1389 n = error code
1390 ------------------------------------------------------------------------------*/
1391
1392 static int ScavTerm( SGlobPtr GPtr )
1393 {
1394 SFCB *fcbP;
1395 BTreeControlBlock *btcbP;
1396 RepairOrderPtr rP;
1397 OSErr err;
1398 ExtentsTable **extentsTableH;
1399 ExtentInfo *curExtentInfo;
1400 int i;
1401
1402 (void) BitMapCheckEnd();
1403
1404 while( (rP = GPtr->MinorRepairsP) != nil ) // loop freeing leftover (undone) repair orders
1405 {
1406 GPtr->MinorRepairsP = rP->link; // (in case repairs were not made)
1407 DisposeMemory(rP);
1408 err = MemError();
1409 }
1410
1411 if( GPtr->validFilesList != nil )
1412 DisposeHandle( (Handle) GPtr->validFilesList );
1413
1414 if( GPtr->overlappedExtents != nil ) {
1415 extentsTableH = GPtr->overlappedExtents;
1416
1417 /* Overlapped extents list also allocated memory for attribute name */
1418 for (i=0; i<(**extentsTableH).count; i++) {
1419 curExtentInfo = &((**extentsTableH).extentInfo[i]);
1420
1421 /* Deallocate memory for attribute name, if any */
1422 if (curExtentInfo->attrname) {
1423 free(curExtentInfo->attrname);
1424 }
1425 }
1426
1427 DisposeHandle( (Handle) GPtr->overlappedExtents );
1428 }
1429
1430 if( GPtr->fileIdentifierTable != nil )
1431 DisposeHandle( (Handle) GPtr->fileIdentifierTable );
1432
1433 if( GPtr->calculatedVCB == nil ) // already freed?
1434 return( noErr );
1435
1436 // If the FCB's and BTCB's have been set up, dispose of them
1437 fcbP = GPtr->calculatedExtentsFCB; // release extent file BTree bit map
1438 if ( fcbP != nil )
1439 {
1440 btcbP = (BTreeControlBlock*)fcbP->fcbBtree;
1441 if ( btcbP != nil)
1442 {
1443 if( btcbP->refCon != nil )
1444 {
1445 if(((BTreeExtensionsRec*)btcbP->refCon)->BTCBMPtr != nil)
1446 {
1447 DisposeMemory(((BTreeExtensionsRec*)btcbP->refCon)->BTCBMPtr);
1448 err = MemError();
1449 }
1450 DisposeMemory( (Ptr)btcbP->refCon );
1451 err = MemError();
1452 btcbP->refCon = nil;
1453 }
1454
1455 fcbP = GPtr->calculatedCatalogFCB; // release catalog BTree bit map
1456 btcbP = (BTreeControlBlock*)fcbP->fcbBtree;
1457
1458 if( btcbP->refCon != nil )
1459 {
1460 if(((BTreeExtensionsRec*)btcbP->refCon)->BTCBMPtr != nil)
1461 {
1462 DisposeMemory(((BTreeExtensionsRec*)btcbP->refCon)->BTCBMPtr);
1463 err = MemError();
1464 }
1465 DisposeMemory( (Ptr)btcbP->refCon );
1466 err = MemError();
1467 btcbP->refCon = nil;
1468 }
1469 }
1470 }
1471
1472 DisposeMemory(GPtr->DirPTPtr);
1473 DisposeMemory((ScavStaticStructures *)GPtr->scavStaticPtr);
1474 GPtr->scavStaticPtr = nil;
1475 GPtr->calculatedVCB = nil;
1476
1477 return( noErr );
1478 }
1479
1480 #define BLUE_BOX_SHARED_DRVR_NAME "\p.BlueBoxShared"
1481 #define BLUE_BOX_FLOPPY_WHERE_STRING "\pdisk%d (Shared)"
1482 #define SONY_DRVR_NAME "\p.Sony"
1483
1484 /*------------------------------------------------------------------------------
1485
1486 Routine: IsBlueBoxSharedDrive
1487
1488 Function: Given a DQE address, return a boolean that determines whether
1489 or not a drive is a Blue Box disk being accessed via Shared mode.
1490 Such drives do not support i/o and cannot be scavenged.
1491
1492 Input: Arg 1 - DQE pointer
1493
1494 Output: D0.L - 0 if drive not to be used
1495 1 otherwise
1496 ------------------------------------------------------------------------------*/
1497
1498 struct IconAndStringRec {
1499 char icon[ 256 ];
1500 Str255 string;
1501 };
1502 typedef struct IconAndStringRec IconAndStringRec, * IconAndStringRecPtr;
1503
1504
1505 Boolean IsBlueBoxSharedDrive ( DrvQElPtr dqPtr )
1506 {
1507 #if 0
1508 Str255 blueBoxSharedDriverName = BLUE_BOX_SHARED_DRVR_NAME;
1509 Str255 blueBoxFloppyWhereString = BLUE_BOX_FLOPPY_WHERE_STRING;
1510 Str255 sonyDriverName = SONY_DRVR_NAME;
1511 DCtlHandle driverDCtlHandle;
1512 DCtlPtr driverDCtlPtr;
1513 DRVRHeaderPtr drvrHeaderPtr;
1514 StringPtr driverName;
1515
1516 if ( dqPtr == NULL )
1517 return false;
1518
1519 // Now look at the name of the Driver name. If it is .BlueBoxShared keep it out of the list of available disks.
1520 driverDCtlHandle = GetDCtlEntry(dqPtr->dQRefNum);
1521 driverDCtlPtr = *driverDCtlHandle;
1522 if((((driverDCtlPtr->dCtlFlags) & Is_Native_Mask) == 0) && (driverDCtlPtr->dCtlDriver != nil))
1523 {
1524 if (((driverDCtlPtr->dCtlFlags) & Is_Ram_Based_Mask) == 0)
1525 {
1526 drvrHeaderPtr = (DRVRHeaderPtr)driverDCtlPtr->dCtlDriver;
1527 }
1528 else
1529 {
1530 //¥¥¥ bek - lock w/o unlock/restore? should be getstate/setstate?
1531 HLock((Handle)(driverDCtlPtr)->dCtlDriver);
1532 drvrHeaderPtr = (DRVRHeaderPtr)*((Handle)(driverDCtlPtr->dCtlDriver));
1533
1534 }
1535 driverName = (StringPtr)&(drvrHeaderPtr->drvrName);
1536 if (!(IdenticalString(driverName,blueBoxSharedDriverName,nil)))
1537 {
1538 return( true );
1539 }
1540
1541 // Special case for the ".Sony" floppy driver which might be accessed in Shared mode inside the Blue Box
1542 // Test its "where" string instead of the driver name.
1543 if (!(IdenticalString(driverName,sonyDriverName,nil)))
1544 {
1545 CntrlParam paramBlock;
1546
1547 paramBlock.ioCompletion = nil;
1548 paramBlock.ioNamePtr = nil;
1549 paramBlock.ioVRefNum = dqPtr->dQDrive;
1550 paramBlock.ioCRefNum = dqPtr->dQRefNum;
1551 paramBlock.csCode = kDriveIcon; // return physical icon
1552
1553 // If PBControl(kDriveIcon) returns an error then the driver is not the Blue Box driver.
1554 if ( noErr == PBControlSync( (ParmBlkPtr) &paramBlock ) )
1555 {
1556 IconAndStringRecPtr iconAndStringRecPtr;
1557 StringPtr whereStringPtr;
1558
1559 iconAndStringRecPtr = * (IconAndStringRecPtr*) & paramBlock.csParam;
1560 whereStringPtr = (StringPtr) & iconAndStringRecPtr->string;
1561 if (!(IdenticalString(whereStringPtr,blueBoxFloppyWhereString,nil)))
1562 {
1563 return( true );
1564 }
1565 }
1566 }
1567 }
1568 #endif
1569
1570 return false;
1571 }
1572
1573
1574
1575
1576 /*------------------------------------------------------------------------------
1577
1578 Function: printVerifyStatus - (Print Verify Status)
1579
1580 Function: Prints out the Verify Status words.
1581
1582 Input: GPtr - pointer to scavenger global area
1583
1584 Output: None.
1585 ------------------------------------------------------------------------------*/
1586 static
1587 void printVerifyStatus(SGlobPtr GPtr)
1588 {
1589 UInt32 stat;
1590
1591 stat = GPtr->VIStat | GPtr->ABTStat | GPtr->EBTStat | GPtr->CBTStat | GPtr->CatStat;
1592
1593 if ( stat != 0 ) {
1594 plog(" Verify Status: VIStat = 0x%04x, ABTStat = 0x%04x EBTStat = 0x%04x\n",
1595 GPtr->VIStat, GPtr->ABTStat, GPtr->EBTStat);
1596 plog(" CBTStat = 0x%04x CatStat = 0x%08x\n",
1597 GPtr->CBTStat, GPtr->CatStat);
1598 }
1599 }