os/kernelhwsrv/userlibandfileserver/fileserver/sfat/sl_scan.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\sfat\sl_scan.cpp
sl@0
    15
// ScanDrive code, specific for EFAT.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
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
sl@0
    25
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
sl@0
    26
//!!
sl@0
    27
//!! WARNING!! DO NOT edit this file !! '\sfat' component is obsolete and is not being used. '\sfat32'replaces it
sl@0
    28
//!!
sl@0
    29
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
sl@0
    30
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
sl@0
    31
sl@0
    32
sl@0
    33
//#define DEBUG_SCANDRIVE
sl@0
    34
sl@0
    35
#include "sl_std.h"
sl@0
    36
#include "sl_scandrv.h"
sl@0
    37
sl@0
    38
const TInt KEndOfDirectory          = 0xFFFF;   ///< End of directory marker
sl@0
    39
const TInt KMaxScanDepth            = 20;       ///< Maximum scan depth of to avoid stack over flow 
sl@0
    40
const TInt KClusterListGranularity  = 8;        ///< Granularity of cluster list used for storage of clusters when KMaxScanDepth is reached
sl@0
    41
sl@0
    42
/**
sl@0
    43
Creates a CScanDrive
sl@0
    44
sl@0
    45
@param aMount The owning mount
sl@0
    46
*/
sl@0
    47
CScanDrive* CScanDrive::NewL(CFatMountCB* aMount)
sl@0
    48
    {
sl@0
    49
    if(aMount==NULL)
sl@0
    50
        User::Leave(KErrArgument);
sl@0
    51
    CScanDrive* self=new (ELeave) CScanDrive();
sl@0
    52
    CleanupStack::PushL(self);
sl@0
    53
    self->ConstructL(aMount);
sl@0
    54
    CleanupStack::Pop();
sl@0
    55
    return self;
sl@0
    56
    }
sl@0
    57
sl@0
    58
sl@0
    59
CScanDrive::CScanDrive()
sl@0
    60
//
sl@0
    61
// Constructor
sl@0
    62
//
sl@0
    63
    {
sl@0
    64
    }
sl@0
    65
sl@0
    66
sl@0
    67
CScanDrive::~CScanDrive()
sl@0
    68
//
sl@0
    69
// Destructor
sl@0
    70
//
sl@0
    71
    {
sl@0
    72
    delete iNewFat;
sl@0
    73
    for(TInt i=0;i<KMaxArrayDepth && iClusterListArray[i]!=NULL;++i)
sl@0
    74
        {
sl@0
    75
        iClusterListArray[i]->Close();
sl@0
    76
        delete iClusterListArray[i];
sl@0
    77
        }
sl@0
    78
    }
sl@0
    79
sl@0
    80
sl@0
    81
void CScanDrive::ConstructL(CFatMountCB* aMount)
sl@0
    82
//
sl@0
    83
// Create the new fat and initalise
sl@0
    84
//
sl@0
    85
    {
sl@0
    86
    iMount=aMount;
sl@0
    87
    iNewFat=CCheckFatTable::NewL(aMount);
sl@0
    88
    iNewFat->InitializeL();
sl@0
    89
    }
sl@0
    90
sl@0
    91
sl@0
    92
TBool CScanDrive::AlreadyExistsL(TInt aCluster)const
sl@0
    93
//
sl@0
    94
//  Return ETrue if aCluster in the new fat contains a non-zero entry
sl@0
    95
//
sl@0
    96
    {
sl@0
    97
    return(iNewFat->ReadL(aCluster)!=0);
sl@0
    98
    }
sl@0
    99
sl@0
   100
sl@0
   101
TBool CScanDrive::IsEndOfRootDir(const TEntryPos& aPos)const
sl@0
   102
//
sl@0
   103
// Return ETrue if aPos is the last entry in the root directory
sl@0
   104
//
sl@0
   105
    {
sl@0
   106
    return(iMount->IsRootDir(aPos)&&(iMount->StartOfRootDirInBytes()+aPos.iPos==(iMount->RootDirEnd()-KSizeOfFatDirEntry)));
sl@0
   107
    }
sl@0
   108
sl@0
   109
/**
sl@0
   110
@param aVal Value of the cluster to be tested
sl@0
   111
@return ETrue if aVal is the end of cluster marker
sl@0
   112
*/
sl@0
   113
TBool CScanDrive::IsEofF(TInt aVal)const 
sl@0
   114
    {
sl@0
   115
    return iMount->IsEndOfClusterCh(aVal);
sl@0
   116
    }
sl@0
   117
sl@0
   118
/**
sl@0
   119
@return True if a directory error has been found
sl@0
   120
*/
sl@0
   121
