os/kernelhwsrv/userlibandfileserver/fileserver/sfat32/sl_scan32.cpp
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
sl@0
     1
// Copyright (c) 1998-2009 Nokia Corporation and/or its subsidiary(-ies).
sl@0
     2
// All rights reserved.
sl@0
     3
// This component and the accompanying materials are made available
sl@0
     4
// under the terms of the License "Eclipse Public License v1.0"
sl@0
     5
// which accompanies this distribution, and is available
sl@0
     6
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
sl@0
     7
//
sl@0
     8
// Initial Contributors:
sl@0
     9
// Nokia Corporation - initial contribution.
sl@0
    10
//
sl@0
    11
// Contributors:
sl@0
    12
//
sl@0
    13
// Description:
sl@0
    14
// f32\sfat32\sl_scan32.cpp
sl@0
    15
// ScanDrive code, specific for EFAT32.FSY
sl@0
    16
// 
sl@0
    17
//
sl@0
    18
sl@0
    19
/**
sl@0
    20
 @file
sl@0
    21
 @internalTechnology
sl@0
    22
*/
sl@0
    23
sl@0
    24
#include "sl_std.h"
sl@0
    25
#include "sl_scandrv.h"
sl@0
    26
sl@0
    27
const TInt KMaxScanDepth			= 20;   ///< Maximum scan depth of to avoid stack over flow 
sl@0
    28
const TInt KClusterListGranularity	= 8;    ///< Granularity of cluster list used for storage of clusters when KMaxScanDepth is reached
sl@0
    29
sl@0
    30
sl@0
    31
sl@0
    32
/**
sl@0
    33
    CScanDrive factory method
sl@0
    34
    @param  aMount the owning mount
sl@0
    35
*/
sl@0
    36
CScanDrive* CScanDrive::NewL(CFatMountCB* aMount)
sl@0
    37
	{
sl@0
    38
	if(!aMount)
sl@0
    39
		{
sl@0
    40
        ASSERT(0);
sl@0
    41
        User::Leave(KErrArgument);
sl@0
    42
        }
sl@0
    43
sl@0
    44
	CScanDrive* self=new (ELeave) CScanDrive();
sl@0
    45
	CleanupStack::PushL(self);
sl@0
    46
	self->ConstructL(aMount);
sl@0
    47
	CleanupStack::Pop();
sl@0
    48
	
sl@0
    49
    return self;
sl@0
    50
	}
sl@0
    51
sl@0
    52
sl@0
    53
CScanDrive::~CScanDrive()
sl@0
    54
	{
sl@0
    55
	for(TUint i=0; i<KMaxArrayDepth && iClusterListArray[i]!=NULL; ++i)
sl@0
    56
		{
sl@0
    57
		iClusterListArray[i]->Close();
sl@0
    58
		delete iClusterListArray[i];
sl@0
    59
		}
sl@0
    60
sl@0
    61
    iMediaFatBits.Close();
sl@0
    62
    iScanFatBits.Close();
sl@0
    63
sl@0
    64
	}
sl@0
    65
sl@0
    66
/**
sl@0
    67
    Creates the structure of this class.
sl@0
    68
    @param aMount The owning mount
sl@0
    69
*/
sl@0
    70
void CScanDrive::ConstructL(CFatMountCB* aMount)
sl@0
    71
    {
sl@0
    72
    ASSERT(aMount);
sl@0
    73
sl@0
    74
    //--- setting up 
sl@0
    75
    iMount=aMount;
sl@0
    76
    iGenericError = ENoErrors;
sl@0
    77
    iDirError     = ENoDirError;  
sl@0
    78
    iMaxClusters  = iMount->UsableClusters()+KFatFirstSearchCluster; //-- UsableClusters() doesn't count first 2 unused clusers
sl@0
    79
    //------------------------------
sl@0
    80
	
sl@0
    81
    //-- create bit vectors that will represent FAT on media and reconstructed by ScanDrive. Each bit in the vector represents 1 FAT cluster.
sl@0
    82
    const TUint32 KClustersNum = MaxClusters();
sl@0
    83
sl@0
    84
    CleanupClosePushL(iMediaFatBits);
sl@0
    85
    CleanupClosePushL(iScanFatBits);
sl@0
    86
sl@0
    87
    iMediaFatBits.CreateL(KClustersNum);
sl@0
    88
    iScanFatBits.CreateL(KClustersNum);
sl@0
    89
sl@0
    90
    CleanupStack::Pop(&iScanFatBits);
sl@0
    91
    CleanupStack::Pop(&iMediaFatBits);
sl@0
    92
    }
sl@0
    93
sl@0
    94
//----------------------------------------------------------------------------------------------------
sl@0
    95
/**
sl@0
    96
    FAT type-agnostic parser. Reads whole FAT and sets up a bit vector.
sl@0
    97
    for FAT12/16 it's OK, because the FAT12/16 is fully cached.
sl@0
    98
*/
sl@0
    99
void CScanDrive::DoParseFatL()
sl@0
   100
    {
sl@0
   101
    const TInt KMaxClusters = MaxClusters();
sl@0
   102
sl@0
   103
    iMediaFatBits.Fill(0);
sl@0
   104
sl@0
   105
    __PRINT1(_L("CScanDrive::DoParseFatL(), clusters:%d"), KMaxClusters);
sl@0
   106
sl@0
   107
    for(TInt i=KFatFirstSearchCluster; i<KMaxClusters; ++i)
sl@0
   108
	    {
sl@0
   109
        const TUint32 nFatEntry = ReadFatL(i);
sl@0
   110
       
sl@0
   111
        //-- each '1' bit represents a used cluster 
sl@0
   112
        if(nFatEntry != KSpareCluster) 
sl@0
   113
            iMediaFatBits.SetBit(i);
sl@0
   114
	    }
sl@0
   115
    }
sl@0
   116
sl@0
   117
//----------------------------------------------------------------------------------------------------
sl@0
   118
/**
sl@0
   119
    Parse FAT32 buffer.
sl@0
   120
    @param  aBuf            buffer, containing FAT32 entries (current portion of FAT)
sl@0
   121
    @param  aCurrFatEntry   current FAT entry processed
sl@0
   122
*/
sl@0
   123
void CScanDrive::DoParseFat32Buf(const TPtrC8& aBuf, TUint32& aCurrFatEntry)
sl@0
   124
    {
sl@0
   125
    ASSERT((aBuf.Size() & (sizeof(TFat32Entry)-1)) == 0);
sl@0
   126
    
sl@0
   127
    const TInt KNumEntries = aBuf.Size() >> KFat32EntrySzLog2;
sl@0
   128
    const TFat32Entry* const pFatEntry = (const TFat32Entry*)(aBuf.Ptr()); 
sl@0
   129
sl@0
   130
    for(TInt i=0; i<KNumEntries; ++i)
sl@0
   131
        {
sl@0
   132
        if(aCurrFatEntry >= KFatFirstSearchCluster)
sl@0
   133
            {
sl@0
   134
            if((pFatEntry[i] & KFat32EntryMask) != KSpareCluster)
sl@0
   135
                {//-- found a non-free FAT32 entry
sl@0
   136
                iMediaFatBits.SetBit(aCurrFatEntry);
sl@0
   137
                }
sl@0
   138
            }
sl@0
   139
        ++aCurrFatEntry;    
sl@0
   140
        }
sl@0
   141
    }
sl@0
   142
sl@0
   143
//----------------------------------------------------------------------------------------------------
sl@0
   144
/**
sl@0
   145
    A specialised method to read and parse FAT32 using a larger buffer.
sl@0
   146
    1. Larger buffer gives better read performance
sl@0
   147
    2. using dedicated buffer doesn't trash FAT32 LRU cache.
sl@0
   148
*/
sl@0
   149
