|
|
1.1 root 1: /*
2: * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3: *
4: * @APPLE_LICENSE_HEADER_START@
5: *
6: * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7: * Reserved. This file contains Original Code and/or Modifications of
8: * Original Code as defined in and that are subject to the Apple Public
9: * Source License Version 1.1 (the "License"). You may not use this file
10: * except in compliance with the License. Please obtain a copy of the
11: * License at http://www.apple.com/publicsource and read it before using
12: * this file.
13: *
14: * The Original Code and all software distributed under the License are
15: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
19: * License for the specific language governing rights and limitations
20: * under the License.
21: *
22: * @APPLE_LICENSE_HEADER_END@
23: */
24:
25: /*
26: File: VolumeCheck.c
27:
28: Contains: Consistency checking code for HFS and HFS Plus volumes.
29: This code was based off MtCheck (in TFSVol.a).
30:
31: Version: HFS Plus 1.0
32:
33: Copyright: � 1995-1998 by Apple Computer, Inc., all rights reserved.
34:
35: File Ownership:
36:
37: DRI: Don Brady
38:
39: Other Contact: xxx put other contact here xxx
40:
41: Technology: xxx put technology here xxx
42:
43: Writers:
44:
45: (NG) Nitin Ganatra
46: (DSH) Deric Horn
47: (msd) Mark Day
48: (djb) Don Brady
49:
50: Change History (most recent first):
51:
52: <Rhap> 6/3/98 djb Switch RenameCatalogNode to MoveRenameCatalogNode.
53: <Rhap> 4/2/98 djb FixOrphanFileThreads only works with MacOS.
54: <Rhap> 03/31/98 djb Sync up with final HFSVolumes.h header file.
55: <Rhap> 03/26/98 djb Minor cleanup.
56: <Rhap> 03/20/98 djb Add leaf-order checking back in. Set USE_BTREE_SCANNER to true
57: to enable disk order checking. Add VOLUME_CHECK_LOG messages.
58: Use PascalBinaryCompare to compare volume names.
59:
60: <CS16> 11/26/97 djb Radar #2003656 (and others) - remove B-tree header fixing.
61: <15> 10/31/97 DSH Modify so DFA can call without actually writing to the disk,
62: added consistencyStatus parameter to MountCheck and fixed bug in
63: freeBlocks calculation in CheckBitmapAndHeader().
64: <CS14> 10/23/97 msd Bug 1685113. Fix incorrect volume header creation dates.
65: <CS13> 9/18/97 NG Don't call Gestalt(osAttr) to check if Temp Mem is available.
66: Instead, use GetExpandMemProcessMgrExists
67: <CS12> 9/7/97 djb Turn off DebugStr at MountCheck's ErrorExit.
68: <CS11> 9/5/97 msd Added CheckBitmapAndHeader to account for space used by the
69: volume header, alternate volume header, and the allocation file
70: (bitmap) itself.
71: <CS10> 9/4/97 msd Added CheckAttributes to account for space used by large
72: attribute values (the ones that occupy extents).
73: <CS9> 8/29/97 djb Fixed bug in CheckExtentsOverflow (radar #1675472 and #1675417).
74: <CS8> 8/19/97 msd In CheckCatalog, don't increment the counts associated with the
75: root directory twice; only do it in the bulk scan. When counting
76: folders, don't count the root directory itself.
77: <CS7> 8/11/97 djb Changed vcb check of largestCNID from "<" to "<=" (radar
78: #1670614). Use thread records when establishing the largest
79: CNID.
80: <CS6> 7/30/97 DSH Casting for SC, needed for DFA compiles
81: <CS5> 7/28/97 msd Use fast new B-Tree scanner. CheckCatalog was using a variable
82: before it was defined; the valance computations should have been
83: wrong.
84: <CS4> 7/16/97 DSH FilesInternal.i renamed FileMgrInternal.i to avoid name
85: collision
86: <CS3> 7/8/97 DSH Loading PrecompiledHeaders from define passed in on C line
87: <CS2> 6/12/97 djb Get in sync with HFS Plus format changes.
88: <CS1> 4/25/97 djb first checked in
89:
90: <HFS17> 4/16/97 djb Remove unused variables to prevent warnings.
91: <HFS16> 4/16/97 djb Always use new B-tree code.
92: <HFS15> 4/11/97 DSH use extended VCB fields catalogRefNum, and extentsRefNum.
93: <HFS14> 4/4/97 djb Get in sync with volume format changes.
94: <HFS13> 2/19/97 djb Use real B-tree control blocks. Removed caching code.
95: <HFS12> 1/17/97 msd Fix an illegal cast from one structure type to another in
96: CheckExtentsOverflow.
97: <HFS11> 1/16/97 djb Fixing extents overflow check for HFS+ volumes.
98: <HFS10> 1/15/97 djb Added support for checking HFS+ volumes.
99: <HFS9> 1/3/97 djb Added support for large keys.Integrated HFSVolumesPriv.h
100: changes.
101: <HFS8> 12/23/96 djb Fixed bug in CheckExtentsOverflow (bad key).
102: <HFS8> 12/23/96 djb Fixed bug in CheckExtentsOverflow (key was wrong).
103: <HFS7> 12/19/96 DSH All refs to VCB are now refs to ExtendedVCB
104: <HFS6> 12/19/96 djb Updated for new B-tree Manager interface.
105: <HFS5> 12/13/96 djb Use new Catalog SPI for GetCatalogNode.
106: <HFS4> 12/4/96 DSH Precompiled Headers
107: <HFS3> 11/21/96 msd Removed definition of kHFSSignature since it is now in
108: FilesInternal.h.
109: <HFS2> 11/19/96 DSH Removed FilesPriv.h, moved MountCheck prototype to
110: FilesInternal.h.
111: <HFS1> 11/13/96 djb first checked in
112:
113: To do:
114: - If a cnid of 5 is found in extents btree make sure vcb attribute is set correctly
115: */
116: #define USE_BTREE_SCANNER 0
117:
118: #if ( PRAGMA_LOAD_SUPPORTED )
119: #pragma load PrecompiledHeaders
120: #else
121: #if TARGET_OS_MAC
122: #include <Gestalt.h>
123: #include <Errors.h>
124: #include <ExpandMemPriv.h>
125: #include <Files.h>
126: #include <StringComparePriv.h>
127: #include <Types.h>
128: #else
129: #include "../headers/system/MacOSTypes.h"
130: #include "../headers/system/MacOSStubs.h"
131: #endif /* TARGET_OS_MAC */
132: #endif /* PRAGMA_LOAD_SUPPORTED */
133:
134: #include "../headers/FileMgrInternal.h"
135: #include "../headers/HFSBtreesPriv.h"
136: #include "../headers/HFSVolumes.h"
137: #include "../headers/BTreesPrivate.h"
138: #include "../headers/BTreeScanner.h"
139:
140:
141: enum {
142: kTypeDataFork = 0x00,
143: kTypeResourceFork = 0xFF,
144:
145: kVCBAttrLocked = 0x8080, // volume locked (software or hardware)
146: kVCBFlagsVolumeDirty = 0x8000
147: };
148:
149:
150: enum {
151: kBTreePrevious = -1,
152: kBTreeCurrent = 0,
153: kBTreeNext = 1
154: };
155:
156:
157: enum {
158: kHFSOrphanedExtents = 0x00000001,
159: kHFSOverlapingExtents = 0x00000002,
160: kHFSCatalogBTreeLoop = 0x00000004,
161: kHFSExtentsBTreeLoop = 0x00000008,
162: kHFSOrphanedThreadRecords = 0x00000010,
163: kHFSMissingThreadRecords = 0x00000020,
164: kHFSInvalidPEOF = 0x00000040,
165: kHFSInvalidLEOF = 0x00000080,
166: kHFSInvalidValence = 0x00000100,
167: kHFSInvalidBTreeHeader = 0x00000200,
168: kHFSInvalidCatalogRecordType = 0x00000400,
169: kHFSInconsistentVolumeName = 0x00000800,
170:
171: kHFSMinorRepairsWereMade = 0x80000000
172: };
173:
174: enum {
175: kMinimumBtreeNodesForCache = 100
176: };
177:
178: #define kMDBValidAttributesMask 0x8780
179:
180: #define badCatalogErr -1311
181:
182:
183: typedef struct {
184: Handle tempBufferHand;
185: UInt32 tempBufferSize;
186: Ptr savedBufPtr;
187: Ptr savedGetNode;
188: Ptr savedRelNode;
189: Ptr savedNodeCache;
190: } BTreeState;
191:
192:
193: // Macros
194:
195: //void M_ExitOnError(OSErr result);
196: //#define M_ExitOnError( result ) if ( ( result ) != noErr ) goto ErrorExit; else ;
197:
198: BTreeControlBlock* GetBTreeControlBlock(short refNum);
199: #define GetBTreeControlBlock(refNum) ((BTreeControlBlock*) GetFileControlBlock((refNum))->fcbBTCBPtr)
200:
201: Ptr LMGetVector(Ptr vector);
202: #define LMGetVector(V) (*(Ptr*) (V))
203:
204: void LMSetVector(Ptr vector, Ptr address);
205: #define LMSetVector(V,A) (*(Ptr *) (V) = (A))
206:
207: Boolean HFSEqualString(ConstStr255Param str1, ConstStr255Param str2);
208: #define HFSEqualString(s1, s2) PascalBinaryCompare((s1), (s2))
209:
210: #if DIAGNOSTIC
211: #ifdef KERNEL
212: #include <sys/systm.h>
213: #define PRINTIT kprintf
214: #else /* !KERNEL */
215: #define PRINTIT printf
216: #endif /* KERNEL */
217: #define VOLUME_CHECK_LOG(s) PRINTIT(" MountCheck: %s\n",(s))
218: #else
219: #define VOLUME_CHECK_LOG(s)
220: #endif
221:
222:
223: #if TARGET_OS_MAC
224: #define GET_SCANNER_BUFFER() LMGetFSVars()->gAttributesBuffer
225: #define GET_SCANNER_BUFFER_SIZE() LMGetFSVars()->gAttributesBufferSize
226: #endif
227:
228:
229: #if TARGET_OS_RHAPSODY
230: #define GET_SCANNER_BUFFER() NULL
231: #define GET_SCANNER_BUFFER_SIZE() 0
232: #endif
233:
234:
235: // local prototypes
236:
237: static OSErr CheckCatalog (ExtendedVCB* volume, UInt32* freeBlocks, UInt32 xCatalogBlocks, UInt32* consistencyStatus );
238:
239: static OSErr CheckExtentsOverflow ( ExtendedVCB* volume, UInt32 *freeBlocks, UInt32 *xCatalogBlocks, UInt32* consistencyStatus );
240:
241: static OSErr CheckAttributes ( ExtendedVCB *volume, UInt32 *freeBlocks, UInt32 *consistencyStatus );
242:
243: static OSErr CheckBitmapAndHeader ( ExtendedVCB *volume, UInt32 *freeBlocks, UInt32 *consistencyStatus );
244:
245: static void VolumeBitMapCheckExtents ( ExtendedVCB* volume, HFSPlusExtentDescriptor* theExtents, UInt32* consistencyStatus );
246:
247: static OSErr CheckCreateDate (ExtendedVCB* volume, UInt32* consistencyStatus );
248:
249: static Boolean ValidPEOF ( ExtendedVCB* volume, CatalogRecord *file );
250:
251: #if TARGET_OS_MAC
252: static OSErr FixOrphanFileThreads ( ExtendedVCB* volume, UInt32 maxRecords, UInt32 *repairsDone);
253: #else
254: #define FixOrphanFileThreads(v, m, f) 0
255: #endif
256:
1.1.1.2 ! root 257: #if TARGET_OS_MAC
1.1 root 258: static Handle GetTempBuffer (Size size);
259: #endif /* TARGET_OS_MAC */
260:
261: static UInt32 CountExtentBlocks (HFSPlusExtentRecord extents, Boolean hfsPlus);
262:
263: static void ConvertToHFSPlusExtent (const HFSExtentRecord oldExtents, HFSPlusExtentRecord newExtents);
264:
265: static OSErr GetFileExtentRecord( const ExtendedVCB *vcb, const FCB *fcb, HFSPlusExtentRecord extents);
266:
267: // glue prototypes
268:
269: extern OSErr CacheReadInPlace(ExtendedVCB *volume, HIOParam *iopb, UInt32 currentPosition, UInt32 maximumBytes, UInt32 *actualBytes);
270:
271: extern pascal Ptr LowerBufPtr(long size);
272:
273:
274: // 68K prototypes
275:
276: extern OSErr FastGetNode(void);
277:
278: extern OSErr FastRelNode(void);
279:
280:
281: /*-------------------------------------------------------------------------------
282:
283: Routine: MountCheck
284:
285: Function: Do a quick check of the volume meta-data to make sure that
286: it is consistent. Minor inconsistencies in the MDB or volume
287: bitmap will be repaired all other inconsistencies will be
288: detected and reported in the consistencyStatus output.
289:
290: This check will terminated if any unexpected BTree errors
291: are encountered.
292:
293: Input: volume - pointer to volume control block
294: consistencyStatus - ????
295:
296: Output: none
297:
298: Result: noErr - success
299:
300: -------------------------------------------------------------------------------*/
301: OSErr
302: MountCheck( ExtendedVCB* volume, UInt32 *consistencyStatus )
303: {
304: OSErr result;
305: UInt32 freeBlocks;
306: UInt32 bitmapFreeBlocks;
307: UInt32 xCatalogBlocks;
308:
309:
310: *consistencyStatus = 0;
311: freeBlocks = volume->totalBlocks; // initially all blocks are free
312:
313: if (volume->vcbSigWord == kHFSPlusSigWord)
314: {
315: //--- make sure the volume header's create date matches the MDB's create date
316: VOLUME_CHECK_LOG("checking HFS Plus wrapper's create date...");
317: result = CheckCreateDate (volume, consistencyStatus);
318: M_ExitOnError(result);
319: }
320:
321: //--- make sure the allocation file, volume header, and alternate volume header are allocated
322: VOLUME_CHECK_LOG("checking allocation file...");
323: result = CheckBitmapAndHeader ( volume, &freeBlocks, consistencyStatus );
324: M_ExitOnError(result);
325:
326: //--- do a quick consistency check of the Extents Overflow b-tree ----------------------------
327: VOLUME_CHECK_LOG("checking extents overflow b-tree...");
328: result = CheckExtentsOverflow(volume, &freeBlocks, &xCatalogBlocks, consistencyStatus);
329: M_ExitOnError(result);
330:
331: //--- do a quick consistency check of the Catalog b-tree ------------------------------------
332: VOLUME_CHECK_LOG("checking catalog b-tree...");
333: result = CheckCatalog( volume, &freeBlocks, xCatalogBlocks, consistencyStatus );
334: M_ExitOnError( result );
335:
336: //--- do a quick consistency check of the Attributes b-tree ------------------------------------
337: result = CheckAttributes( volume, &freeBlocks, consistencyStatus );
338: M_ExitOnError( result );
339:
340: if( freeBlocks != volume->freeBlocks && volume->vcbAtrb & kVCBAttrLocked )
341: {
342: *consistencyStatus |= kHFSMinorRepairsWereMade;
343: VOLUME_CHECK_LOG("repairing free block count");
344: }
345:
346: (void) UpdateFreeCount( volume ); // update freeBlocks from volume bitmap
347:
348: // make sure there are not any doubly allocated blocks...
349: // we are not concerned with orphaned blocks (since this is not fatal)
350:
351: bitmapFreeBlocks = volume->freeBlocks;
352:
353: if ( freeBlocks < bitmapFreeBlocks) // there are doubly allocated blocks!
354: {
355: *consistencyStatus |= kHFSOverlapingExtents;
356: VOLUME_CHECK_LOG("uh oh, there are doubly allocated file blocks!");
357: goto ErrorExit;
358: }
359: else if ( freeBlocks > bitmapFreeBlocks) // there are orphaned blocks
360: {
361: *consistencyStatus |= kHFSOrphanedExtents;
362: VOLUME_CHECK_LOG("orphaned volume bitmap blocks detected");
363: }
364:
365: return noErr;
366:
367: ErrorExit:
368:
369: VOLUME_CHECK_LOG("a fatal inconsistency was found! :( ");
370:
371: return badMDBErr;
372: }
373:
374:
375: /*-------------------------------------------------------------------------------
376:
377: Routine: VolumeCheckCatalog
378:
379: Function: Make sure the name of the root matches the volume name in the MDB.
380: Make sure the volume bitmap reflects the allocations of all file
381: extents.
382:
383: Assumption:
384:
385: Input: volume - pointer to volume control block
386: freeBlocks
387: xCatalogBlocks
388: consistencyStatus
389:
390: Output: none
391:
392: Result: noErr - success
393:
394: -------------------------------------------------------------------------------*/
395: static OSErr
396: CheckCatalog ( ExtendedVCB *volume, UInt32 *freeBlocks, UInt32 xCatalogBlocks, UInt32 *consistencyStatus )
397: {
398: UInt32 maxRecords;
399: UInt32 largestCNID;
400: UInt32 fileCount;
401: UInt16 rootFileCount; //XXX can this overflow on HFS Plus volumes?
402: UInt16 rootDirectoryCount; //XXX can this overflow on HFS Plus volumes?
403: UInt32 directoryCount;
404: UInt32 fileThreads, directoryThreads;
405: UInt32 fileThreadsExpected;
406: UInt32 catalogValence;
407: UInt32 recordsFound;
408: UInt32 catalogBlocks;
409: OSErr result;
410: CatalogNodeData nodeData;
411: CatalogRecord *record;
412: CatalogKey *key;
413: FSSpec spec;
414: UInt16 recordSize;
415: BTreeControlBlock* btree;
416: UInt32 hint;
417: FCB* file;
418: HFSPlusExtentRecord extentRecord;
419: BTScanState scanState;
420: Boolean hfsPlus = (volume->vcbSigWord == kHFSPlusSigWord);
421: UInt16 operation;
422: BTreeIterator btreeIterator;
423: FSBufferDescriptor btRecord;
424: CatalogRecord catalogRecord;
425:
426: fileThreads = fileThreadsExpected = directoryThreads = 0;
427: fileCount = directoryCount = 0;
428: rootFileCount = rootDirectoryCount = 0;
429: catalogValence = 0;
430: recordsFound = 0;
431: largestCNID = kHFSFirstUserCatalogNodeID;
432:
433: file = GetFileControlBlock(volume->catalogRefNum);
434:
435: result = GetFileExtentRecord(volume, file, extentRecord);
436: M_ExitOnError(result);
437:
438: catalogBlocks = CountExtentBlocks(extentRecord, hfsPlus);
439: *freeBlocks -= catalogBlocks;
440: VolumeBitMapCheckExtents(volume, extentRecord, consistencyStatus);
441:
442:
443: // Read in the root directory's record so we can perform some special checks
444: hint = 0;
445: result = GetCatalogNode(volume, kHFSRootFolderID, NULL, kNoHint, &spec, &nodeData, &hint);
446: M_ExitOnError(result);
447:
448: if ( DEBUG_BUILD && nodeData.nodeID != kHFSRootFolderID ) // works for small and large folders
449: DebugStr("\p CheckCatalog: nodeData.nodeID != kHFSRootFolderID");
450:
451: // We'll see the root directory's record later, during the full BTree scan, so don't
452: // update the counts here.
453: // ++recordsFound;
454: // catalogValence += nodeData.valence;
455:
456: /* UUU */
457: // make sure the name of the root directory matches the volume name (HFS only)
458: if ( !hfsPlus && false /*&& !HFSEqualString( spec.name, volume->vcbVN )*/ )
459: {
460: if ( volume->vcbAtrb & kVCBAttrLocked )
461: {
462: *consistencyStatus |= kHFSInconsistentVolumeName;
463: }
464: else // try and repair it
465: {
466: if (strlen( volume->vcbVN ) > 0) // check length of volume name
467: {
468: if (volume->vcbVN[0] > kHFSMaxVolumeNameChars) // if name is too long truncate it
469: volume->vcbVN[0] = kHFSMaxVolumeNameChars;
470:
471: if ( (!FORDISKFIRSTAID) || (GetDFAStage() == kRepairStage) )
472: {
473: // the length of volume name is OK, rename the root directory
474: result = MoveRenameCatalogNode(volume, kHFSRootFolderID, NULL, hint, kHFSRootFolderID, volume->vcbVN, &hint);
475: M_ExitOnError(result);
476: }
477: *consistencyStatus |= kHFSMinorRepairsWereMade;
478:
479: // now we must reset for btree search below!
480: result = GetCatalogNode(volume, kHFSRootFolderID, NULL, kNoHint, &spec, &nodeData, &hint);
481: M_ExitOnError(result);
482: }
483: else if ( strlen( spec.name ) > 0 ) // check root name length
484: {
485: if ( spec.name[0] > kHFSMaxVolumeNameChars ) // if name is too long truncate it
486: spec.name[0] = kHFSMaxVolumeNameChars;
487:
488: // the root name length is OK, change the volume name to match root name
489: BlockMoveData( spec.name, volume->vcbVN, spec.name[0]+1);
490: volume->vcbFlags |= kVCBFlagsVolumeDirty;
491: *consistencyStatus |= kHFSMinorRepairsWereMade;
492: }
493: }
494: VOLUME_CHECK_LOG("inconsistent volume name (hfs disk)");
495: }
496:
497: // make sure name locked bit is not set
498: // if (record.hfsFolder.finderDirInfo.frFlags & kNameLocked) //XXX need to use node data, not "record"
499: // {
500: // }
501:
502: // Make a conservative estimate of the upper limit of B*-Tree records that
503: // could conceivably be encountered in a scan of all the leaf nodes.
504: maxRecords = (file->fcbEOF) / sizeof(HFSCatalogFolder);
505:
506: if (USE_BTREE_SCANNER) // scan the tree in physical order (faster)
507: {
508: result = BTScanInitialize(file, 0, 0, 0, GET_SCANNER_BUFFER(), GET_SCANNER_BUFFER_SIZE(), &scanState);
509: if (result != noErr)
510: goto ErrorExit;
511: }
512: else // scan in leaf node order (slower but more reliable)
513: {
514: operation = kBTreeFirstRecord; // first leaf record
515:
516: key = (CatalogKey*) &btreeIterator.key;
517:
518: btRecord.bufferAddress = record = &catalogRecord;
519: btRecord.itemCount = 1;
520: btRecord.itemSize = sizeof(catalogRecord);
521: }
522:
523:
524: // visit all the leaf node data records in the catalog
525: while ( --maxRecords > 0 )
526: {
527: if (USE_BTREE_SCANNER)
528: result = BTScanNextRecord(&scanState, false, (void**)&key, (void**)&record, (UInt32*) &recordSize);
529: else
530: result = BTIterateRecord(file, operation, &btreeIterator, &btRecord, &recordSize);
531:
532: if ( result != noErr )
533: break;
534:
535: ++recordsFound;
536: operation = kBTreeNextRecord;
537:
538: switch (record->recordType)
539: {
540: case kHFSFolderRecord:
541: {
542: catalogValence += record->hfsFolder.valence;
543:
544: // Count all directories except the root itself
545: if (key->hfs.parentID != kHFSRootParentID)
546: ++directoryCount;
547:
548: // Count directories directly inside the root
549: if (key->hfs.parentID == kHFSRootFolderID)
550: ++rootDirectoryCount;
551:
552: if (record->hfsFolder.folderID > largestCNID)
553: largestCNID = record->hfsFolder.folderID;
554: break;
555: }
556:
557: case kHFSPlusFolderRecord:
558: {
559: catalogValence += record->hfsPlusFolder.valence;
560:
561: // Count all directories except the root itself
562: if (key->hfsPlus.parentID != kHFSRootParentID)
563: ++directoryCount;
564:
565: // Count directories directly inside the root
566: if (key->hfsPlus.parentID == kHFSRootFolderID)
567: ++rootDirectoryCount;
568:
569: if (record->hfsPlusFolder.folderID > largestCNID)
570: largestCNID = record->hfsPlusFolder.folderID;
571: break;
572: }
573:
574: case kHFSFileRecord:
575: {
576: ++fileCount;
577: if ( key->hfs.parentID == kHFSRootFolderID )
578: ++rootFileCount;
579:
580: if ( record->hfsFile.fileID > largestCNID )
581: largestCNID = record->hfsFile.fileID;
582:
583: if ( record->hfsFile.flags & kHFSThreadExistsMask )
584: ++fileThreadsExpected;
585:
586: // check the blocks allocated to this file (both forks)
587:
588: ConvertToHFSPlusExtent(record->hfsFile.dataExtents, extentRecord);
589: *freeBlocks -= CountExtentBlocks(extentRecord, false);
590: VolumeBitMapCheckExtents(volume, extentRecord, consistencyStatus);
591:
592: ConvertToHFSPlusExtent(record->hfsFile.rsrcExtents, extentRecord);
593: *freeBlocks -= CountExtentBlocks(extentRecord, false);
594: VolumeBitMapCheckExtents(volume, extentRecord, consistencyStatus);
595:
596: // check the LEOF and PEOF (both forks)
597:
598: if ( (record->hfsFile.rsrcLogicalSize > record->hfsFile.rsrcPhysicalSize) ||
599: (record->hfsFile.dataLogicalSize > record->hfsFile.dataPhysicalSize) )
600: {
601: *consistencyStatus |= kHFSInvalidLEOF;
602: VOLUME_CHECK_LOG("inconsistent file leof (not fixed)");
603: }
604:
605: if ( !ValidPEOF(volume, record) )
606: {
607: *consistencyStatus |= kHFSInvalidPEOF;
608: VOLUME_CHECK_LOG("inconsistent file peof (not fixed)");
609: }
610: break;
611: }
612:
613: case kHFSPlusFileRecord:
614: {
615: UInt32 blockSize = volume->blockSize;
616:
617: ++fileCount;
618: ++fileThreadsExpected; // HFS Plus files always have thread records
619:
620: if ( key->hfsPlus.parentID == kHFSRootFolderID )
621: ++rootFileCount;
622:
623: if ( record->hfsPlusFile.fileID > largestCNID )
624: largestCNID = record->hfsPlusFile.fileID;
625:
626: // check the blocks allocated to this file (both forks)
627:
628: *freeBlocks -= CountExtentBlocks(record->hfsPlusFile.dataFork.extents, true);
629: VolumeBitMapCheckExtents(volume, record->hfsPlusFile.dataFork.extents, consistencyStatus);
630:
631: *freeBlocks -= CountExtentBlocks(record->hfsPlusFile.resourceFork.extents, true);
632: VolumeBitMapCheckExtents(volume, record->hfsPlusFile.resourceFork.extents, consistencyStatus);
633:
634: // check the LEOF and PEOF (both forks)
635:
636: if ( (record->hfsPlusFile.resourceFork.logicalSize.lo > record->hfsPlusFile.resourceFork.totalBlocks * blockSize) ||
637: (record->hfsPlusFile.dataFork.logicalSize.lo > record->hfsPlusFile.dataFork.totalBlocks * blockSize) )
638: {
639: *consistencyStatus |= kHFSInvalidLEOF;
640: VOLUME_CHECK_LOG("inconsistent file leof (not fixed)");
641: }
642:
643: if ( !ValidPEOF(volume, record) )
644: {
645: *consistencyStatus |= kHFSInvalidPEOF;
646: VOLUME_CHECK_LOG("inconsistent file peof (not fixed)");
647: }
648: break;
649: }
650:
651: case kHFSFolderThreadRecord:
652: {
653: ++directoryThreads;
654: if ( key->hfs.parentID > largestCNID ) // <CS7>
655: largestCNID = key->hfs.parentID;
656: break;
657: }
658:
659: case kHFSPlusFolderThreadRecord:
660: {
661: ++directoryThreads;
662: if ( key->hfsPlus.parentID > largestCNID ) // <CS7>
663: largestCNID = key->hfsPlus.parentID;
664: break;
665: }
666:
667: case kHFSFileThreadRecord:
668: {
669: ++fileThreads;
670: if ( key->hfs.parentID > largestCNID ) // <CS7>
671: largestCNID = key->hfs.parentID;
672: break;
673: }
674:
675: case kHFSPlusFileThreadRecord:
676: {
677: ++fileThreads;
678: if ( key->hfsPlus.parentID > largestCNID ) // <CS7>
679: largestCNID = key->hfsPlus.parentID;
680: break;
681: }
682:
683: default:
684: *consistencyStatus |= kHFSInvalidCatalogRecordType;
685: VOLUME_CHECK_LOG("unknown catalog record");
686: break;
687: }
688:
689: } // end while
690:
691: if ( result == noErr )
692: {
693: *consistencyStatus |= kHFSCatalogBTreeLoop;
694: goto ErrorExit; // punt on loop error
695: }
696:
697: if ( result != fsBTRecordNotFoundErr )
698: goto ErrorExit; // punt on BTree errors
699:
700:
701: // Check if calculated totals match the Volume Control Block
702:
703: if (volume->vcbNxtCNID <= largestCNID) // <CS7>
704: {
705: volume->vcbNxtCNID = largestCNID + 1;
706: volume->vcbFlags |= kVCBFlagsVolumeDirty;
707: VOLUME_CHECK_LOG("fixed vcbNxtCNID");
708: }
709:
710: if (volume->vcbFilCnt != fileCount)
711: {
712: volume->vcbFilCnt = fileCount;
713: volume->vcbFlags |= kVCBFlagsVolumeDirty;
714: VOLUME_CHECK_LOG("fixed vcbFilCnt (volume file count)");
715: }
716:
717: if (volume->vcbDirCnt != directoryCount)
718: {
719: volume->vcbDirCnt = directoryCount;
720: volume->vcbFlags |= kVCBFlagsVolumeDirty;
721: VOLUME_CHECK_LOG("fixed vcbDirCnt (volume directory count)");
722: }
723:
724: if (!hfsPlus && volume->vcbNmFls != rootFileCount)
725: {
726: volume->vcbNmFls = rootFileCount;
727: volume->vcbFlags |= kVCBFlagsVolumeDirty;
728: VOLUME_CHECK_LOG("fixed vcbNmFls (root file count)");
729: }
730:
731: if (!hfsPlus && volume->vcbNmRtDirs != rootDirectoryCount)
732: {
733: volume->vcbNmRtDirs = rootDirectoryCount;
734: volume->vcbFlags |= kVCBFlagsVolumeDirty;
735: VOLUME_CHECK_LOG("fixed vcbNmRtDirs (root directory count)");
736: }
737:
738: if ( catalogValence != (fileCount + directoryCount) )
739: {
740: *consistencyStatus |= kHFSInvalidValence; // valence inconsistency detected
741: VOLUME_CHECK_LOG("valence inconsistency detected (not fixed)");
742: }
743:
744: #if 0
745: if (volume->vcbAtrb & ~kMDBValidAttributesMask) // check for invalid bits
746: {
747: volume->vcbAtrb &= kMDBValidAttributesMask;
748: volume->vcbFlags |= kVCBFlagsVolumeDirty;
749: }
750: #endif
751:
752: if ( (volume->vcbFlags & kVCBFlagsVolumeDirty) && (volume->vcbAtrb & kVCBAttrLocked) == 0 )
753: *consistencyStatus |= kHFSMinorRepairsWereMade;
754:
755: if (directoryThreads > (directoryCount + 1))
756: {
757: *consistencyStatus |= kHFSOrphanedThreadRecords; // too many directory thread records
758: VOLUME_CHECK_LOG("orphaned directory thread records detected (not fixed)");
759: }
760:
761: if (fileThreads > fileThreadsExpected)
762: {
763: if ( volume->vcbAtrb & kVCBAttrLocked )
764: {
765: *consistencyStatus |= kHFSOrphanedThreadRecords; // too many file thread records
766: VOLUME_CHECK_LOG("orphaned file thread records detected (not fixed)");
767: }
768: else if ( !hfsPlus ) // try and repair orphaned thread records
769: {
770: // If we fix up some threads, see if we can come out even without counting all the file
771: // threads again. If we don't, then looks like we have to keep count and do a second
772: // pass of file records (gotta do that anyway when we fix the KHFSMissingThreadRecords
773: // case below) and to count thread records again with the FixOrphanFileThreads call.
774:
775: if ( (!FORDISKFIRSTAID) || (GetDFAStage() == kRepairStage) )
776: {
777: UInt32 repairsDone = 0;
778:
779: (void) FixOrphanFileThreads(volume, recordsFound, &repairsDone);
780: if (repairsDone)
781: {
782: *consistencyStatus |= kHFSMinorRepairsWereMade;
783: VOLUME_CHECK_LOG("orphaned file thread records detected (fixed)");
784: }
785:
786: if (fileThreads > (fileThreadsExpected + repairsDone) )
787: {
788: *consistencyStatus |= kHFSOrphanedThreadRecords; // still too many thread records
789: VOLUME_CHECK_LOG("orphaned file thread records detected (fix attempt failed)");
790: }
791: }
792: }
793: }
794:
795: if ((directoryThreads < (directoryCount + 1)) || (fileThreads < fileThreadsExpected))
796: {
797: *consistencyStatus |= kHFSMissingThreadRecords; // not enough thread records
798: VOLUME_CHECK_LOG("some catalog thread records are missing (not fixed)");
799: }
800:
801: btree = GetBTreeControlBlock(volume->catalogRefNum);
802:
803: // okay, lets see if the blocks used by the catalog looks right
804: catalogBlocks += xCatalogBlocks;
805: if ( (catalogBlocks * volume->blockSize) != file->fcbPLen )
806: {
807: *consistencyStatus |= kHFSInvalidPEOF;
808: VOLUME_CHECK_LOG("inconsistent catalog file size detected (not fixed)");
809: }
810:
811: if ( btree->totalNodes != (file->fcbPLen / btree->nodeSize) )
812: {
813: *consistencyStatus |= kHFSInvalidBTreeHeader;
814: VOLUME_CHECK_LOG("inconsistent catalog b-tree node count detected (not fixed)");
815: }
816:
817: if (btree->leafRecords != recordsFound)
818: {
819: // We now use the B-tree scanner instead of iterating through all
820: // the leaf nodes in order. So let's be more conservative and not
821: // fix the leafRecords field until we can do adequate testing of
822: // this new method of visiting nodes. -DJB
823:
824: if (USE_BTREE_SCANNER || volume->vcbAtrb & kVCBAttrLocked)
825: {
826: *consistencyStatus |= kHFSInvalidBTreeHeader; // invalid record count
827: VOLUME_CHECK_LOG("inconsistent catalog b-tree record count detected (not fixed)");
828: }
829: else // go ahead and make this simple repair!
830: {
831: btree->leafRecords = recordsFound;
832: btree->flags |= kBTHeaderDirty; // mark BTreeControlBlock dirty
833: *consistencyStatus |= kHFSMinorRepairsWereMade;
834: VOLUME_CHECK_LOG("inconsistent catalog b-tree record count detected (fixed)");
835: }
836: }
837:
838: if ( (!FORDISKFIRSTAID) || (GetDFAStage() == kRepairStage) )
839: {
840: if ( btree->flags & kBTHeaderDirty ) // if we made changes, write them out
841: (void) BTFlushPath(file);
842: }
843:
844: return noErr;
845:
846: ErrorExit:
847: return badCatalogErr;
848:
849: } // end CheckCatalog
850:
851:
852: /*-------------------------------------------------------------------------------
853:
854: Routine: CheckExtentsOverflow
855:
856: Function: Make sure the volume bitmap reflects the allocations of all extents.
857:
858: Assumption:
859:
860: Input: volume - pointer to volume control block
861: freeBlocks
862: xCatalogBlocks
863: consistencyStatus
864:
865: Output: none
866:
867: Result: noErr - success
868:
869: -------------------------------------------------------------------------------*/
870: static OSErr
871: CheckExtentsOverflow ( ExtendedVCB *volume, UInt32 *freeBlocks, UInt32 *xCatalogBlocks, UInt32 *consistencyStatus )
872: {
873: OSErr result;
874: UInt32 maxRecords;
875: UInt32 fileID;
876: UInt32 largestCNID;
877: UInt32 recordsFound;
878: UInt32 extentFileBlocks;
879: UInt32 extentRecordBlocks;
880: HFSPlusExtentKey *extentKeyPtr;
881: HFSPlusExtentRecord *extentDataPtr;
882: HFSPlusExtentRecord extentRecord;
883: UInt16 recordSize;
884: BTreeControlBlock *btree;
885: FCB* file;
886: BTScanState scanState;
887: Boolean hfsPlus = (volume->vcbSigWord == kHFSPlusSigWord);
888: UInt16 operation;
889: BTreeIterator btreeIterator;
890: FSBufferDescriptor btRecord;
891:
892: *xCatalogBlocks = 0; // set this early since its a return value
893: largestCNID = kHFSFirstUserCatalogNodeID;
894:
895: file = GetFileControlBlock(volume->extentsRefNum);
896:
897: result = GetFileExtentRecord(volume, file, extentRecord);
898: ReturnIfError(result);
899:
900: extentFileBlocks = CountExtentBlocks(extentRecord, hfsPlus);
901: *freeBlocks -= extentFileBlocks;
902: VolumeBitMapCheckExtents(volume, extentRecord, consistencyStatus);
903:
904: // now look at the extent tree...
905:
906: // Make a conservative estimate of the upper limit of B*-Tree records that
907: // could conceivably be encountered in a scan of all the leaf nodes.
908: recordsFound = 0;
909: maxRecords = (file->fcbEOF) / (hfsPlus ? sizeof(HFSPlusExtentRecord) : sizeof(HFSExtentRecord));
910:
911: if (USE_BTREE_SCANNER) // scan the tree in physical order (faster)
912: {
913: result = BTScanInitialize(file, 0, 0, 0, GET_SCANNER_BUFFER(), GET_SCANNER_BUFFER_SIZE(), &scanState);
914: if (result != noErr)
915: return badMDBErr;
916: }
917: else // scan in leaf node order (slower but more reliable)
918: {
919: operation = kBTreeFirstRecord; // first leaf record
920: extentKeyPtr = (HFSPlusExtentKey*) &btreeIterator.key;
921:
922: btRecord.bufferAddress = &extentRecord;
923: btRecord.itemCount = 1;
924: btRecord.itemSize = sizeof(extentRecord);
925: }
926:
927: // visit all the leaf node data records in the extents B*-Tree
928: while ( recordsFound < maxRecords )
929: {
930: if (USE_BTREE_SCANNER)
931: result = BTScanNextRecord(&scanState, false, (void **) &extentKeyPtr, (void **) &extentDataPtr, (UInt32*) &recordSize);
932: else
933: result = BTIterateRecord(file, operation, &btreeIterator, &btRecord, &recordSize);
934:
935: if ( result != noErr )
936: break;
937:
938: ++recordsFound;
939:
940: // check the blocks allocated to this extent record
941:
942: if (USE_BTREE_SCANNER)
943: {
944: if ( hfsPlus )
945: BlockMoveData(extentDataPtr, &extentRecord, sizeof(extentRecord)); // just make a copy
946: else
947: ConvertToHFSPlusExtent(*((HFSExtentRecord *) extentDataPtr), extentRecord); // convert it
948: }
949: else
950: {
951: operation = kBTreeNextRecord;
952: if ( !hfsPlus )
953: ConvertToHFSPlusExtent(*(HFSExtentRecord*) &extentRecord, extentRecord); // convert it in place (since its a copy)
954: }
955:
956: extentRecordBlocks = CountExtentBlocks(extentRecord, hfsPlus);
957:
958: *freeBlocks -= extentRecordBlocks;
959: VolumeBitMapCheckExtents(volume, extentRecord, consistencyStatus);
960:
961: if ( hfsPlus )
962: fileID = extentKeyPtr->fileID;
963: else
964: fileID = ((HFSExtentKey*) extentKeyPtr)->fileID;
965:
966: // if these were catalog blocks, we need to keep track
967: if ( fileID == kHFSCatalogFileID )
968: *xCatalogBlocks += extentRecordBlocks;
969:
970: if ( fileID > largestCNID )
971: largestCNID = fileID;
972:
973: } // end while
974:
975: if ( result != fsBTRecordNotFoundErr)
976: {
977: VOLUME_CHECK_LOG("extents b-tree has serious problems!");
978:
979: return badMDBErr; // punt on error (loop error or BTree error)
980: }
981:
982: if (volume->vcbNxtCNID < largestCNID)
983: {
984: volume->vcbNxtCNID = largestCNID + 1;
985: volume->vcbFlags |= kVCBFlagsVolumeDirty;
986: volume->vcbAtrb |= 1; // need a constant for this!
987: }
988:
989: btree = GetBTreeControlBlock(volume->extentsRefNum);
990:
991: if ( (extentFileBlocks * volume->blockSize) != file->fcbPLen )
992: {
993: *consistencyStatus |= kHFSInvalidPEOF;
994: VOLUME_CHECK_LOG("inconsistent extents file size detected (not fixed)");
995: }
996:
997: if ( btree->totalNodes != (file->fcbPLen / btree->nodeSize) )
998: {
999: *consistencyStatus |= kHFSInvalidBTreeHeader;
1000: VOLUME_CHECK_LOG("inconsistent extents b-tree node count detected (not fixed)");
1001: }
1002:
1003: if (btree->leafRecords != recordsFound)
1004: {
1005: // We now use the B-tree scanner instead of iterating through all
1006: // the leaf nodes in order. So let's be more conservative and not
1007: // fix the leafRecords field until we can do adequate testing of
1008: // this new method of visiting nodes. -DJB
1009:
1010: if (USE_BTREE_SCANNER || volume->vcbAtrb & kVCBAttrLocked)
1011: {
1012: *consistencyStatus |= kHFSInvalidBTreeHeader;
1013: VOLUME_CHECK_LOG("inconsistent extents b-tree record count detected (not fixed)");
1014: }
1015: else // go ahead and make this simple repair!
1016: {
1017: btree->leafRecords = recordsFound;
1018: btree->flags |= kBTHeaderDirty; // mark BTreeControlBlock dirty
1019: *consistencyStatus |= kHFSMinorRepairsWereMade;
1020: VOLUME_CHECK_LOG("inconsistent extents b-tree record count detected (fixed)");
1021: }
1022: }
1023:
1024: if ( (!FORDISKFIRSTAID) || (GetDFAStage() == kRepairStage) )
1025: {
1026: if ( btree->flags & kBTHeaderDirty ) // if we made changes, write them out
1027: (void) BTFlushPath(file);
1028: }
1029:
1030: return noErr;
1031:
1032: } // end CheckExtentsOverflow
1033:
1034:
1035:
1036: /*-------------------------------------------------------------------------------
1037:
1038: Routine: CheckAttributes
1039:
1040: Function: Make sure the volume bitmap reflects the allocations of all attributes
1041: that occupy extents.
1042:
1043: Assumption:
1044:
1045: Input: volume - pointer to volume control block
1046: freeBlocks
1047: consistencyStatus
1048:
1049: Output: none
1050:
1051: Result: noErr - success
1052:
1053: -------------------------------------------------------------------------------*/
1054: static OSErr
1055: CheckAttributes ( ExtendedVCB *volume, UInt32 *freeBlocks, UInt32 *consistencyStatus )
1056: {
1057: OSErr result;
1058: UInt32 maxRecords;
1059: UInt32 recordsFound;
1060: UInt32 attributesFileBlocks;
1061: UInt32 extentRecordBlocks;
1062: AttributeKey *attributeKey;
1063: HFSPlusAttrRecord *attributeData;
1064: HFSPlusExtentRecord extentRecord;
1065: UInt32 recordSize;
1066: BTreeControlBlock *btree;
1067: FCB* file;
1068: BTScanState scanState;
1069: Boolean hfsPlus = (volume->vcbSigWord == kHFSPlusSigWord);
1070:
1071: //
1072: // If there isn't an attributes B-tree, there's no work to do
1073: //
1074: if (volume->attributesRefNum == 0)
1075: return noErr;
1076:
1077: file = GetFileControlBlock(volume->attributesRefNum);
1078:
1079: result = GetFileExtentRecord(volume, file, extentRecord);
1080: ReturnIfError(result);
1081:
1082: attributesFileBlocks = CountExtentBlocks(extentRecord, hfsPlus);
1083: *freeBlocks -= attributesFileBlocks;
1084: VolumeBitMapCheckExtents(volume, extentRecord, consistencyStatus);
1085:
1086:
1087: // now look at the attributes tree...
1088:
1089: // Make a conservative estimate of the upper limit of B*-Tree records that
1090: // could conceivably be encountered in a scan of all the leaf nodes.
1091: recordsFound = 0;
1092: maxRecords = (file->fcbEOF) / 8; // 8 = minimum data length (for zero length inline attribute)
1093:
1094: // Prepare to scan the tree in physical order
1095: result = BTScanInitialize(file, 0, 0, 0, GET_SCANNER_BUFFER(), GET_SCANNER_BUFFER_SIZE(), &scanState);
1096: if (result != noErr)
1097: return badMDBErr;
1098:
1099: // visit all the leaf node data records in the extents B*-Tree
1100: while ( recordsFound < maxRecords )
1101: {
1102: result = BTScanNextRecord(&scanState, false, (void **) &attributeKey, (void **) &attributeData, &recordSize);
1103:
1104: if ( result != noErr )
1105: break;
1106:
1107: ++recordsFound;
1108:
1109: // If this block has information about extents, make sure they're allocated
1110: switch (attributeData->recordType) {
1111: case kHFSPlusAttrInlineData:
1112: break;
1113: case kHFSPlusAttrForkData:
1114: extentRecordBlocks = CountExtentBlocks(attributeData->forkData.theFork.extents, true);
1115: *freeBlocks -= extentRecordBlocks;
1116: VolumeBitMapCheckExtents(volume, attributeData->forkData.theFork.extents, consistencyStatus);
1117: break;
1118: case kHFSPlusAttrExtents:
1119: extentRecordBlocks = CountExtentBlocks(attributeData->overflowExtents.extents, true);
1120: *freeBlocks -= extentRecordBlocks;
1121: VolumeBitMapCheckExtents(volume, attributeData->overflowExtents.extents, consistencyStatus);
1122: break;
1123: default:
1124: if (DEBUG_BUILD)
1125: DebugStr("\pHFS+: Unknown attribute record");
1126: break;
1127: } // end switch
1128: } // end while
1129:
1130: if ( result != fsBTRecordNotFoundErr )
1131: {
1132: VOLUME_CHECK_LOG("extents b-tree has serious problems!");
1133: return badMDBErr; // punt on error (loop error or BTree error)
1134: }
1135:
1136: btree = GetBTreeControlBlock(volume->attributesRefNum);
1137:
1138: if ( (attributesFileBlocks * volume->blockSize) != file->fcbPLen )
1139: *consistencyStatus |= kHFSInvalidPEOF;
1140:
1141: if ( btree->totalNodes != (file->fcbPLen / btree->nodeSize) )
1142: *consistencyStatus |= kHFSInvalidBTreeHeader;
1143:
1144: if (btree->leafRecords != recordsFound)
1145: {
1146: // We now use the B-tree scanner instead of iterating through all
1147: // the leaf nodes in order. So let's be more conservative and not
1148: // fix the leafRecords field until we can do adequate testing of
1149: // this new method of visiting nodes. -DJB
1150:
1151: if (USE_BTREE_SCANNER || volume->vcbAtrb & kVCBAttrLocked)
1152: {
1153: *consistencyStatus |= kHFSInvalidBTreeHeader;
1154: }
1155: else // go ahead and make this simple repair!
1156: {
1157: btree->leafRecords = recordsFound;
1158: btree->flags |= kBTHeaderDirty; // mark BTreeControlBlock dirty
1159: *consistencyStatus |= kHFSMinorRepairsWereMade;
1160: }
1161: }
1162:
1163: if ( (!FORDISKFIRSTAID) || (GetDFAStage() == kRepairStage) )
1164: {
1165: if ( btree->flags & kBTHeaderDirty ) // if we made changes, write them out
1166: (void) BTFlushPath(file);
1167: }
1168: return noErr;
1169:
1170: } // end CheckAttributes
1171:
1172:
1173:
1174: /*-------------------------------------------------------------------------------
1175:
1176: Routine: CheckBitmapAndHeader
1177:
1178: Function: Make sure the volume header, alternate volume header, and the bitmap
1179: itself are marked as used in the bitmap.
1180:
1181: Assumption:
1182:
1183: Input: volume - pointer to volume control block
1184: freeBlocks
1185: consistencyStatus
1186:
1187: Output: none
1188:
1189: Result: noErr - success
1190:
1191: -------------------------------------------------------------------------------*/
1192: static OSErr
1193: CheckBitmapAndHeader ( ExtendedVCB *volume, UInt32 *freeBlocks, UInt32 *consistencyStatus )
1194: {
1195: OSErr result;
1196: UInt32 blocksUsed;
1197: FCB* file;
1198: HFSPlusExtentRecord extentRecord;
1199:
1200: //
1201: // If there isn't an allocation file, there's no work to do
1202: //
1203: if (volume->allocationsRefNum == 0)
1204: return noErr;
1205:
1206: file = GetFileControlBlock(volume->allocationsRefNum);
1207:
1208: result = GetFileExtentRecord(volume, file, extentRecord);
1209: ReturnIfError(result);
1210:
1211: // Mark the space used by the allocation file (bitmap) itself
1212: blocksUsed = CountExtentBlocks(extentRecord, true);
1213: *freeBlocks -= blocksUsed;
1214: VolumeBitMapCheckExtents(volume, extentRecord, consistencyStatus);
1215:
1216: // Now mark sectors zero through two (boot blocks and volume header)
1217: // and the last two sectors (alternate volume header and a reserved
1218: // sector). The number of allocation blocks depends on the size of
1219: // an allocation block. What we do is create a fake extent record
1220: // for these blocks, and call VolumeBitMapCheckExtents as usual.
1221:
1222: ClearMemory(&extentRecord, sizeof(extentRecord));
1223:
1224: extentRecord[0].startBlock = 0;
1225: extentRecord[0].blockCount = 1 + (1024 / volume->blockSize); // sectors 0-2
1226:
1227: if (volume->blockSize == 512) {
1228: extentRecord[1].startBlock = volume->totalBlocks - 2;
1229: extentRecord[1].blockCount = 2;
1230: } else {
1231: extentRecord[1].startBlock = volume->totalBlocks - 1;
1232: extentRecord[1].blockCount = 1;
1233: }
1234:
1235: *freeBlocks -= extentRecord[0].blockCount + extentRecord[1].blockCount;
1236: VolumeBitMapCheckExtents(volume, extentRecord, consistencyStatus);
1237:
1238: return noErr;
1239: }
1240:
1241:
1242: // For Disk First Aid, this routine will not update the BitMap, but will set the
1243: // kHFSMinorRepairsWereMade bit to indicate the bitmap needs updating.
1244: static void VolumeBitMapCheckExtents (
1245: ExtendedVCB* volume,
1246: HFSPlusExtentDescriptor* theExtents,
1247: UInt32* consistencyStatus )
1248: {
1249: if ( BlockCheck(volume, theExtents) == -1 )
1250: {
1251: *consistencyStatus |= kHFSMinorRepairsWereMade;
1252: VOLUME_CHECK_LOG("repairing volume bitmap");
1253: }
1254: }
1255:
1256:
1257: /*-------------------------------------------------------------------------------
1258:
1259: Routine: ValidPEOF
1260:
1261: Function: Make sure the local extents match the PEOF.
1262: Currently this only checks the local extent.
1263: We could also check the oveflow extents for
1264: a more complete (but slower) test.
1265:
1266: Assumption:
1267:
1268: Input:
1269:
1270: Output:
1271:
1272: Result:
1273:
1274: -------------------------------------------------------------------------------*/
1275:
1276: static Boolean
1277: ValidPEOF( ExtendedVCB* volume, CatalogRecord *file )
1278: {
1279: UInt32 minPEOF;
1280: UInt32 allocBlkSize = volume->blockSize;
1281:
1282: if (file->recordType == kHFSPlusFileRecord)
1283: {
1284: UInt32 minBlocks;
1285:
1286: minBlocks = CountExtentBlocks(file->hfsPlusFile.resourceFork.extents, true);
1287:
1288: if ( file->hfsPlusFile.resourceFork.totalBlocks < minBlocks )
1289: return false;
1290: else if ( (file->hfsPlusFile.resourceFork.totalBlocks > minBlocks) && (file->hfsPlusFile.resourceFork.extents[kHFSPlusExtentDensity-1].blockCount == 0) )
1291: return false;
1292:
1293: minBlocks = CountExtentBlocks(file->hfsPlusFile.dataFork.extents, true);
1294:
1295: if ( file->hfsPlusFile.dataFork.totalBlocks < minBlocks )
1296: return false;
1297: else if ( (file->hfsPlusFile.dataFork.totalBlocks > minBlocks) && (file->hfsPlusFile.dataFork.extents[kHFSPlusExtentDensity-1].blockCount == 0) )
1298: return false;
1299: }
1300: else
1301: {
1302: HFSPlusExtentRecord extentRecord;
1303:
1304: ConvertToHFSPlusExtent(file->hfsFile.rsrcExtents, extentRecord);
1305: minPEOF = CountExtentBlocks(extentRecord, false) * allocBlkSize;
1306:
1307: if ( file->hfsFile.rsrcPhysicalSize < minPEOF )
1308: return false;
1309: else if ( (file->hfsFile.rsrcPhysicalSize > minPEOF) && (extentRecord[kHFSExtentDensity-1].blockCount == 0) )
1310: return false;
1311:
1312:
1313: ConvertToHFSPlusExtent(file->hfsFile.dataExtents, extentRecord);
1314: minPEOF = CountExtentBlocks(extentRecord, false) * allocBlkSize;
1315:
1316: if ( file->hfsFile.dataPhysicalSize < minPEOF )
1317: return false;
1318: else if ( (file->hfsFile.dataPhysicalSize > minPEOF) && (extentRecord[kHFSExtentDensity-1].blockCount == 0) )
1319: return false;
1320: }
1321:
1322:
1323: return true;
1324: }
1325:
1326:
1327: /*-------------------------------------------------------------------------------
1328:
1329: Routine: FixOrphanFileThreads
1330:
1331: Function: For orphan thread records during VolumeCheckConsistency have the catalog
1332: searched again for file records that match. If their thread record flag
1333: is off, mark it. If it is on, do nothing. If the file record doesn't exist,
1334: delete the thread record.
1335:
1336: Assumption: This is a function to be called only if the thread records detected during
1337: CheckCatalog was greater than the file records that had their flag set for
1338: file thread existance. I've thought of doing this differently, like keeping
1339: a table of all file records or threads scanned in the first pass, but the
1340: table gets large. You can't predict how large so may to hold upto the max
1341: fileIDs possible. (2**32). Using two such tables to match file threads
1342: against file records would be bad. Less space would be keeping a bitmap of
1343: all fileIDS (to 2**32) and marking those with threads. Then a separate
1344: bitmap for those file records with thread flags on marked.You do a byte by
1345: byte comparison across the whole bitmap. When they differ, you know if you're
1346: missing
1347: threads or orphans threads. (Note 0s would also mean IDs represented by that
1348: bit in the bitmap that is non-existant). Though, this sounds clever, it still
1349: seems it would take 8bits/byte * 512 bytes/sector * 8 sectors/ vmpage = 2**15
1350: bits per vmpage and 2**17 vmpages to cover the bitmap! Too large. SO, let's
1351: just scan the catalog again. Yep, it's timeconsuming, but we are only taking
1352: the hit in repair mode for that particular problem. No extra memory for tables
1353: or bitmaps are even required during the original scan.
1354:
1355: Input:
1356:
1357: Output:
1358:
1359: Result: E_NoError - success
1360: != E_NoError - failure
1361:
1362: -------------------------------------------------------------------------------*/
1363: #if TARGET_OS_MAC
1364: static OSErr FixOrphanFileThreads(ExtendedVCB* volume, UInt32 maxRecords, UInt32 *repairsDone)
1365: {
1366: UInt16 recordSize;
1367: OSErr tempResult, result = noErr;
1368: CatalogKey key;
1369: FSSpec spec;
1370: CatalogRecord record;
1371: CatalogNodeData nodeData;
1372: UInt32 hint = 0;
1373:
1374: // let's iterate the catalog looking for file thread records. As we hit each one,
1375: // do a search for the file record. If it exists and the thread flag is off,set
1376: // it. If it exists, and the thread flag is on, all is fine and dandy. It it
1377: // doesn't exist, remove the orphaned thread record.
1378:
1379: *repairsDone = 0;
1380:
1381: result = GetCatalogNode(volume, kHFSRootFolderID, NULL, kNoHint, &spec, &nodeData, &hint);
1382: M_ExitOnError(result);
1383:
1384: // visit all the leaf node data records in the catalog
1385: while ( --maxRecords > 0 ) // starts out at recordsFound value passed in
1386: {
1387: tempResult = GetBTreeRecord(volume->catalogRefNum, kBTreeNext, &key, &record,
1388: &recordSize, &hint );
1389: if ( tempResult )
1390: {
1391: // DebugStr("\p FixOrphanFileThreads: did we run out of records?");
1392: break;
1393: }
1394:
1395: // if this is a file thread record, check it out...
1396: if (record.recordType == kHFSFileThreadRecord)
1397: {
1398: CatalogKey currentKey;
1399: CatalogKey fileKey;
1400: UInt32 fileHint;
1401:
1402: // save our current position
1403: currentKey = key;
1404:
1405: // create a key for the file record
1406: fileHint = 0;
1407: fileKey.hfs.parentID = record.hfsThread.parentID;
1408: BlockMoveData ( record.hfsThread.nodeName, fileKey.hfs.nodeName,
1409: record.hfsThread.nodeName[0] + 1);
1410:
1411: // try and get the file record for this thread record...
1412: tempResult = SearchBTreeRecord ( volume->catalogRefNum, &fileKey, fileHint, &key, &record, &recordSize, &fileHint );
1413:
1414: if (tempResult == noErr)
1415: {
1416: if ((record.hfsFile.flags & kHFSThreadExistsMask) == 0)
1417: {
1418: // thread record exists but file record doesn't think so....set it
1419: record.hfsFile.flags |= kHFSThreadExistsMask;
1420:
1421: // update the file record on disk
1422: if ( ReplaceBTreeRecord(volume->catalogRefNum, &key, fileHint, &record, recordSize, &fileHint) == noErr )
1423: ++(*repairsDone);
1424: }
1425: }
1426: else if (tempResult == btNotFound)
1427: {
1428: // BTDelete invalidates the current node mark so before we delete the thread record,
1429: // point currentKey and hint at something valid (ie the previous record)
1430:
1431: (void) SearchBTreeRecord( volume->catalogRefNum, ¤tKey, hint, &key, &record, &recordSize, &hint );
1432: (void) GetBTreeRecord( volume->catalogRefNum, kBTreePrevious, &key, &record, &recordSize, &hint );
1433:
1434: // file record doesn't exist. Delete the thread record.
1435: if ( DeleteBTreeRecord( volume->catalogRefNum, ¤tKey) == noErr )
1436: ++(*repairsDone);
1437:
1438: currentKey = key; // now point to the record before the thread record
1439: }
1440:
1441: // now we need to reset the current node mark for GetBTreeRecord iteration...
1442: (void) SearchBTreeRecord( volume->catalogRefNum, ¤tKey, hint, &key, &record, &recordSize, &hint );
1443:
1444: } // end if thread
1445:
1446: } // end while
1447:
1448: return( result );
1449: ErrorExit:
1450: return ( result ); //XXX which error should it be ???
1451: } // end FixOrphanFileThreads
1452: #endif /* TARGET_OS_MAC */
1453:
1454: //_______________________________________________________________________
1455: //
1456: // GetTempBuffer
1457: //
1458: // This routine allocates a temporary buffer.
1459: //
1460: // Note: At startup TempNewHandle may not be available.
1461: //_______________________________________________________________________
1462: #if TARGET_OS_MAC
1463: static Handle GetTempBuffer (Size size)
1464: {
1465: OSErr error;
1466: Handle tempBuffer;
1467:
1468: if ( GetExpandMemProcessMgrExists() )
1469: tempBuffer = TempNewHandle(size, &error);
1470: else
1471: tempBuffer = NewHandleSys(size);
1472:
1473: return tempBuffer;
1474: }
1475: #endif /* TARGET_OS_MAC */
1476:
1477:
1478:
1479: static UInt32
1480: CountExtentBlocks(HFSPlusExtentRecord extents, Boolean hfsPlus)
1481: {
1482: UInt32 blocks;
1483:
1484: // grab the first 3
1485: blocks = extents[0].blockCount + extents[1].blockCount + extents[2].blockCount;
1486:
1487: if (hfsPlus)
1488: {
1489: UInt32 i;
1490:
1491: for (i = 3; i < kHFSPlusExtentDensity; ++i)
1492: blocks += extents[i].blockCount; // HFS Plus has additional extents
1493: }
1494:
1495: return blocks;
1496: }
1497:
1498:
1499: static void
1500: ConvertToHFSPlusExtent( const HFSExtentRecord oldExtents, HFSPlusExtentRecord newExtents)
1501: {
1502: UInt16 i;
1503:
1504: // go backwards so we can convert in place!
1505:
1506: for (i = kHFSPlusExtentDensity-1; i > 2; --i)
1507: {
1508: newExtents[i].blockCount = 0;
1509: newExtents[i].startBlock = 0;
1510: }
1511:
1512: newExtents[2].blockCount = oldExtents[2].blockCount;
1513: newExtents[2].startBlock = oldExtents[2].startBlock;
1514: newExtents[1].blockCount = oldExtents[1].blockCount;
1515: newExtents[1].startBlock = oldExtents[1].startBlock;
1516: newExtents[0].blockCount = oldExtents[0].blockCount;
1517: newExtents[0].startBlock = oldExtents[0].startBlock;
1518: }
1519:
1520:
1521: static OSErr
1522: GetFileExtentRecord( const ExtendedVCB *vcb, const FCB *fcb, HFSPlusExtentRecord extents)
1523: {
1524: OSErr err;
1525: ExtendedFCB *extendedFCB;
1526:
1527: err = noErr;
1528:
1529: if (vcb->vcbSigWord == kHFSSigWord)
1530: {
1531: ConvertToHFSPlusExtent(fcb->fcbExtRec, extents);
1532: }
1533: else
1534: {
1535: /* XXX PPD: Is there any reason this isn't the same for EVERY platform? */
1536: #if TARGET_OS_MAC
1537: extendedFCB = ParallelFCBFromRefnum( GetFileRefNumFromFCB(fcb) );
1538: #else
1539: extendedFCB = GetParallelFCB( GetFileRefNumFromFCB(fcb) );
1540: #endif
1541:
1542: if (extendedFCB == NULL)
1543: {
1544: if ( DEBUG_BUILD )
1545: DebugStr("\pFATAL: Extended FCB not found!");
1546: err = fsDSIntErr;
1547: }
1548: else
1549: {
1550: BlockMoveData(extendedFCB->extents, extents, sizeof(HFSPlusExtentRecord));
1551: }
1552: }
1553:
1554: return err;
1555: }
1556:
1557:
1558:
1559: //_______________________________________________________________________
1560: //
1561: // CheckCreateDate
1562: //
1563: // For HFS Plus volumes, make sure the createDate in the VolumeHeader
1564: // is the same as in the MDB. If not, just fix it.
1565: //
1566: //_______________________________________________________________________
1567:
1568: static OSErr CheckCreateDate (ExtendedVCB* volume, UInt32* consistencyStatus )
1569: {
1570: OSErr err;
1571: HFSMasterDirectoryBlock *mdb;
1572: UInt32 createDate;
1573:
1574: return noErr;
1575:
1576: //
1577: // Read in the MDB
1578: //
1579: #if TARGET_OS_MAC
1580: err = GetBlock_glue(gbReleaseMask, 2, (Ptr *) &mdb, volume->vcbVRefNum, volume);
1581: #else
1582: err = GetBlock_glue(gbReleaseMask, 2, (Ptr *) &mdb, kNoFileReference, volume);
1583: #endif
1584: if (err != noErr) return err;
1585:
1586: //
1587: // Make sure it really is a wrappered volume with an MDB
1588: //
1589: if (mdb->drSigWord != kHFSSigWord || mdb->drEmbedSigWord != kHFSPlusSigWord)
1590: return noErr; // must not be a wrapper, so nothing to fix
1591:
1592: createDate = mdb->drCrDate;
1593:
1594: if (volume->vcbCrDate != createDate) {
1595: volume->vcbCrDate = createDate; // fix create date in the VCB
1596: volume->vcbFlags |= kVCBFlagsVolumeDirty; // make it dirty so change will be written to volume header
1597: *consistencyStatus |= kHFSMinorRepairsWereMade; // the problem is now fixed
1598: }
1599:
1600: return noErr;
1601: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.