TBool CScanDrive::IsDirError()const
sl@0
   122
    {
sl@0
   123
    return(iDirError!=0);
sl@0
   124
    }
sl@0
   125
sl@0
   126
sl@0
   127
sl@0
   128
/**
sl@0
   129
    After StartL() and finishing allows us to know if there were any problems at all.
sl@0
   130
    The client may wish to remount the filesystem if there were errors.
sl@0
   131
sl@0
   132
    @return EFalse if there were no problems in FS.
sl@0
   133
*/
sl@0
   134
TBool CScanDrive::ProblemsDiscovered() const
sl@0
   135
{
sl@0
   136
    return IsDirError() || iFoundProblems;
sl@0
   137
}
sl@0
   138
sl@0
   139
/**
sl@0
   140
    Sets the flag indicating than there are errors in filesystem structure
sl@0
   141
    See ProblemsDiscovered()
sl@0
   142
*/
sl@0
   143
void CScanDrive::IndicateErrorsFound()
sl@0
   144
{
sl@0
   145
    iFoundProblems = ETrue;
sl@0
   146
}
sl@0
   147
sl@0
   148
sl@0
   149
sl@0
   150
/**
sl@0
   151
Start point for scan drive also fixes up errors 
sl@0
   152
sl@0
   153
@return The result of the scan
sl@0
   154
@leave 
sl@0
   155
*/
sl@0
   156
TInt CScanDrive::StartL()
sl@0
   157
    {
sl@0
   158
    __PRINT(_L("CScanDrive::StartL"));
sl@0
   159
    // check directory structure
sl@0
   160
    CheckDirStructureL();
sl@0
   161
#if defined(DEBUG_SCANDRIVE)
sl@0
   162
    CompareFatsL();
sl@0
   163
#endif
sl@0
   164
    // fix error in directory structure
sl@0
   165
    if(IsDirError())
sl@0
   166
        FixupDirErrorL();
sl@0
   167
    // flush new fat
sl@0
   168
    WriteNewFatsL();
sl@0
   169
#if defined(DEBUG_SCANDRIVE)
sl@0
   170
    PrintErrors();
sl@0
   171
#endif
sl@0
   172
    return KErrNone;
sl@0
   173
    }
sl@0
   174
sl@0
   175
/**
sl@0
   176
Fix errors detected by the drive scan
sl@0
   177
sl@0
   178
@leave System wide error code
sl@0
   179
*/
sl@0
   180
void CScanDrive::FixupDirErrorL()
sl@0
   181
    {
sl@0
   182
    if(!IsDirError())
sl@0
   183
        return;
sl@0
   184
    if(iDirError==EScanMatchingEntry)
sl@0
   185
        {
sl@0
   186
        FindSameStartClusterL();
sl@0
   187
        FixMatchingEntryL();
sl@0
   188
        }
sl@0
   189
    else
sl@0
   190
        {
sl@0
   191
        FixPartEntryL();
sl@0
   192
        }
sl@0
   193
sl@0
   194
    IndicateErrorsFound(); //-- indicate that we have found errors
sl@0
   195
    }
sl@0
   196
sl@0
   197
/**
sl@0
   198
Find positions of entries with same start cluster for error correction, searches
sl@0
   199
the whole volume. Starts at the root directory. 
sl@0
   200
sl@0
   201
@leave System wide error code
sl@0
   202
*/
sl@0
   203
void CScanDrive::FindSameStartClusterL()
sl@0
   204
    {
sl@0
   205
    TInt err=FindStartClusterL(0);
sl@0
   206
    if(err==KErrNone)
sl@0
   207
        return;
sl@0
   208
    for(TInt i=0;i<KMaxArrayDepth && iClusterListArray[i]!=NULL;++i)
sl@0
   209
        {
sl@0
   210
        RArray<TInt>* clusterList=iClusterListArray[i];
sl@0
   211
        for(TInt j=0;j<clusterList->Count();++j)
sl@0
   212
            {
sl@0
   213
            iRecursiveDepth=0;
sl@0
   214
            err=FindStartClusterL((*clusterList)[j]);
sl@0
   215
            if(err==KErrNone)
sl@0
   216
                return;
sl@0
   217
            }
sl@0
   218
        }
sl@0
   219
    __ASSERT_ALWAYS(err==KErrNone,User::Leave(KErrNotFound));
sl@0
   220
    }
sl@0
   221
/**
sl@0
   222
Scan through directory structure looking for start cluster found in iMatching
sl@0
   223
sl@0
   224
@param aDirCluster Start cluster for scan to start
sl@0
   225
@return System wide error value
sl@0
   226
@leave 
sl@0
   227
*/
sl@0
   228