void CScanDrive::DoParseFat32L()
sl@0
   150
    {
sl@0
   151
    const TInt    KNumClusters = MaxClusters();
sl@0
   152
    
sl@0
   153
    __PRINT1(_L("CScanDrive::DoParseFat32L(), clusters:%d"), KNumClusters);
sl@0
   154
sl@0
   155
    ASSERT(iMount->FatType() == EFat32);
sl@0
   156
    
sl@0
   157
    const TUint32 KFat1StartPos = iMount->StartOfFatInBytes();
sl@0
   158
    const TUint32 KFatSize      = KNumClusters * sizeof(TFat32Entry); //-- usable size of one FAT.
sl@0
   159
sl@0
   160
    const TUint32 KFatBufSz = 32*K1KiloByte; //-- buffer size for FAT reading. 32K seems to be optimal size
sl@0
   161
sl@0
   162
    iMediaFatBits.Fill(0);
sl@0
   163
sl@0
   164
    RBuf8 buf;
sl@0
   165
    CleanupClosePushL(buf);
sl@0
   166
sl@0
   167
    //-- allocate memory for FAT parse buffer
sl@0
   168
    buf.CreateMaxL(KFatBufSz);
sl@0
   169
sl@0
   170
    //-- read FAT directly from the media into the large buffer and parse it
sl@0
   171
    TUint32 rem = KFatSize;
sl@0
   172
    TUint32 mediaPos = KFat1StartPos;   
sl@0
   173
    TUint32 currFatEntry = 0;
sl@0
   174
sl@0
   175
    while(rem)
sl@0
   176
        {
sl@0
   177
        const TUint32 bytesToRead=Min(rem, KFatBufSz);
sl@0
   178
        TPtrC8 ptrData(buf.Ptr(), bytesToRead);
sl@0
   179
sl@0
   180
        //-- read portion of the FAT into buffer
sl@0
   181
        User::LeaveIfError(iMount->LocalDrive()->Read(mediaPos, bytesToRead, buf)); 
sl@0
   182
sl@0
   183
        //-- parse the buffer and populate bit vector
sl@0
   184
        DoParseFat32Buf(ptrData, currFatEntry);
sl@0
   185
        
sl@0
   186
        mediaPos += bytesToRead;
sl@0
   187
        rem -= bytesToRead;
sl@0
   188
        }
sl@0
   189
sl@0
   190
    buf.Close();
sl@0
   191
    CleanupStack::PopAndDestroy(&buf); 
sl@0
   192
    }
sl@0
   193
sl@0
   194
sl@0
   195
sl@0
   196
//----------------------------------------------------------------------------------------------------
sl@0
   197
/**
sl@0
   198
    Sets up a bit list representation of the media fat
sl@0
   199
    Reads whole FAT and sets '1' bits in the bit vector corresponding to the occupied clusters.
sl@0
   200
*/
sl@0
   201
void CScanDrive::ReadMediaFatL()
sl@0
   202
    {
sl@0
   203
    ASSERT(iMount->ConsistentState());
sl@0
   204
    
sl@0
   205
    TInt nRes;
sl@0
   206
sl@0
   207
    if(iMount->FatType() == EFat32)
sl@0
   208
        {//-- for FAT32 try to use specialised method of parsing
sl@0
   209
        TRAP(nRes, DoParseFat32L())
sl@0
   210
        if(nRes == KErrNone)
sl@0
   211
            return;
sl@0
   212
        }
sl@0
   213
sl@0
   214
sl@0
   215
    //-- use old FAT-agnostic parsing
sl@0
   216
    DoParseFatL();
sl@0
   217
    }
sl@0
   218
sl@0
   219
sl@0
   220
/**
sl@0
   221
    @return True if a directory error has been found
sl@0
   222
*/
sl@0
   223
TBool CScanDrive::IsDirError() const
sl@0
   224
	{
sl@0
   225
	return(iDirError!=0);
sl@0
   226
	}
sl@0
   227
sl@0
   228
/**
sl@0
   229
    After StartL() and finishing allows us to know if there were any problems discovered at all.
sl@0
   230
    The client may wish to remount the filesystem if there were errors.
sl@0
   231
sl@0
   232
    @return The code describing the problem.
sl@0
   233
*/
sl@0
   234
CScanDrive::TGenericError CScanDrive::ProblemsDiscovered() const
sl@0
   235
{
sl@0
   236
sl@0
   237
    if(IsDirError()) 
sl@0
   238
        return EScanDriveDirError;
sl@0
   239
    else
sl@0
   240
        return iGenericError;
sl@0
   241
}
sl@0
   242
sl@0
   243
/**
sl@0
   244
    Sets the flag indicating than there are errors in filesystem structure
sl@0
   245
    See ProblemsDiscovered()
sl@0
   246
sl@0
   247
    @param  aError a code describing the error
sl@0
   248
*/
sl@0
   249
void CScanDrive::IndicateErrorsFound(TGenericError aError)
sl@0
   250
{
sl@0
   251
    ASSERT(aError != ENoErrors);
sl@0
   252
    iGenericError = aError;
sl@0
   253
}
sl@0
   254
sl@0
   255
sl@0
   256
//----------------------------------------------------------------------------------------------------
sl@0
   257
/**
sl@0
   258
    Start the scanner. The this calss description about what it actually does.
sl@0
   259
    @param  aMode specifies the operational mode.
sl@0
   260
*/
sl@0
   261
void CScanDrive::StartL(TScanDriveMode aMode)
sl@0
   262
	{
sl@0
   263
	__PRINT2(_L("CScanDrive::StartL(%d), drive:%d"), aMode, iMount->DriveNumber());
sl@0
   264
    iScanDriveMode = aMode;
sl@0
   265
sl@0
   266
    //-- used for measuring time
sl@0
   267
    TTime   timeStart;
sl@0
   268
    TTime   timeEnd;
sl@0
   269
    
sl@0
   270
    timeStart.UniversalTime(); //-- take start time
sl@0
   271
sl@0
   272
	ReadMediaFatL();
sl@0
   273
sl@0
   274
        //timeEnd.UniversalTime(); //-- take end time
sl@0
   275
        //elapsedTime = (TInt)( (timeEnd.MicroSecondsFrom(timeStart)).Int64() / K1mSec);
sl@0
   276
        //__PRINT1(_L("#@@@ CScanDrive #1:%d ms "), elapsedTime);
sl@0
   277
sl@0
   278
	CheckDirStructureL();
sl@0
   279
sl@0
   280
    //-- uncomments a line below if you need to compare real and restored FAT tables and print out all differences
sl@0
   281
    //CompareFatsL(EFalse);
sl@0
   282
sl@0
   283
        //timeEnd.UniversalTime(); //-- take end time
sl@0
   284
        //elapsedTime = (TInt)( (timeEnd.MicroSecondsFrom(timeStart)).Int64() / K1mSec);
sl@0
   285
        //__PRINT1(_L("#@@@ CScanDrive #2:%d ms "), elapsedTime);
sl@0
   286
sl@0
   287
    if(CheckDiskMode())
sl@0
   288
        {//-- in check disk mode it is nesessarily just to detech FS errors
sl@0
   289
        CompareFatsL(ETrue); //-- will stop on the first error found
sl@0
   290
        }
sl@0
   291
    else
sl@0
   292
        {//-- In ScanDrive mode we need to find and fix Rugged FAT artefacts.
sl@0
   293
     
sl@0
   294
        if(IsDirError())
sl@0
   295
		    FixupDirErrorL();
sl@0
   296
sl@0
   297
	    CompareAndFixFatsL();
sl@0
   298
        }
sl@0
   299
sl@0
   300
	PrintErrors();
sl@0
   301
sl@0
   302
sl@0
   303
    timeEnd.UniversalTime(); //-- take end time
sl@0
   304
    const TInt elapsedTime = (TInt)( (timeEnd.MicroSecondsFrom(timeStart)).Int64() / K1mSec);
sl@0
   305
    (void)elapsedTime;
sl@0
   306
sl@0
   307
    __PRINT1(_L("CScanDrive: Directories visisted = %d\n"),iDirsChecked);
sl@0
   308
    __PRINT1(_L("#@@@ CScanDrive time taken:%d ms "), elapsedTime);
sl@0
   309
    
sl@0
   310
    
sl@0
   311
sl@0
   312
	return;
sl@0
   313
	}
sl@0
   314
sl@0
   315
//----------------------------------------------------------------------------------------------------
sl@0
   316
/**
sl@0
   317
    Fix errors detected by the drive scan
sl@0
   318
 
sl@0
   319
    @leave System wide error code
sl@0
   320
*/
sl@0
   321