TInt CScanDrive::FindStartClusterL(TInt aDirCluster)
sl@0
   229
    {
sl@0
   230
    __PRINT1(_L("CScanDrive::FindStartCluster dirCluster=%d"),aDirCluster);
sl@0
   231
    __ASSERT_ALWAYS(aDirCluster>=0,User::Leave(KErrCorrupt));
sl@0
   232
    if(++iRecursiveDepth==KMaxScanDepth)
sl@0
   233
        {
sl@0
   234
        --iRecursiveDepth;
sl@0
   235
        return(KErrNotFound);
sl@0
   236
        }
sl@0
   237
    TEntryPos entryPos(aDirCluster,0);
sl@0
   238
    TInt dirEntries=0;
sl@0
   239
    FOREVER
sl@0
   240
        {
sl@0
   241
        TFatDirEntry entry;
sl@0
   242
        iMount->ReadDirEntryL(entryPos,entry);
sl@0
   243
        if(entry.IsParentDirectory()||entry.IsCurrentDirectory()||entry.IsErased())
sl@0
   244
            {
sl@0
   245
            if(IsEndOfRootDir(entryPos))
sl@0
   246
                break;
sl@0
   247
            iMount->MoveToNextEntryL(entryPos);
sl@0
   248
            continue;
sl@0
   249
            }
sl@0
   250
        if(entry.IsEndOfDirectory())
sl@0
   251
            break;
sl@0
   252
        TBool isComplete;
sl@0
   253
        TEntryPos vfatPos=entryPos;
sl@0
   254
        isComplete=MoveToVFatEndL(entryPos,entry,dirEntries);
sl@0
   255
        __ASSERT_ALWAYS(isComplete,User::Leave(KErrBadName));
sl@0
   256
        TInt err=CheckEntryClusterL(entry,vfatPos);
sl@0
   257
        if(err==KErrNone)
sl@0
   258
            {
sl@0
   259
            --iRecursiveDepth;
sl@0
   260
            return(err);
sl@0
   261
            }
sl@0
   262
        if(IsEndOfRootDir(entryPos))
sl@0
   263
            break;
sl@0
   264
        iMount->MoveToNextEntryL(entryPos);
sl@0
   265
        }
sl@0
   266
    --iRecursiveDepth;
sl@0
   267
    return(KErrNotFound);
sl@0
   268
    }
sl@0
   269
sl@0
   270
/**
sl@0
   271
Procces aEntry to find matching start cluster
sl@0
   272
sl@0
   273
@param aEntry Directory entry to check
sl@0
   274
@param aEntryPos Position of directory to check
sl@0
   275
@return System wide error value
sl@0
   276
@leave 
sl@0
   277
*/
sl@0
   278
TInt CScanDrive::CheckEntryClusterL(const TFatDirEntry& aEntry, const TEntryPos& aEntryPos)
sl@0
   279
    {
sl@0
   280
    __PRINT(_L("CScanDrive::CheckEntryClusterL"));
sl@0
   281
    if(iMount->StartCluster(aEntry)==iMatching.iStartCluster)
sl@0
   282
        {
sl@0
   283
        TBool complete=AddMatchingEntryL(aEntryPos);
sl@0
   284
        if(complete)
sl@0
   285
            return(KErrNone);
sl@0
   286
        }
sl@0
   287
    else if(aEntry.Attributes()&KEntryAttDir)
sl@0
   288
        return(FindStartClusterL(iMount->StartCluster(aEntry)));
sl@0
   289
    return(KErrNotFound);
sl@0
   290
    }
sl@0
   291
sl@0
   292
/**
sl@0
   293
Checks directory strucutre for errors, can be considered the start point of the scan.  
sl@0
   294
Handles recursion depth to avoid stack overflow.
sl@0
   295
sl@0
   296
@leave System wide error code
sl@0
   297
*/
sl@0
   298
void CScanDrive::CheckDirStructureL()
sl@0
   299
    {
sl@0
   300
    CheckDirL(iMount->RootIndicator());
sl@0
   301
    // Due to recursive nature of CheckDirL when a depth of
sl@0
   302
    // KMaxScanDepth is reached clusters are stored in a list
sl@0
   303
    // and passed into CheckDirL afresh
sl@0
   304
    for(TInt i=0;i<KMaxArrayDepth && iClusterListArray[i]!=NULL;++i)
sl@0
   305
        {
sl@0
   306
        RArray<TInt>* clusterList=iClusterListArray[i];
sl@0
   307
        ++iListArrayIndex;
sl@0
   308
        for(TInt j=0;j<clusterList->Count();++j)
sl@0
   309
            {
sl@0
   310
            iRecursiveDepth=0;
sl@0
   311
            CheckDirL((*clusterList)[j]);
sl@0
   312
            }
sl@0
   313
        }
sl@0
   314
    }
sl@0
   315
/**
sl@0
   316
Function is called recursively with Process entry untill the whole volume has been scanned.
sl@0
   317
Each directory entry is scanned for errors, these are recorded for later fixing. 
sl@0
   318
sl@0
   319
@param aCluster Directory cluster to start checking
sl@0
   320
@leave System wide error codes
sl@0
   321
*/
sl@0
   322
void CScanDrive::CheckDirL(TInt aCluster)
sl@0
   323
    {
sl@0
   324
    __PRINT1(_L("CScanDrive::CheckDirL aCluster=%d"),aCluster);
sl@0
   325
    __ASSERT_ALWAYS(aCluster>=0,User::Leave(KErrCorrupt));
sl@0
   326
    // check depth of recursion
sl@0
   327
    if(++iRecursiveDepth==KMaxScanDepth)
sl@0
   328
        {
sl@0
   329
        AddToClusterListL(aCluster);
sl@0
   330
        --iRecursiveDepth;
sl@0
   331
        return;
sl@0
   332
        }
sl@0
   333
#if defined(DEBUG_SCANDRIVE)
sl@0
   334
    ++iDirsChecked;
sl@0
   335
#endif
sl@0
   336
    TEntryPos entryPos(aCluster,0);
sl@0
   337
    TInt dirEntries=0;
sl@0
   338
    FOREVER
sl@0
   339
        {
sl@0
   340
        TFatDirEntry entry;
sl@0
   341
        iMount->ReadDirEntryL(entryPos,entry);
sl@0
   342
        if(!iMount->IsEndOfClusterCh(entryPos.iCluster))
sl@0
   343
            ++dirEntries;
sl@0
   344
        if(entry.IsParentDirectory()||entry.IsCurrentDirectory()||entry.IsErased())
sl@0
   345
            {
sl@0
   346
            if(IsEndOfRootDir(entryPos))
sl@0
   347
                break;
sl@0
   348
            iMount->MoveToNextEntryL(entryPos);
sl@0
   349
            continue;
sl@0
   350
            }
sl@0
   351
        if(entry.IsEndOfDirectory())
sl@0
   352
            {
sl@0
   353
            if(aCluster)    
sl@0
   354
                WriteClusterChainL(aCluster,dirEntries<<KSizeOfFatDirEntryLog2);
sl@0
   355
            break;
sl@0
   356
            }
sl@0
   357
        TEntryPos origPos=entryPos;
sl@0
   358
        TFatDirEntry origEntry=entry;
sl@0
   359
        TInt origDirEntries=dirEntries;
sl@0
   360
        TBool isComplete;
sl@0
   361
        isComplete=MoveToVFatEndL(entryPos,entry,dirEntries);
sl@0
   362
        // Only assume that this is a corrupted VFAT entry if the VFAT attributes are set; 
sl@0
   363
        // assuming a non-VFAT corrupted entry is a VFAT entry is dangerous as we then assume that the 
sl@0
   364
        // first byte is a count of entries to skip, thus completely invalidating the next <n> directories.
sl@0
   365
        if (!isComplete && origEntry.IsVFatEntry())
sl@0
   366
            {
sl@0
   367
            AddPartialVFatL(origPos,origEntry);
sl@0
   368
            if(entryPos.iCluster!=KEndOfDirectory)
sl@0
   369
                {
sl@0
   370
                TInt toMove=origEntry.NumFollowing()-(dirEntries-origDirEntries);
sl@0
   371
                if(toMove)
sl@0
   372
                    MovePastEntriesL(entryPos,entry,toMove,dirEntries);
sl@0
   373
                }
sl@0
   374
            else
sl@0
   375
                {
sl@0
   376
                // we fell off the end of the directory file, so just strip this
sl@0
   377
                // incomplete long file name entry
sl@0
   378
                dirEntries = origDirEntries;
sl@0
   379
                }
sl@0
   380
            }
sl@0
   381
        else
sl@0
   382
            ProcessEntryL(entry);
sl@0
   383
        if(IsEndOfRootDir(entryPos))
sl@0
   384
            break;
sl@0
   385
        iMount->MoveToNextEntryL(entryPos);
sl@0
   386
        }
sl@0
   387
    --iRecursiveDepth;
sl@0
   388
    }
sl@0
   389
sl@0
   390