void CScanDrive::FixupDirErrorL()
sl@0
   322
	{
sl@0
   323
	ASSERT(!CheckDiskMode());
sl@0
   324
    
sl@0
   325
    if(!IsDirError())
sl@0
   326
		return;
sl@0
   327
	
sl@0
   328
    if(iDirError==EScanMatchingEntry)
sl@0
   329
		{
sl@0
   330
		FindSameStartClusterL();
sl@0
   331
		FixMatchingEntryL();
sl@0
   332
		}
sl@0
   333
	else
sl@0
   334
		{
sl@0
   335
        FixPartEntryL();
sl@0
   336
        }
sl@0
   337
sl@0
   338
    IndicateErrorsFound(EScanDriveDirError); //-- indicate that we have found errors
sl@0
   339
	}
sl@0
   340
sl@0
   341
//----------------------------------------------------------------------------------------------------
sl@0
   342
/**
sl@0
   343
    Find positions of entries with same start cluster for error correction, searches
sl@0
   344
    the whole volume. Starts at the root directory. 
sl@0
   345
sl@0
   346
    @leave System wide error code
sl@0
   347
*/
sl@0
   348
void CScanDrive::FindSameStartClusterL()
sl@0
   349
	{
sl@0
   350
	TInt err=FindStartClusterL(iMount->RootIndicator());
sl@0
   351
	if(err==KErrNone)
sl@0
   352
		return;
sl@0
   353
	
sl@0
   354
    for(TUint i=0;i<KMaxArrayDepth && iClusterListArray[i]!=NULL;++i)
sl@0
   355
		{
sl@0
   356
		RArray<TInt>* clusterList=iClusterListArray[i];
sl@0
   357
		for(TInt j=0;j<clusterList->Count();++j)
sl@0
   358
			{
sl@0
   359
			iRecursiveDepth=0;
sl@0
   360
			err=FindStartClusterL((*clusterList)[j]);
sl@0
   361
			if(err==KErrNone)
sl@0
   362
				return;
sl@0
   363
			}
sl@0
   364
		}
sl@0
   365
sl@0
   366
	__ASSERT_ALWAYS(err==KErrNone,User::Leave(KErrNotFound));
sl@0
   367
	}
sl@0
   368
sl@0
   369
//----------------------------------------------------------------------------------------------------
sl@0
   370
/**
sl@0
   371
    Scan through directory structure looking for start cluster found in iMatching
sl@0
   372
sl@0
   373
    @param aDirCluster Start cluster for scan to start
sl@0
   374
    @return System wide error value
sl@0
   375
    @leave 
sl@0
   376
*/
sl@0
   377
TInt CScanDrive::FindStartClusterL(TInt aDirCluster)
sl@0
   378
	{
sl@0
   379
	__PRINT1(_L("CScanDrive::FindStartCluster dirCluster=%d"),aDirCluster);
sl@0
   380
	__ASSERT_ALWAYS(aDirCluster>=iMount->RootIndicator(),User::Leave(KErrCorrupt));
sl@0
   381
	if(++iRecursiveDepth==KMaxScanDepth)
sl@0
   382
		{
sl@0
   383
		--iRecursiveDepth;
sl@0
   384
		return(KErrNotFound);
sl@0
   385
		}
sl@0
   386
	TEntryPos entryPos(aDirCluster,0);
sl@0
   387
	TInt dirEntries=0;
sl@0
   388
	FOREVER
sl@0
   389
		{
sl@0
   390
		TFatDirEntry entry;
sl@0
   391
		ReadDirEntryL(entryPos,entry);
sl@0
   392
		if(entry.IsParentDirectory()||entry.IsCurrentDirectory()||entry.IsErased())
sl@0
   393
			{
sl@0
   394
			if(IsEndOfRootDir(entryPos))
sl@0
   395
				break;
sl@0
   396
			MoveToNextEntryL(entryPos);
sl@0
   397
			continue;
sl@0
   398
			}
sl@0
   399
		if(entry.IsEndOfDirectory())
sl@0
   400
			break;
sl@0
   401
		TBool isComplete;
sl@0
   402
		TEntryPos vfatPos=entryPos;
sl@0
   403
		isComplete=MoveToVFatEndL(entryPos,entry,dirEntries);
sl@0
   404
		__ASSERT_ALWAYS(isComplete,User::Leave(KErrBadName));
sl@0
   405
sl@0
   406
		TInt err=CheckEntryClusterL(entry,vfatPos);
sl@0
   407
		if(err==KErrNone)
sl@0
   408
			{
sl@0
   409
			--iRecursiveDepth;
sl@0
   410
			return(err);
sl@0
   411
			}
sl@0
   412
sl@0
   413
		if(IsEndOfRootDir(entryPos))
sl@0
   414
			break;
sl@0
   415
sl@0
   416
		MoveToNextEntryL(entryPos);
sl@0
   417
		}
sl@0
   418
	--iRecursiveDepth;
sl@0
   419
	return(KErrNotFound);
sl@0
   420
	}
sl@0
   421
sl@0
   422
//----------------------------------------------------------------------------------------------------
sl@0
   423
/**
sl@0
   424
    Procces aEntry to find matching start cluster
sl@0
   425
sl@0
   426
    @param aEntry Directory entry to check
sl@0
   427
    @param aEntryPos Position of directory to check
sl@0
   428
    @return System wide error value
sl@0
   429
    @leave 
sl@0
   430
*/
sl@0
   431
TInt CScanDrive::CheckEntryClusterL(const TFatDirEntry& aEntry, const TEntryPos& aEntryPos)
sl@0
   432
	{
sl@0
   433
	__PRINT(_L("CScanDrive::CheckEntryClusterL"));
sl@0
   434
	if((TUint)iMount->StartCluster(aEntry)==iMatching.iStartCluster)
sl@0
   435
		{
sl@0
   436
		TBool complete=AddMatchingEntryL(aEntryPos);
sl@0
   437
		if(complete)
sl@0
   438
			return(KErrNone);
sl@0
   439
		}
sl@0
   440
	else if(aEntry.Attributes()&KEntryAttDir)
sl@0
   441
		return(FindStartClusterL(iMount->StartCluster(aEntry)));
sl@0
   442
sl@0
   443
	return(KErrNotFound);
sl@0
   444
	}
sl@0
   445
sl@0
   446
//----------------------------------------------------------------------------------------------------
sl@0
   447
/**
sl@0
   448
    Checks directory structure for errors, can be considered the start point of the scan.  
sl@0
   449
    Handles recursion depth to avoid stack overflow.
sl@0
   450
sl@0
   451
    @leave System wide error code
sl@0
   452
*/
sl@0
   453
void CScanDrive::CheckDirStructureL()
sl@0
   454
	{
sl@0
   455
	CheckDirL(iMount->RootIndicator());
sl@0
   456
	// Due to recursive nature of CheckDirL when a depth of
sl@0
   457
	// KMaxScanDepth is reached clusters are stored in a list
sl@0
   458
	// and passed into CheckDirL afresh
sl@0
   459
sl@0
   460
	for(TUint i=0;i<KMaxArrayDepth && iClusterListArray[i]!=NULL;++i)
sl@0
   461
		{
sl@0
   462
		RArray<TInt>* clusterList=iClusterListArray[i];
sl@0
   463
		++iListArrayIndex;
sl@0
   464
		for(TInt j=0;j<clusterList->Count();++j)
sl@0
   465
			{
sl@0
   466
			iRecursiveDepth=0;
sl@0
   467
			CheckDirL((*clusterList)[j]);
sl@0
   468
			}
sl@0
   469
		}
sl@0
   470
	}
sl@0
   471
sl@0
   472
sl@0
   473
//----------------------------------------------------------------------------------------------------
sl@0
   474
/**
sl@0
   475
    This function is called recursively with Process entry untill the whole volume has been scanned.
sl@0
   476
    Each directory entry is scanned for errors, these are recorded for later fixing. 
sl@0
   477
sl@0
   478
    @param aCluster Directory cluster to start checking
sl@0
   479
    @leave System wide error codes
sl@0
   480
*/
sl@0
   481