/**
sl@0
   391
Process non trivial entries, such as files, if they are correct by filling out their 
sl@0
   392
cluster allocation in the bit packed Fat table. If it comes accross a directory 
sl@0
   393
CheckDirL will be called.
sl@0
   394
sl@0
   395
@param aEntry Directory entry to check
sl@0
   396
@leave System wide error code
sl@0
   397
*/
sl@0
   398
void CScanDrive::ProcessEntryL(const TFatDirEntry& aEntry)
sl@0
   399
    {
sl@0
   400
    __PRINT(_L("CScanDrive::ProcessEntryL"));
sl@0
   401
    TInt entryAtt=aEntry.Attributes();
sl@0
   402
    __ASSERT_ALWAYS(!(entryAtt&~KEntryAttMaskSupported)&&!aEntry.IsErased(),User::Leave(KErrCorrupt));
sl@0
   403
    if(!(entryAtt&(KEntryAttDir|KEntryAttVolume)) && iMount->StartCluster(aEntry)>0)
sl@0
   404
        WriteClusterChainL(iMount->StartCluster(aEntry),aEntry.Size());
sl@0
   405
    else if(entryAtt&KEntryAttDir)
sl@0
   406
        CheckDirL(iMount->StartCluster(aEntry));
sl@0
   407
    }
sl@0
   408
sl@0
   409
/**
sl@0
   410
Writes out the cluster chain for a correct file or directory, checks that the cluster 
sl@0
   411
has not already been used and that the correct number of clusters are allocated for the 
sl@0
   412
size of file. Registers cluster as used if correct
sl@0
   413
sl@0
   414
@param aCluster Cluster chain start point
sl@0
   415
@param aSizeInBytes Size of the file or directory in bytes
sl@0
   416
@leave System wide error values
sl@0
   417
*/
sl@0
   418
void CScanDrive::WriteClusterChainL(TInt aCluster,TInt aSizeInBytes)
sl@0
   419
//
sl@0
   420
// Mark off in the new fat the clusters used by entry with start cluster of aCluster
sl@0
   421
//
sl@0
   422
    {
sl@0
   423
sl@0
   424
    IndicateErrorsFound(); //-- indicate that we have found errors
sl@0
   425
sl@0
   426
    __PRINT1(_L("CScanDrive::WriteClusterChainL starting at %d"),aCluster);
sl@0
   427
    __ASSERT_ALWAYS(aCluster>0 && aSizeInBytes>=0,User::Leave(KErrCorrupt));
sl@0
   428
    TInt clusterCount;
sl@0
   429
    if(aSizeInBytes==0)
sl@0
   430
        clusterCount=1;
sl@0
   431
    else
sl@0
   432
        clusterCount=(aSizeInBytes+(1<<iMount->ClusterSizeLog2())-1)>>iMount->ClusterSizeLog2();
sl@0
   433
    TInt startCluster=aCluster;
sl@0
   434
    while(clusterCount)
sl@0
   435
        {
sl@0
   436
        if(AlreadyExistsL(aCluster))
sl@0
   437
            {
sl@0
   438
            __ASSERT_ALWAYS(!IsDirError()&&iMatching.iStartCluster==0&&aCluster==startCluster,User::Leave(KErrCorrupt));
sl@0
   439
            iMatching.iStartCluster=aCluster;
sl@0
   440
            iDirError=EScanMatchingEntry;
sl@0
   441
            return;
sl@0
   442
            }
sl@0
   443
        if(clusterCount==1)
sl@0
   444
            {
sl@0
   445
            iNewFat->WriteFatEntryEofFL(aCluster);
sl@0
   446
            return;
sl@0
   447
            }
sl@0
   448
        else
sl@0
   449
            {
sl@0
   450
            TInt clusterVal;
sl@0
   451
            clusterVal=iMount->FAT().ReadL(aCluster);
sl@0
   452
            __ASSERT_ALWAYS(!IsEofF(clusterVal) && clusterVal!=0,User::Leave(KErrCorrupt));
sl@0
   453
            iNewFat->WriteL(aCluster,clusterVal);
sl@0
   454
            aCluster=clusterVal;
sl@0
   455
            --clusterCount;
sl@0
   456
            }
sl@0
   457
        }
sl@0
   458
    }
sl@0
   459
sl@0
   460
/**
sl@0
   461
Move to dos entry, checking all vfat entry ID numbers are in sequence.
sl@0
   462
Assumes aEntry is not erased
sl@0
   463
sl@0
   464
@param aPos Position of the entry to move from, returns with new position
sl@0
   465
@param aEntry The Dos entry after the Vfat entries on return
sl@0
   466
@param aDirLength Running total of the length of the directory in entries
sl@0
   467
@leave System wide error codes
sl@0
   468
@return EFalse if not valid vfat entries or dos entry, else returns ETrue
sl@0
   469
*/
sl@0
   470
TBool CScanDrive::MoveToVFatEndL(TEntryPos& aPos,TFatDirEntry& aEntry,TInt& aDirLength)
sl@0
   471
    {
sl@0
   472
    __PRINT2(_L("CScanDrive::MoveToVFatEndL cluster=%d,pos=%d"),aPos.iCluster,aPos.iPos);
sl@0
   473
    if(!aEntry.IsVFatEntry())
sl@0
   474
        return IsDosEntry(aEntry);
sl@0
   475
    TInt toFollow=aEntry.NumFollowing();
sl@0
   476
    __ASSERT_ALWAYS(toFollow>0&&!aEntry.IsErased(),User::Leave(KErrCorrupt));
sl@0
   477
    FOREVER
sl@0
   478
        {
sl@0
   479
        iMount->MoveToNextEntryL(aPos);
sl@0
   480
        iMount->ReadDirEntryL(aPos,aEntry);
sl@0
   481
        ++aDirLength;
sl@0
   482
        --toFollow;
sl@0
   483
        if(!toFollow)
sl@0
   484
            break;
sl@0
   485
        if(!IsValidVFatEntry(aEntry,toFollow))
sl@0
   486
            return(EFalse);
sl@0
   487
        }
sl@0
   488
    return(IsDosEntry(aEntry));
sl@0
   489
    }
sl@0
   490
sl@0
   491
/**
sl@0
   492
Check if an entry is valid VFat
sl@0
   493
sl@0
   494
@param aEntry Entry to check
sl@0
   495
@param aPrevNum Number into VFat entries for a dos entry to ensure in correct position
sl@0
   496
@return ETrue if aEntry is a valid vfat entry
sl@0
   497
*/
sl@0
   498
TBool CScanDrive::IsValidVFatEntry(const TFatDirEntry& aEntry, TInt aPrevNum)const
sl@0
   499
    {
sl@0
   500
    if(aEntry.IsErased()||!aEntry.IsVFatEntry())
sl@0
   501
        return(EFalse);
sl@0
   502
    return(aEntry.NumFollowing()==aPrevNum);
sl@0
   503
    }
sl@0
   504
sl@0
   505
/**
sl@0
   506
Check if an entry is a Dos entry
sl@0
   507
sl@0
   508
@param aEntry Entry to check
sl@0
   509
@return ETrue if aEntry is a dos entry
sl@0
   510
*/
sl@0
   511
TBool CScanDrive::IsDosEntry(const TFatDirEntry& aEntry)const
sl@0
   512
    {
sl@0
   513
    TBool res = !(aEntry.Attributes()&~KEntryAttMaskSupported) && !aEntry.IsErased() && !aEntry.IsVFatEntry() && !aEntry.IsEndOfDirectory();
sl@0
   514
    return res;
sl@0
   515
    } 
sl@0
   516
sl@0
   517
/**
sl@0
   518
Add partial entry to iPartEntry under the error condition of not all Vfat entries 
sl@0
   519
being present
sl@0
   520
sl@0
   521
@param aStartPos Position of the Dos entry associated with the VFat entries
sl@0
   522
@param aEntry Directory Entry of the Dos entry associated with the VFat entries
sl@0
   523
@leave KErrCorrupt Occurs if the entry is not valid
sl@0
   524
*/
sl@0
   525
void CScanDrive::AddPartialVFatL(const TEntryPos& aStartPos, const TFatDirEntry& aEntry)
sl@0
   526
    {
sl@0
   527
    __PRINT2(_L("CScanDrive::AddPartialVFatL cluster=%d pos=%d"),aStartPos.iCluster,aStartPos.iPos);
sl@0
   528
    __ASSERT_ALWAYS(!IsDirError(),User::Leave(KErrCorrupt));
sl@0
   529
    iPartEntry.iEntryPos=aStartPos;
sl@0
   530
    iPartEntry.iEntry=aEntry;
sl@0
   531
    iDirError=EScanPartEntry;
sl@0
   532
    }
sl@0
   533
sl@0
   534
/**
sl@0
   535
Add entry position to iMatching
sl@0
   536
sl@0
   537
@param aEntryPos Position of the entry with the matching entry
sl@0
   538
@leave KErrCorrupt if the start cluster is 0 or more that two matching entries occurs
sl@0
   539
@return 
sl@0
   540
*/
sl@0
   541