void CScanDrive::CheckDirL(TUint32 aCluster)
sl@0
   482
	{
sl@0
   483
	__PRINT1(_L("CScanDrive::CheckDirL aCluster=%d"),aCluster);
sl@0
   484
sl@0
   485
	// check depth of recursion
sl@0
   486
	if(++iRecursiveDepth==KMaxScanDepth)
sl@0
   487
		{
sl@0
   488
		AddToClusterListL(aCluster);
sl@0
   489
		--iRecursiveDepth;
sl@0
   490
		return;
sl@0
   491
		}
sl@0
   492
sl@0
   493
	++iDirsChecked;
sl@0
   494
sl@0
   495
	TEntryPos entryPos(aCluster,0);
sl@0
   496
	TInt dirEntries=0;
sl@0
   497
	FOREVER
sl@0
   498
		{
sl@0
   499
		TFatDirEntry entry;
sl@0
   500
		ReadDirEntryL(entryPos,entry);
sl@0
   501
		if(!iMount->IsEndOfClusterCh(entryPos.iCluster))
sl@0
   502
			++dirEntries;
sl@0
   503
sl@0
   504
		if(entry.IsParentDirectory() || entry.IsCurrentDirectory() || entry.IsErased())
sl@0
   505
			{
sl@0
   506
			if(IsEndOfRootDir(entryPos))
sl@0
   507
				break;
sl@0
   508
sl@0
   509
			MoveToNextEntryL(entryPos);
sl@0
   510
			continue;
sl@0
   511
			}
sl@0
   512
sl@0
   513
		if(entry.IsEndOfDirectory())
sl@0
   514
			{
sl@0
   515
			if(aCluster)	
sl@0
   516
				RecordClusterChainL(aCluster,dirEntries<<KSizeOfFatDirEntryLog2);
sl@0
   517
sl@0
   518
			break;
sl@0
   519
			}
sl@0
   520
sl@0
   521
		TEntryPos origPos=entryPos;
sl@0
   522
		TFatDirEntry origEntry=entry;
sl@0
   523
		TInt origDirEntries=dirEntries;
sl@0
   524
		
sl@0
   525
		const TBool isComplete = MoveToVFatEndL(entryPos,entry,dirEntries);
sl@0
   526
        
sl@0
   527
        if(!isComplete && CheckDiskMode())
sl@0
   528
            {//-- broken VFAT entryset; in CheckDisk mode this is the FS error, abort further activity
sl@0
   529
                IndicateErrorsFound(EInvalidEntrySize);
sl@0
   530
                User::Leave(KErrCorrupt);
sl@0
   531
            }
sl@0
   532
sl@0
   533
        // Only assume that this is a corrupted VFAT entry if the VFAT attributes are set; 
sl@0
   534
		// assuming a non-VFAT corrupted entry is a VFAT entry is dangerous as we then assume that the 
sl@0
   535
		// first byte is a count of entries to skip, thus completely invalidating the next <n> directories.
sl@0
   536
		
sl@0
   537
        //-- this code seems to deal with one of the Rugged FAT artefacts: partially deleted VFAT entryset, when DOS entry is deleted first
sl@0
   538
        //-- and delettion of VFAT ones had failed
sl@0
   539
        if(!isComplete && origEntry.IsVFatEntry())
sl@0
   540
			{
sl@0
   541
			AddPartialVFatL(origPos,origEntry);
sl@0
   542
			if(!IsEofF(entryPos.iCluster))
sl@0
   543
				{
sl@0
   544
				TInt toMove=origEntry.NumFollowing()-(dirEntries-origDirEntries);
sl@0
   545
				if(toMove)
sl@0
   546
					MovePastEntriesL(entryPos,entry,toMove,dirEntries);
sl@0
   547
				}
sl@0
   548
			else
sl@0
   549
				{
sl@0
   550
				// we fell off the end of the directory file, so just strip this
sl@0
   551
				// incomplete long file name entry
sl@0
   552
				dirEntries = origDirEntries;
sl@0
   553
				}
sl@0
   554
			}
sl@0
   555
		else
sl@0
   556
			{
sl@0
   557
            ProcessEntryL(entry);
sl@0
   558
            }
sl@0
   559
sl@0
   560
		if(IsEndOfRootDir(entryPos))
sl@0
   561
			break;
sl@0
   562
sl@0
   563
		MoveToNextEntryL(entryPos);
sl@0
   564
		}
sl@0
   565
	--iRecursiveDepth;
sl@0
   566
	}
sl@0
   567
sl@0
   568
//----------------------------------------------------------------------------------------------------
sl@0
   569
/**
sl@0
   570
    Process non trivial entries, such as files, if they are correct by filling out their 
sl@0
   571
    cluster allocation in the bit packed Fat table. If it comes accross a directory 
sl@0
   572
    CheckDirL will be called.
sl@0
   573
sl@0
   574
    @param aEntry Directory entry to check
sl@0
   575
    @leave System wide error code
sl@0
   576
*/
sl@0
   577
void CScanDrive::ProcessEntryL(const TFatDirEntry& aEntry)
sl@0
   578
	{
sl@0
   579
	__PRINT(_L("CScanDrive::ProcessEntryL"));
sl@0
   580
	TInt entryAtt=aEntry.Attributes();
sl@0
   581
sl@0
   582
	__ASSERT_ALWAYS(!(entryAtt&~KEntryAttMaskSupported)&&!aEntry.IsErased(),User::Leave(KErrCorrupt));
sl@0
   583
	
sl@0
   584
    if(!(entryAtt&(KEntryAttDir|KEntryAttVolume)) && iMount->StartCluster(aEntry)>0)
sl@0
   585
		{//-- this is a file with length >0. Check that its cluster chain corresponds to its size
sl@0
   586
        RecordClusterChainL(iMount->StartCluster(aEntry),(TUint) aEntry.Size());
sl@0
   587
        }
sl@0
   588
	else if(entryAtt&KEntryAttDir)
sl@0
   589
		{//-- this is the directory, walk into it
sl@0
   590
        CheckDirL(iMount->StartCluster(aEntry));
sl@0
   591
        }
sl@0
   592
	}
sl@0
   593
sl@0
   594
//----------------------------------------------------------------------------------------------------
sl@0
   595
/**
sl@0
   596
    Walks the cluster chain for a correct file or directory, checks that the cluster 
sl@0
   597
    has not already been used and that the correct number of clusters are allocated for the 
sl@0
   598
    size of file. Registers cluster as used, if correct.
sl@0
   599
sl@0
   600
    @param aCluster Cluster chain start point
sl@0
   601
    @param aSizeInBytes Size of the file or directory in bytes
sl@0
   602
    @leave System wide error values
sl@0
   603
*/
sl@0
   604
void CScanDrive::RecordClusterChainL(TInt aCluster, TUint aSizeInBytes)
sl@0
   605
	{
sl@0
   606
	__PRINT2(_L("CScanDrive::RecordClusterChainL() cl:%d, sz:%d") ,aCluster, aSizeInBytes);
sl@0
   607
	__ASSERT_ALWAYS(aCluster>0, User::Leave(KErrCorrupt));
sl@0
   608
	
sl@0
   609
    TUint clusterCount;
sl@0
   610
	
sl@0
   611
    if(aSizeInBytes==0)
sl@0
   612
		clusterCount=1;
sl@0
   613
	else
sl@0
   614
		{
sl@0
   615
        const TUint64 tmp = aSizeInBytes + Pow2_64(iMount->ClusterSizeLog2()) - 1;
sl@0
   616
        clusterCount = (TUint) (tmp >> iMount->ClusterSizeLog2());
sl@0
   617
        }
sl@0
   618
sl@0
   619
	TInt startCluster=aCluster;
sl@0
   620
	while(clusterCount)
sl@0
   621
		{
sl@0
   622
        if(IsClusterUsedL(aCluster))
sl@0
   623
			{//-- this cluster already seems to belong to some other object; crosslinked cluster chain. Can't fix it.
sl@0
   624
			if(CheckDiskMode())
sl@0
   625
                {//-- in check disk mode this is a FS error; Indicate error and abort furter scanning
sl@0
   626
                __PRINT1(_L("CScanDrive::RecordClusterChainL #1 %d"),aCluster); 
sl@0
   627
                IndicateErrorsFound(EClusterAlreadyInUse);
sl@0
   628
                User::Leave(KErrCorrupt);
sl@0
   629
                }
sl@0
   630
            
sl@0
   631
            __ASSERT_ALWAYS(!IsDirError() && iMatching.iStartCluster==0 && aCluster==startCluster,User::Leave(KErrCorrupt));
sl@0
   632
			iMatching.iStartCluster=aCluster;
sl@0
   633
			iDirError=EScanMatchingEntry;		//ERROR POINT
sl@0
   634
            IndicateErrorsFound(EScanDriveDirError); //-- indicate that we have found errors
sl@0
   635
			return;
sl@0
   636
			}
sl@0
   637
sl@0
   638
		
sl@0
   639
        if(clusterCount==1)
sl@0
   640
			{
sl@0
   641
			if(!iMount->IsEndOfClusterCh(ReadFatL(aCluster)))
sl@0
   642
				{//-- seems to be a rugged FAT artefact; File truncation had failed before and now file length is less than
sl@0
   643
                 //-- the corresponding cluster chain shall be. It will be truncated.
sl@0
   644
				iTruncationCluster = aCluster;								
sl@0
   645
                
sl@0
   646
                if(CheckDiskMode())
sl@0
   647
                    {//-- in check disk mode this is a FS error; Indicate error and abort furter scanning
sl@0
   648
                    __PRINT1(_L("CScanDrive::RecordClusterChainL #2 %d"),aCluster); 
sl@0
   649
                    IndicateErrorsFound(EInvalidEntrySize);
sl@0
   650
                    User::Leave(KErrCorrupt);
sl@0
   651
                    }
sl@0
   652
                }
sl@0
   653
sl@0
   654
            //__PRINT1(_L("#--: %d -> EOC"), aCluster); 
sl@0
   655
            MarkClusterUsedL(aCluster);
sl@0
   656
			return;
sl@0
   657
			}
sl@0
   658
		else
sl@0
   659
			{
sl@0
   660
			const TUint clusterVal=ReadFatL(aCluster);
sl@0
   661
sl@0
   662
            //__PRINT2(_L("#--: %d -> %d"), aCluster, clusterVal); 
sl@0
   663
			
sl@0
   664
            __ASSERT_ALWAYS(!IsEofF(clusterVal) && clusterVal !=KSpareCluster, User::Leave(KErrCorrupt));
sl@0
   665
			MarkClusterUsedL(aCluster);
sl@0
   666
			aCluster=clusterVal;
sl@0
   667
			--clusterCount;
sl@0
   668
			}
sl@0
   669
		
sl@0
   670
        }//while(clusterCount)
sl@0
   671
	}
sl@0
   672
sl@0
   673
//----------------------------------------------------------------------------------------------------
sl@0
   674
/**
sl@0
   675
    Move to dos entry, checking all vfat entry ID numbers are in sequence.
sl@0
   676
    Assumes aEntry is not erased
sl@0
   677
sl@0
   678
    @param aPos Position of the entry to move from, returns with new position
sl@0
   679
    @param aEntry The Dos entry after the Vfat entries on return
sl@0
   680
    @param aDirLength Running total of the length of the directory in entries
sl@0
   681
    @leave System wide error codes
sl@0
   682
    @return EFalse if not valid vfat entries or dos entry, else returns ETrue
sl@0
   683
*/
sl@0
   684
TBool CScanDrive::MoveToVFatEndL(TEntryPos& aPos,TFatDirEntry& aEntry,TInt& aDirLength)
sl@0
   685
	{
sl@0
   686
	__PRINT2(_L("CScanDrive::MoveToVFatEndL cluster=%d,pos=%d"),aPos.iCluster,aPos.iPos);
sl@0
   687
	if(!aEntry.IsVFatEntry())
sl@0
   688
		return IsDosEntry(aEntry);
sl@0
   689
sl@0
   690
	TInt toFollow=aEntry.NumFollowing();
sl@0
   691
	__ASSERT_ALWAYS(toFollow>0 && !aEntry.IsErased(), User::Leave(KErrCorrupt));
sl@0
   692
sl@0
   693
	FOREVER
sl@0
   694
		{
sl@0
   695
		MoveToNextEntryL(aPos);
sl@0
   696
		ReadDirEntryL(aPos,aEntry);
sl@0
   697
		++aDirLength;
sl@0
   698
		--toFollow;
sl@0
   699
		if(!toFollow)
sl@0
   700
			break;
sl@0
   701
		
sl@0
   702
        if(!IsValidVFatEntry(aEntry,toFollow))
sl@0
   703
			return(EFalse);
sl@0
   704
		}
sl@0
   705
	
sl@0
   706
    return(IsDosEntry(aEntry));
sl@0
   707
	}
sl@0
   708
sl@0
   709
//----------------------------------------------------------------------------------------------------
sl@0
   710
/**
sl@0
   711
    Check if an entry is valid VFat
sl@0
   712
sl@0
   713
    @param aEntry Entry to check
sl@0
   714
    @param aPrevNum Number into VFat entries for a dos entry to ensure in correct position
sl@0
   715
    @return ETrue if aEntry is a valid vfat entry
sl@0
   716
*/
sl@0
   717
TBool CScanDrive::IsValidVFatEntry(const TFatDirEntry& aEntry,TInt aPrevNum)const
sl@0
   718
	{
sl@0
   719
	if(aEntry.IsErased()||!aEntry.IsVFatEntry())
sl@0
   720
		return EFalse;
sl@0
   721
sl@0
   722
	return(aEntry.NumFollowing()==aPrevNum);
sl@0
   723
	}
sl@0
   724
sl@0
   725
//----------------------------------------------------------------------------------------------------
sl@0
   726
/**
sl@0
   727
    Check if an entry is a Dos entry
sl@0
   728
sl@0
   729
    @param aEntry Entry to check
sl@0
   730
    @return ETrue if aEntry is a dos entry
sl@0
   731
*/
sl@0
   732
TBool CScanDrive::IsDosEntry(const TFatDirEntry& aEntry)const
sl@0
   733
	{
sl@0
   734
	TBool res = !(aEntry.Attributes()&~KEntryAttMaskSupported) && !aEntry.IsErased() && !aEntry.IsVFatEntry() && !aEntry.IsEndOfDirectory();
sl@0
   735
	return res;
sl@0
   736
	} 
sl@0
   737
sl@0
   738
//----------------------------------------------------------------------------------------------------
sl@0
   739
/**
sl@0
   740
    Add partial entry to iPartEntry under the error condition of not all Vfat entries being present
sl@0
   741
sl@0
   742
    @param aStartPos Position of the Dos entry associated with the VFat entries
sl@0
   743
    @param aEntry Directory Entry of the Dos entry associated with the VFat entries
sl@0
   744
    @leave KErrCorrupt Occurs if the entry is not valid
sl@0
   745
*/
sl@0
   746
void CScanDrive::AddPartialVFatL(const TEntryPos& aStartPos, const TFatDirEntry& aEntry)
sl@0
   747
	{
sl@0
   748
	__PRINT2(_L("CScanDrive::AddPartialVFatL cluster=%d pos=%d"),aStartPos.iCluster,aStartPos.iPos);
sl@0
   749
	__ASSERT_ALWAYS(!IsDirError(),User::Leave(KErrCorrupt));
sl@0
   750
	iPartEntry.iEntryPos=aStartPos;
sl@0
   751
	iPartEntry.iEntry=aEntry;
sl@0
   752
	iDirError=EScanPartEntry;
sl@0
   753
	}
sl@0
   754
sl@0
   755
//----------------------------------------------------------------------------------------------------
sl@0
   756
/**
sl@0
   757
    Add entry position to iMatching
sl@0
   758
sl@0
   759
    @param aEntryPos Position of the entry with the matching entry
sl@0
   760
    @leave KErrCorrupt if the start cluster is 0 or more that two matching entries occurs
sl@0
   761
    @return 
sl@0
   762
*/
sl@0
   763
TBool CScanDrive::AddMatchingEntryL(const TEntryPos& aEntryPos)
sl@0
   764
	{
sl@0
   765
	__PRINT2(_L("CScanDrive::AddMatchingEntryL cluster=%d pos=%d"),aEntryPos.iCluster,aEntryPos.iPos);
sl@0
   766
	__ASSERT_ALWAYS(iMatching.iStartCluster>0 && iMatching.iCount<KMaxMatchingEntries,User::Leave(KErrCorrupt));
sl@0
   767
	iMatching.iEntries[iMatching.iCount++]=aEntryPos;
sl@0
   768
	return iMatching.iCount==KMaxMatchingEntries;
sl@0
   769
	}