TBool CScanDrive::AddMatchingEntryL(const TEntryPos& aEntryPos)
sl@0
   542
    {
sl@0
   543
    __PRINT2(_L("CScanDrive::AddMatchingEntryL cluster=%d pos=%d"),aEntryPos.iCluster,aEntryPos.iPos);
sl@0
   544
    __ASSERT_ALWAYS(iMatching.iStartCluster>0 && iMatching.iCount<KMaxMatchingEntries,User::Leave(KErrCorrupt));
sl@0
   545
    iMatching.iEntries[iMatching.iCount++]=aEntryPos;
sl@0
   546
    return iMatching.iCount==KMaxMatchingEntries;
sl@0
   547
    }
sl@0
   548
sl@0
   549
sl@0
   550
/**
sl@0
   551
Scan for differnces in the new and old FAT table writing them to media if discovered
sl@0
   552
sl@0
   553
@leave System wide error codes
sl@0
   554
*/
sl@0
   555
void CScanDrive::WriteNewFatsL()
sl@0
   556
//
sl@0
   557
// Write the new fat table to disk
sl@0
   558
//
sl@0
   559
    {
sl@0
   560
    if(iNewFat->FlushL())
sl@0
   561
        IndicateErrorsFound(); //-- indicate that we have found errors       
sl@0
   562
    }
sl@0
   563
sl@0
   564
TInt CScanDrive::GetReservedidL(TEntryPos aVFatPos)
sl@0
   565
//
sl@0
   566
// Return the id found in reserved2 field of dos entry
sl@0
   567
//
sl@0
   568
    {
sl@0
   569
    __PRINT(_L("CScanDrive::GetReservedidL"));
sl@0
   570
    TFatDirEntry entry;
sl@0
   571
    iMount->ReadDirEntryL(aVFatPos,entry);
sl@0
   572
    if(!IsDosEntry(entry))
sl@0
   573
        {
sl@0
   574
        TInt toMove=entry.NumFollowing();
sl@0
   575
        while(toMove--)
sl@0
   576
            iMount->MoveToNextEntryL(aVFatPos);
sl@0
   577
        iMount->ReadDirEntryL(aVFatPos,entry);
sl@0
   578
        }
sl@0
   579
    return(entry.RuggedFatEntryId());
sl@0
   580
    }
sl@0
   581
sl@0
   582
/**
sl@0
   583
Erase part entry found in iPartEntry
sl@0
   584
sl@0
   585
@leave System wide error code
sl@0
   586
*/
sl@0
   587
void CScanDrive::FixPartEntryL()
sl@0
   588
    {
sl@0
   589
    __PRINT2(_L("CScanDrive::FixPartEntryL cluster=%d,pos=%d"),iPartEntry.iEntryPos.iCluster,iPartEntry.iEntryPos.iPos);
sl@0
   590
    iMount->EraseDirEntryL(iPartEntry.iEntryPos,iPartEntry.iEntry);
sl@0
   591
    IndicateErrorsFound(); //-- indicate that we have found errors
sl@0
   592
    }
sl@0
   593
    
sl@0
   594
/**
sl@0
   595
Delete entry with largest value in the reserved2 section(bytes 20 and 21) of dos entry
sl@0
   596
    
sl@0
   597
@leave System wide error code
sl@0
   598
*/
sl@0
   599
void CScanDrive::FixMatchingEntryL()
sl@0
   600
    {
sl@0
   601
    __PRINT1(_L("CScanDrive::FixMatchingEntryL() start cluster=%d"),iMatching.iStartCluster);
sl@0
   602
    __ASSERT_ALWAYS(iMatching.iCount==KMaxMatchingEntries,User::Leave(KErrCorrupt));
sl@0
   603
    TInt idOne=GetReservedidL(iMatching.iEntries[0]);
sl@0
   604
    TInt idTwo=GetReservedidL(iMatching.iEntries[1]);
sl@0
   605
    TFatDirEntry entry;
sl@0
   606
    TInt num=idOne>idTwo?0:1;
sl@0
   607
    iMount->ReadDirEntryL(iMatching.iEntries[num],entry);
sl@0
   608
    iMount->EraseDirEntryL(iMatching.iEntries[num],entry);
sl@0
   609
    IndicateErrorsFound(); //-- indicate that we have found errors
sl@0
   610
    }
sl@0
   611
/**
sl@0
   612
Move past specified number of entries
sl@0
   613
sl@0
   614
@param aEntryPos Start position to move from, updated as move takes place
sl@0
   615
@param aEntry Directory entry moved to
sl@0
   616
@param aToMove Number of entries to move through
sl@0
   617
@param aDirEntries Number of entries moved, updated as move takes place
sl@0
   618
@leave System wide error code
sl@0
   619
*/
sl@0
   620