sl@0
   770
sl@0
   771
sl@0
   772
//----------------------------------------------------------------------------------------------------
sl@0
   773
/**
sl@0
   774
    Scan for differnces in the new and old FAT table writing them to media if discovered
sl@0
   775
    It is supposed to be called in 'ScanDrive' mode only
sl@0
   776
sl@0
   777
    @leave System wide error codes
sl@0
   778
*/
sl@0
   779
void CScanDrive::CompareAndFixFatsL()
sl@0
   780
    {
sl@0
   781
    __PRINT1(_L("CScanDrive::CompareAndFixFatsL() drv:%d"),iMount->DriveNumber());
sl@0
   782
sl@0
   783
    ASSERT(!CheckDiskMode());
sl@0
   784
sl@0
   785
    TUint32 nClustersFixed = 0; //-- fixed clusters count
sl@0
   786
    TUint32 nBadClusters   = 0; //-- bad cluster count
sl@0
   787
    TUint32 dirtyFatSector = 0; //-- FAT table media sector with not-flushed data
sl@0
   788
sl@0
   789
    const TUint32 KSectorSzLog2 = iMount->SectorSizeLog2(); //-- Log2(media Sector Size)
sl@0
   790
    
sl@0
   791
    TUint32 diffPos;
sl@0
   792
    if(iMediaFatBits.Diff(iScanFatBits, diffPos))
sl@0
   793
    {//-- there is a difference between FATs' bit representation
sl@0
   794
    
sl@0
   795
        ASSERT(diffPos >= KFatFirstSearchCluster);
sl@0
   796
sl@0
   797
        const TUint32 maxClusters = iScanFatBits.Size();
sl@0
   798
    
sl@0
   799
        for(TUint32 i=diffPos; i<maxClusters; ++i)
sl@0
   800
	        {
sl@0
   801
            if(BoolXOR(iMediaFatBits[i], iScanFatBits[i]))
sl@0
   802
                {//-- difference in the cluster "i" between a real FAT and what ScanDrive restored.
sl@0
   803
          
sl@0
   804
                //-- indicate that there are some problems in FAT. and we probably wrote something there.
sl@0
   805
                IndicateErrorsFound(EScanDriveDirError); 
sl@0
   806
                
sl@0
   807
                //-- skip BAD cluster, can't mark it as unused.
sl@0
   808
                if(iMount->IsBadCluster(ReadFatL(i)))
sl@0
   809
                    {
sl@0
   810
                    ++nBadClusters;
sl@0
   811
                    continue;
sl@0
   812
                    }
sl@0
   813
         
sl@0
   814
                //-- here we found a lost cluster. Its FAT entry will be replaced with KSpareCluster. In the case of multiple lost clusters FAT table will
sl@0
   815
                //-- be flushed on media sector basis. It is much faster than flushing FAT after every write and will
sl@0
   816
                //-- guarantee that FAT won't be corrupted if the media driver provides atomic sector write. 
sl@0
   817
                if(nClustersFixed == 0)
sl@0
   818
                    {//-- this is the first lost cluster entry we found
sl@0
   819
                    
sl@0
   820
                    //-- relative FAT media sector for the 'i' entry. The real value doesn't matter, 
sl@0
   821
                    //-- we will just be flushing FAT before writing to the different FAT media sector.
sl@0
   822
                    dirtyFatSector = iMount->FAT().PosInBytes(i) >> KSectorSzLog2; 
sl@0
   823
                    
sl@0
   824
                    iMount->FAT().WriteL(i, KSpareCluster); //-- fix lost cluster
sl@0
   825
                    }
sl@0
   826
                else
sl@0
   827
                    {
sl@0
   828
                    const TUint32 fatSec = iMount->FAT().PosInBytes(i) >> KSectorSzLog2; 
sl@0
   829
sl@0
   830
                    if(fatSec != dirtyFatSector)
sl@0
   831
                        {//-- we are going to write to a differrent media sector
sl@0
   832
                        iMount->FAT().FlushL();
sl@0
   833
                        iMount->FAT().WriteL(i, KSpareCluster); //-- fix lost cluster
sl@0
   834
                        dirtyFatSector = fatSec;
sl@0
   835
                        }
sl@0
   836
                    else
sl@0
   837
                        {//-- write to the same FAT media sector without flushing
sl@0
   838
                        iMount->FAT().WriteL(i, KSpareCluster); //-- fix lost cluster
sl@0
   839
                        }
sl@0
   840
                    
sl@0
   841
                    }
sl@0
   842
sl@0
   843
                ++nClustersFixed;
sl@0
   844
        
sl@0
   845
                }//if(BoolXOR(iMediaFatBits[i], iScanFatBits[i])
sl@0
   846
sl@0
   847
            }//for(TInt i=KFatFirstSearchCluster; i<maxClusters; ++i)
sl@0
   848
sl@0
   849
    }//if(iMediaFatBits.Diff(iScanFatBits, diffPos))
sl@0
   850
sl@0
   851
sl@0
   852
    if(nClustersFixed)
sl@0
   853
        iMount->FAT().FlushL();
sl@0
   854
    
sl@0
   855
    //------
sl@0
   856
sl@0
   857
    if(iTruncationCluster != 0)
sl@0
   858
        {
sl@0
   859
	    iMount->FAT().WriteFatEntryEofL(iTruncationCluster); 
sl@0
   860
		iMount->FAT().FlushL();
sl@0
   861
		
sl@0
   862
        //-- indicate that there are some problems in FAT. and we probably wrote something there.
sl@0
   863
        IndicateErrorsFound(EScanDriveDirError); //-- indicate that we have found errors
sl@0
   864
sl@0
   865
        ++nClustersFixed;
sl@0
   866
        }
sl@0
   867
    
sl@0
   868
    __PRINT2(_L("CScanDrive::WriteNewFatsL() fixed:%d, bad:%d"), nClustersFixed, nBadClusters);
sl@0
   869
    }
sl@0
   870
sl@0
   871
//----------------------------------------------------------------------------------------------------
sl@0
   872
/**
sl@0
   873
    Read the "Rugged FAT" ID, stored in reserved2 in the Dos entry or associated with the Dos entry of the 
sl@0
   874
    Entry at the position passed in. This is used to find which version of two matching entries should be kept.
sl@0
   875
sl@0
   876
sl@0
   877
    @param aVFatPos Position of an entry to read ID from
sl@0
   878
    @leave System wide error codes
sl@0
   879
    @return The ID found in reserved2 field of dos entry 
sl@0
   880
*/
sl@0
   881
TInt CScanDrive::GetReservedidL(TEntryPos aVFatPos)
sl@0
   882
	{
sl@0
   883
	__PRINT(_L("CScanDrive::GetReservedidL"));
sl@0
   884
	TFatDirEntry entry;
sl@0
   885
	ReadDirEntryL(aVFatPos,entry);
sl@0
   886
	if(!IsDosEntry(entry))
sl@0
   887
		{
sl@0
   888
		TInt toMove=entry.NumFollowing();
sl@0
   889
		while(toMove--)
sl@0
   890
			MoveToNextEntryL(aVFatPos);
sl@0
   891
		ReadDirEntryL(aVFatPos,entry);
sl@0
   892
		}
sl@0
   893
	return(entry.RuggedFatEntryId());
sl@0
   894
	}
sl@0
   895
sl@0
   896
//----------------------------------------------------------------------------------------------------
sl@0
   897
/**
sl@0
   898
    Erase part entry found in iPartEntry
sl@0
   899
    @leave System wide error code
sl@0
   900
*/
sl@0
   901
void CScanDrive::FixPartEntryL()
sl@0
   902
	{
sl@0
   903
	__PRINT2(_L("CScanDrive::FixPartEntryL cluster=%d,pos=%d"),iPartEntry.iEntryPos.iCluster,iPartEntry.iEntryPos.iPos);
sl@0
   904
	ASSERT(!CheckDiskMode());
sl@0
   905
    iMount->EraseDirEntryL(iPartEntry.iEntryPos,iPartEntry.iEntry);
sl@0
   906
    IndicateErrorsFound(EScanDriveDirError); //-- indicate that we have found errors
sl@0
   907
	}