void CScanDrive::MovePastEntriesL(TEntryPos& aEntryPos,TFatDirEntry& aEntry,TInt aToMove,TInt& aDirEntries)
sl@0
   621
    {
sl@0
   622
    while(aToMove-- && aEntryPos.iCluster!=KEndOfDirectory)
sl@0
   623
        {
sl@0
   624
        iMount->MoveToNextEntryL(aEntryPos);
sl@0
   625
        ++aDirEntries;
sl@0
   626
        }
sl@0
   627
    iMount->ReadDirEntryL(aEntryPos,aEntry);
sl@0
   628
    }
sl@0
   629
sl@0
   630
/**
sl@0
   631
Adds aCluster to cluster list array so that it may be revisited later, avoids stack 
sl@0
   632
over flow
sl@0
   633
sl@0
   634
@param aCluster Directory cluster number to add to the list
sl@0
   635
@leave KErrNoMemory If allocation fails
sl@0
   636
*/
sl@0
   637
void CScanDrive::AddToClusterListL(TInt aCluster)
sl@0
   638
    {
sl@0
   639
    if(iListArrayIndex>=KMaxArrayDepth)
sl@0
   640
        return;
sl@0
   641
    if(iClusterListArray[iListArrayIndex]==NULL)
sl@0
   642
        iClusterListArray[iListArrayIndex]=new(ELeave) RArray<TInt>(KClusterListGranularity);
sl@0
   643
    iClusterListArray[iListArrayIndex]->Append(aCluster);
sl@0
   644
    }
sl@0
   645
sl@0
   646
sl@0
   647
#if defined(DEBUG_SCANDRIVE)
sl@0
   648
void CScanDrive::CompareFatsL()
sl@0
   649
//
sl@0
   650
// Compare new fat and first fat table
sl@0
   651
//  
sl@0
   652
    {
sl@0
   653
    __PRINT(_L("CScanDrive::CompareFatsL()"));
sl@0
   654
    TInt maxClusters;
sl@0
   655
    maxClusters=iMount->UsableClusters();
sl@0
   656
    for(TInt i=KFatFirstSearchCluster; i<maxClusters; ++i)
sl@0
   657
        {
sl@0
   658
        TInt realFat=iMount->FAT().ReadL(i);
sl@0
   659
        TInt newFat=iNewFat->ReadL(i);
sl@0
   660
        if(realFat!=newFat)
sl@0
   661
            {
sl@0
   662
            if(realFat!=0 && newFat==0)
sl@0
   663
                __PRINT1(_L("Lost cluster=%d\n"),i)
sl@0
   664
            else if((realFat>0 && !IsEofF(realFat)) && IsEofF(newFat))
sl@0
   665
                __PRINT1(_L("Hanging cluster = %d\n"),i)
sl@0
   666
            else if(realFat==0 && newFat>0)
sl@0
   667
                __PRINT1(_L("Unflushed cluster = %d\n"),i)
sl@0
   668
            else
sl@0
   669
                User::Leave(KErrCorrupt);
sl@0
   670
            }
sl@0
   671
        }
sl@0
   672
    }   
sl@0
   673
sl@0
   674
sl@0
   675
/** 
sl@0
   676
For debug purposes, print errors found as debug output 
sl@0
   677
 
sl@0
   678
*/ 
sl@0
   679
void CScanDrive::PrintErrors()
sl@0
   680
    {
sl@0
   681
    __PRINT1(_L("Directories visisted = %d\n"),iDirsChecked);
sl@0
   682
    if(iDirError==EScanPartEntry)
sl@0
   683
        __PRINT2(_L("Part entry-dir cluster=%d,dir pos=%d,\n"),iPartEntry.iEntryPos.iCluster,iPartEntry.iEntryPos.iPos)
sl@0
   684
    else if(iDirError==EScanMatchingEntry)
sl@0
   685
        {
sl@0
   686
        __PRINT1(_L("Matching cluster - cluster no=%d\n"),iMatching.iStartCluster);
sl@0
   687
        __PRINT2(_L("\tcluster 1 - dir cluster=%d,dir pos=%d\n"),iMatching.iEntries[0].iCluster,iMatching.iEntries[0].iPos);
sl@0
   688
        __PRINT2(_L("\tcluster 2 - dir cluster=%d,dir pos=%d\n"),iMatching.iEntries[1].iCluster,iMatching.iEntries[1].iPos);
sl@0
   689
        }
sl@0
   690
    }
sl@0
   691
    
sl@0
   692
#endif
sl@0
   693
sl@0
   694
sl@0
   695
sl@0
   696