sl@0
   908
	
sl@0
   909
//----------------------------------------------------------------------------------------------------
sl@0
   910
/**
sl@0
   911
    Delete entry with largest value in the reserved2 section(bytes 20 and 21) of dos entry
sl@0
   912
sl@0
   913
    @leave System wide error code
sl@0
   914
*/
sl@0
   915
void CScanDrive::FixMatchingEntryL()
sl@0
   916
	{
sl@0
   917
	
sl@0
   918
    __PRINT1(_L("CScanDrive::FixMatchingEntryL() start cluster=%d"),iMatching.iStartCluster);
sl@0
   919
	__ASSERT_ALWAYS(iMatching.iCount==KMaxMatchingEntries,User::Leave(KErrCorrupt));
sl@0
   920
	ASSERT(!CheckDiskMode());
sl@0
   921
sl@0
   922
    TInt idOne=GetReservedidL(iMatching.iEntries[0]);
sl@0
   923
	TInt idTwo=GetReservedidL(iMatching.iEntries[1]);
sl@0
   924
	TFatDirEntry entry;
sl@0
   925
	TInt num=idOne>idTwo?0:1;
sl@0
   926
	ReadDirEntryL(iMatching.iEntries[num],entry);
sl@0
   927
	iMount->EraseDirEntryL(iMatching.iEntries[num],entry);
sl@0
   928
    IndicateErrorsFound(EScanDriveDirError); //-- indicate that we have found errors
sl@0
   929
	}
sl@0
   930
sl@0
   931
//----------------------------------------------------------------------------------------------------
sl@0
   932
/**
sl@0
   933
    Move past specified number of entries
sl@0
   934
sl@0
   935
    @param aEntryPos Start position to move from, updated as move takes place
sl@0
   936
    @param aEntry Directory entry moved to
sl@0
   937
    @param aToMove Number of entries to move through
sl@0
   938
    @param aDirEntries Number of entries moved, updated as move takes place
sl@0
   939
    @leave System wide error code
sl@0
   940
*/
sl@0
   941
void CScanDrive::MovePastEntriesL(TEntryPos& aEntryPos,TFatDirEntry& aEntry,TInt aToMove,TInt& aDirEntries)
sl@0
   942
	{
sl@0
   943
	while(aToMove-- && !IsEofF(aEntryPos.iCluster))
sl@0
   944
		{
sl@0
   945
		MoveToNextEntryL(aEntryPos);
sl@0
   946
		++aDirEntries;
sl@0
   947
		}
sl@0
   948
	ReadDirEntryL(aEntryPos,aEntry);
sl@0
   949
	}
sl@0
   950
sl@0
   951
//----------------------------------------------------------------------------------------------------
sl@0
   952
/**
sl@0
   953
    Adds aCluster to cluster list array so that it may be revisited later, avoids stack 
sl@0
   954
    over flow
sl@0
   955
sl@0
   956
    @param aCluster Directory cluster number to add to the list
sl@0
   957
    @leave KErrNoMemory If allocation fails
sl@0
   958
*/
sl@0
   959
void CScanDrive::AddToClusterListL(TInt aCluster)
sl@0
   960
	{
sl@0
   961
sl@0
   962
	if(iListArrayIndex>=KMaxArrayDepth)
sl@0
   963
		return;
sl@0
   964
sl@0
   965
	if(iClusterListArray[iListArrayIndex]==NULL)
sl@0
   966
		iClusterListArray[iListArrayIndex]=new(ELeave) RArray<TInt>(KClusterListGranularity);
sl@0
   967
	iClusterListArray[iListArrayIndex]->Append(aCluster);
sl@0
   968
	}
sl@0
   969
sl@0
   970
sl@0
   971
//----------------------------------------------------------------------------------------------------
sl@0
   972
/**
sl@0
   973
    Used in "CheckDisk" mode mostly. Compares first FAT table on the media with the FAT bitmap restored by walking the directory structure.
sl@0
   974
    Displays any differences and records an error if found.
sl@0
   975
    
sl@0
   976
    @param  aStopOnFirstErrorFound if ETrue will stop after discovering first error (FATs discrepancy)
sl@0
   977
    
sl@0
   978
    @leave  System wide error codes
sl@0
   979
*/
sl@0
   980
void CScanDrive::CompareFatsL(TBool aStopOnFirstErrorFound)  
sl@0
   981
    {
sl@0
   982
	__PRINT1(_L("CScanDrive::CompareFatsL(%d)"), aStopOnFirstErrorFound);
sl@0
   983
		
sl@0
   984
   
sl@0
   985
    TUint32 diffPos;
sl@0
   986
    if(!iMediaFatBits.Diff(iScanFatBits, diffPos))
sl@0
   987
        return; //-- FATs are identical
sl@0
   988
    
sl@0
   989
    //-- there is a difference between the real FAT and reconstructed one. Find the mismaching bit and fix FAT. 
sl@0
   990
    const TUint clusters = iMount->UsableClusters();
sl@0
   991
    ASSERT(diffPos < (TUint32)clusters);
sl@0
   992
                        
sl@0
   993
    TUint scanusedcnt=0;
sl@0
   994
	TUint mediausedcnt=0;
sl@0
   995
	
sl@0
   996
    for(TUint i=diffPos; i<clusters; ++i)
sl@0
   997
	    {
sl@0
   998
        const TBool bRealFatEntry = iMediaFatBits[i];
sl@0
   999
        const TBool bNewFatEntry  = iScanFatBits[i];
sl@0
  1000
sl@0
  1001
		if(BoolXOR(bRealFatEntry, bNewFatEntry))
sl@0
  1002
		    {//-- mismatch between FAT on the media and the FAT bitmap restored by walking directory structure
sl@0
  1003
sl@0
  1004
			    if(bRealFatEntry)
sl@0
  1005
                {//-- FAT[i] on the media is marked as occupied, but retored FAT bitmap shows that it is free
sl@0
  1006
                    if(iMount->IsBadCluster(ReadFatL(i)))
sl@0
  1007
                        continue; //-- this is a BAD cluster it can't be occupied by the FS object, OK.
sl@0
  1008
sl@0
  1009
                    __PRINT2(_L("FAT[%d] = %d\n"), i, ReadFatL(i));        
sl@0
  1010
                    __PRINT1(_L("iTruncationCluster = %d\n"), iTruncationCluster);        
sl@0
  1011
                    
sl@0
  1012
                    //-- this is a lost cluster
sl@0
  1013
                    if(!IsEofF(ReadFatL(i)) && (i==iTruncationCluster))
sl@0
  1014
                        {//-- seems to be a Rugged FAT ertefact
sl@0
  1015
                        __PRINT1(_L("Hanging cluster = %d\n"),i);        
sl@0
  1016
                        }
sl@0
  1017
                    else
sl@0
  1018
                        {
sl@0
  1019
                        __PRINT1(_L("Lost cluster=%d\n"),i);
sl@0
  1020
                        }
sl@0
  1021
                    
sl@0
  1022
                    
sl@0
  1023
                    IndicateErrorsFound(EBadClusterValue);
sl@0
  1024
                }
sl@0
  1025
                else
sl@0
  1026
                {//-- FAT[i] on the media is marked as free, but retored FAT bitmap shows that it is occupied by some object
sl@0
  1027
                    IndicateErrorsFound(EClusterAlreadyInUse);
sl@0
  1028
                    __PRINT1(_L("Unflushed cluster = %d\n"),i);
sl@0
  1029
                }
sl@0
  1030
sl@0
  1031
             if(aStopOnFirstErrorFound)
sl@0
  1032
                 break; //-- not asked to check for errors further
sl@0
  1033
sl@0
  1034
            }
sl@0
  1035
		
sl@0
  1036
        if(bRealFatEntry)
sl@0
  1037
			mediausedcnt++;
sl@0
  1038
sl@0
  1039
		if(bNewFatEntry)
sl@0
  1040
			scanusedcnt++;
sl@0
  1041
        }        	
sl@0
  1042
sl@0
  1043
    __PRINT2(_L("Scan Fat Used=%d, Media Fat Used=%d \n"),scanusedcnt,mediausedcnt);
sl@0
  1044
    }	
sl@0
  1045
sl@0
  1046
//----------------------------------------------------------------------------------------------------
sl@0
  1047
/**
sl@0
  1048
    For debug purposes, print errors found as debug output
sl@0
  1049
*/
sl@0
  1050
void CScanDrive::PrintErrors()
sl@0
  1051
    {
sl@0
  1052
#if defined(_DEBUG)    
sl@0
  1053
	__PRINT1(_L("Directories visisted = %d\n"),iDirsChecked);
sl@0
  1054
sl@0
  1055
	if(iDirError==EScanPartEntry)
sl@0
  1056
	    {
sl@0
  1057
    	__PRINT2(_L("Part entry-dir cluster=%d,dir pos=%d,\n"),iPartEntry.iEntryPos.iCluster,iPartEntry.iEntryPos.iPos);
sl@0
  1058
        }
sl@0
  1059
	else if(iDirError==EScanMatchingEntry)
sl@0
  1060
	    {
sl@0
  1061
		__PRINT1(_L("Matching cluster - cluster no=%d\n"),iMatching.iStartCluster);
sl@0
  1062
		__PRINT2(_L("\tcluster 1 - dir cluster=%d,dir pos=%d\n"),iMatching.iEntries[0].iCluster,iMatching.iEntries[0].iPos);
sl@0
  1063
		__PRINT2(_L("\tcluster 2 - dir cluster=%d,dir pos=%d\n"),iMatching.iEntries[1].iCluster,iMatching.iEntries[1].iPos);
sl@0
  1064
	    }
sl@0
  1065
#endif
sl@0
  1066
    }
sl@0
  1067
sl@0
  1068
sl@0
  1069
sl@0
  1070
/**
sl@0
  1071
Read a FAT directory entry from disk, either reads directly from the main cache or
sl@0
  1072
from the cluster buffer if scan drive is running in a seperate thread.
sl@0
  1073
sl@0
  1074
@param aPos Media position of entry to read
sl@0
  1075
@param aDirEntry Contents of directory entry read
sl@0
  1076
@leave System wide error code 
sl@0
  1077
*/
sl@0
  1078
void CScanDrive::ReadDirEntryL(const TEntryPos& aPos,TFatDirEntry& aDirEntry)
sl@0
  1079
    {
sl@0
  1080
	//__PRINT(_L("CScanDrive::ReadDirEntryL"));
sl@0
  1081
	if (iMount->IsEndOfClusterCh(aPos.iCluster))
sl@0
  1082
		{
sl@0
  1083
		Mem::FillZ(&aDirEntry,sizeof(TFatDirEntry));
sl@0
  1084
		return;
sl@0
  1085
		}
sl@0
  1086
sl@0
  1087
	iMount->ReadDirEntryL(aPos, aDirEntry);
sl@0
  1088
    }
sl@0
  1089
sl@0
  1090
sl@0
  1091
/**
sl@0
  1092
    Move to next directory entry, if anEntry is at the end of the cluster, and we are not 
sl@0
  1093
    the root dir, move it to the next cluster in the chain.
sl@0
  1094
sl@0
  1095
    @param aPos Current directory position up dated to position of next entry.
sl@0
  1096
*/
sl@0
  1097
void CScanDrive::MoveToNextEntryL(TEntryPos& aPos)
sl@0
  1098
	{
sl@0
  1099
	//__PRINT(_L("CScanDrive::MoveToNextEntryL"));
sl@0
  1100
	iMount->MoveToNextEntryL(aPos);
sl@0
  1101
	}	
sl@0
  1102
sl@0
  1103
/**
sl@0
  1104
    Read a cluster from the Media Fat if scan run in a seperate thread read from scan fat table
sl@0
  1105
    otherwise read from mount owned Fat table
sl@0
  1106
sl@0
  1107
    @param aClusterNum Cluster to read
sl@0
  1108
    @return Value of cluster read from Fat
sl@0
  1109
*/
sl@0
  1110
TUint32 CScanDrive::ReadFatL(TUint aClusterNum) 
sl@0
  1111
	{
sl@0
  1112
	if(aClusterNum < KFatFirstSearchCluster || aClusterNum >= MaxClusters())
sl@0
  1113
        {
sl@0
  1114
        IndicateErrorsFound(EBadClusterNumber);
sl@0
  1115
        User::Leave(KErrCorrupt);
sl@0
  1116
        }
sl@0
  1117
sl@0
  1118
    //-- actually, ReadL() can leave with some error code, that won't be reflected in IndicateErrorsFound().
sl@0
  1119
    //-- it's possible to improve but is it worth it?
sl@0
  1120
    return iMount->FAT().ReadL(aClusterNum);			
sl@0
  1121
    }
sl@0
  1122
sl@0
  1123
sl@0
  1124
/**
sl@0
  1125
    Set a cluster as visited in the bit packed scan Fat
sl@0
  1126
    @param aCluster Cluster number
sl@0
  1127
*/
sl@0
  1128
void CScanDrive::MarkClusterUsedL(TUint aClusterNum)
sl@0
  1129
	{
sl@0
  1130
	if(aClusterNum < KFatFirstSearchCluster || aClusterNum >= MaxClusters())
sl@0
  1131
        {
sl@0
  1132
        IndicateErrorsFound(EBadClusterNumber);
sl@0
  1133
        User::Leave(KErrCorrupt);
sl@0
  1134
        }
sl@0
  1135
sl@0
  1136
    iScanFatBits.SetBit(aClusterNum);
sl@0
  1137
	}
sl@0
  1138
sl@0
  1139
sl@0
  1140
/**
sl@0
  1141
    Query whether a cluster is already set as used 
sl@0
  1142
    @param aCluster Cluster to query
sl@0
  1143
*/
sl@0
  1144
TBool CScanDrive::IsClusterUsedL(TUint aClusterNum) 
sl@0
  1145
	{
sl@0
  1146
	if(aClusterNum < KFatFirstSearchCluster || aClusterNum >= MaxClusters())
sl@0
  1147
        {
sl@0
  1148
        IndicateErrorsFound(EBadClusterNumber);
sl@0
  1149
        User::Leave(KErrCorrupt);
sl@0
  1150
        }
sl@0
  1151
sl@0
  1152
    return iScanFatBits[aClusterNum];
sl@0
  1153
	}
sl@0
  1154
sl@0
  1155
/**
sl@0
  1156
    @param aPos Position in a directory cluster
sl@0
  1157
    @return  ETrue if aPos is the last entry in the root directory
sl@0
  1158
*/
sl@0
  1159
TBool CScanDrive::IsEndOfRootDir(const TEntryPos& aPos)const
sl@0
  1160
	{
sl@0
  1161
	return(iMount->IsRootDir(aPos)&&(iMount->StartOfRootDirInBytes()+aPos.iPos==(iMount->RootDirEnd()-KSizeOfFatDirEntry)));
sl@0
  1162
	}
sl@0
  1163
sl@0
  1164
/**
sl@0
  1165
    @param aVal Value of the cluster to be tested
sl@0
  1166
    @return ETrue if aVal is the end of cluster marker
sl@0
  1167
*/
sl@0
  1168
TBool CScanDrive::IsEofF(TInt aVal) const 
sl@0
  1169
	{
sl@0
  1170
    return iMount->IsEndOfClusterCh(aVal);
sl@0
  1171
	}
sl@0
  1172
sl@0
  1173
/** @return max. number of clusters on the volume being scanned */
sl@0
  1174
TUint32 CScanDrive::MaxClusters() const
sl@0
  1175
    {
sl@0
  1176
        ASSERT(iMaxClusters);
sl@0
  1177
        return iMaxClusters;
sl@0
  1178
    }
sl@0
  1179
sl@0
  1180
/** @return ETrue in we are operating in "CheckDisk" mode*/
sl@0
  1181
TBool CScanDrive::CheckDiskMode() const 
sl@0
  1182
    {
sl@0
  1183
    return iScanDriveMode == ECheckDisk;
sl@0
  1184
    }
sl@0
  1185
sl@0
  1186
sl@0
  1187
sl@0
  1188
sl@0
  1189
sl@0
  1190
sl@0
  1191
sl@0
  1192
sl@0
  1193