userlibandfileserver/fileserver/sfat32/sl_scan32.cpp
changeset 43 96e5fb8b040d
child 33 0173bcd7697c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/userlibandfileserver/fileserver/sfat32/sl_scan32.cpp	Thu Dec 17 09:24:54 2009 +0200
@@ -0,0 +1,1034 @@
+// Copyright (c) 1998-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of the License "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+// f32\sfat32\sl_scan32.cpp
+// ScanDrive code, specific for EFAT32.FSY
+// 
+//
+
+/**
+ @file
+ @internalTechnology
+*/
+
+//#define DEBUG_SCANDRIVE
+
+#include "sl_std.h"
+#include "sl_scandrv.h"
+
+
+
+const TInt KEndOfDirectory			= 0xFFFF;   ///< End of directory marker
+const TInt KMaxScanDepth			= 20;       ///< Maximum scan depth of to avoid stack over flow 
+const TInt KClusterListGranularity	= 8;        ///< Granularity of cluster list used for storage of clusters when KMaxScanDepth is reached
+
+
+/**
+Creates a CScanDrive
+@param aMount The owning mount
+*/
+ CScanDrive* CScanDrive::NewL(CFatMountCB* aMount)
+	{
+	if(aMount==NULL)
+		User::Leave(KErrArgument);
+	CScanDrive* self=new (ELeave) CScanDrive();
+	CleanupStack::PushL(self);
+	self->ConstructL(aMount);
+	CleanupStack::Pop();
+	return self;
+	}
+
+
+CScanDrive::~CScanDrive()
+	{
+	for(TInt i=0;i<KMaxArrayDepth && iClusterListArray[i]!=NULL;++i)
+		{
+		iClusterListArray[i]->Close();
+		delete iClusterListArray[i];
+		}
+
+    iMediaFatBits.Close();
+    iScanFatBits.Close();
+
+	}
+
+/**
+Allocates the Cluster array, the bit packed Fats and if run in a seperate
+thread the extra CFatTable and cluster buffer
+
+@param aMount The owning mount
+*/
+void CScanDrive::ConstructL(CFatMountCB* aMount)
+    {
+	iMount=aMount;
+	
+    //-- create bit vectors that will represent FAT on media and reconstructed by ScanDrive
+    //-- each bit in the vector represents 1 FAT cluster.
+    const TUint32 KClustersNum = iMount->UsableClusters()+KFatFirstSearchCluster; //-- UsableClusters() doesn't count first 2 unused clusers
+    
+    CleanupClosePushL(iMediaFatBits);
+    CleanupClosePushL(iScanFatBits);
+
+    iMediaFatBits.CreateL(KClustersNum);
+    iScanFatBits.CreateL(KClustersNum);;
+
+    CleanupStack::Pop(&iScanFatBits);
+    CleanupStack::Pop(&iMediaFatBits);
+    }
+
+//----------------------------------------------------------------------------------------------------
+/**
+    FAT type-agnnostic parser. Reads whole FAT and sets up a bit vector.
+    for FAT12/16 it's OK, because the FAT12/16 is fully cached.
+*/
+void CScanDrive::DoParseFatL()
+    {
+    const TInt MaxClusters = iMount->UsableClusters()+KFatFirstSearchCluster;
+    
+
+
+    iMediaFatBits.Fill(0);
+
+    for(TInt i=KFatFirstSearchCluster; i<MaxClusters; ++i)
+	    {
+        const TUint32 nFatEntry = ReadFatL(i);
+       
+        //-- each '1' bit represents a used cluster 
+        if(nFatEntry != KSpareCluster) 
+            iMediaFatBits.SetBit(i);
+	    }
+    }
+
+//----------------------------------------------------------------------------------------------------
+/**
+    Parse FAT32 buffer.
+    @param  aBuf            buffer, containing FAT32 entries (current portion of FAT)
+    @param  aCurrFatEntry   current FAT entry processed
+*/
+void CScanDrive::DoParseFat32Buf(const TPtrC8& aBuf, TUint32& aCurrFatEntry)
+    {
+    ASSERT((aBuf.Size() & (sizeof(TFat32Entry)-1)) == 0);
+    
+    const TInt KNumEntries = aBuf.Size() >> KFat32EntrySzLog2;
+    const TFat32Entry* const pFatEntry = (const TFat32Entry*)(aBuf.Ptr()); 
+
+    for(TInt i=0; i<KNumEntries; ++i)
+        {
+        if(aCurrFatEntry >= KFatFirstSearchCluster)
+            {
+            if((pFatEntry[i] & KFat32EntryMask) != KSpareCluster)
+                {//-- found a non-free FAT32 entry
+                iMediaFatBits.SetBit(aCurrFatEntry);
+                }
+            }
+        ++aCurrFatEntry;    
+        }
+    }
+
+//----------------------------------------------------------------------------------------------------
+/**
+    A specialised method to read and parse FAT32 using a larger buffer.
+    1. Larger buffer gives better read performance
+    2. using dedicated buffer doesn't trash FAT32 LRU cache.
+*/
+void CScanDrive::DoParseFat32L()
+    {
+    ASSERT(iMount->FatType() == EFat32);
+
+    const TUint32 KNumClusters  = iMount->UsableClusters()+KFatFirstSearchCluster;
+    const TUint32 KFat1StartPos = iMount->StartOfFatInBytes();
+    const TUint32 KFatSize      = KNumClusters * sizeof(TFat32Entry); //-- usable size of one FAT.
+
+    const TUint32 KFatBufSz = 32*K1KiloByte; //-- buffer size for FAT reading. 32K seems to be optimal size
+
+    iMediaFatBits.Fill(0);
+
+    RBuf8 buf;
+    CleanupClosePushL(buf);
+
+    //-- allocate memory for FAT parse buffer
+    buf.CreateMaxL(KFatBufSz);
+
+    //-- read FAT directly from the media into the large buffer and parse it
+    TUint32 rem = KFatSize;
+    TUint32 mediaPos = KFat1StartPos;   
+    TUint32 currFatEntry = 0;
+
+    while(rem)
+        {
+        const TUint32 bytesToRead=Min(rem, KFatBufSz);
+        TPtrC8 ptrData(buf.Ptr(), bytesToRead);
+
+        //-- read portion of the FAT into buffer
+        User::LeaveIfError(iMount->LocalDrive()->Read(mediaPos, bytesToRead, buf)); 
+
+        //-- parse the buffer and populate bit vector
+        DoParseFat32Buf(ptrData, currFatEntry);
+        
+        mediaPos += bytesToRead;
+        rem -= bytesToRead;
+        }
+
+    buf.Close();
+    CleanupStack::PopAndDestroy(&buf); 
+    }
+
+//----------------------------------------------------------------------------------------------------
+/**
+    Sets up a bit list representation of the media fat
+    Reads whole FAT and sets '1' bits in the bit vector corresponding to the occupied clusters.
+*/
+void CScanDrive::ReadMediaFatL()
+    {
+    ASSERT(iMount->ConsistentState());
+    
+    if(iMount->FatType() == EFat32)
+        {//-- for FAT32 try to use specialised method of parsing
+        TInt nRes;
+        TRAP(nRes, DoParseFat32L())
+        if(nRes == KErrNone)
+            return;
+        }
+    
+    //-- use old FAT-agnostic parsing
+    DoParseFatL();
+    }
+
+/**
+Set a cluster as visited in the bit packed scan Fat
+
+@param aCluster Cluster number
+*/
+void CScanDrive::SetUsedL(TUint aCluster)
+	{
+	__ASSERT_ALWAYS(aCluster >= KFatFirstSearchCluster && aCluster < (KFatFirstSearchCluster+iMount->UsableClusters()),User::Leave(KErrCorrupt));
+    iScanFatBits.SetBit(aCluster);
+	}
+
+
+/**
+Query whether a cluster is already set as used 
+
+@param aCluster Cluster to query
+*/
+TBool CScanDrive::AlreadyUsedL(TUint aCluster) const
+	{
+	__ASSERT_ALWAYS(aCluster >= KFatFirstSearchCluster && aCluster < (KFatFirstSearchCluster+iMount->UsableClusters()),User::Leave(KErrCorrupt));
+
+    return iScanFatBits[aCluster];
+	}
+
+/**
+@param aPos Position in a directory cluster
+@return  ETrue if aPos is the last entry in the root directory
+*/
+TBool CScanDrive::IsEndOfRootDir(const TEntryPos& aPos)const
+	{
+	return(iMount->IsRootDir(aPos)&&(iMount->StartOfRootDirInBytes()+aPos.iPos==(iMount->RootDirEnd()-KSizeOfFatDirEntry)));
+	}
+
+/**
+@param aVal Value of the cluster to be tested
+@return ETrue if aVal is the end of cluster marker
+*/
+TBool CScanDrive::IsEofF(TInt aVal) const 
+	{
+    return iMount->IsEndOfClusterCh(aVal);
+	}
+
+
+/**
+@return True if a directory error has been found
+*/
+TBool CScanDrive::IsDirError() const
+	{
+	return(iDirError!=0);
+	}
+
+/**
+    After StartL() and finishing allows us to know if there were any problems at all.
+    The client may wish to remount the filesystem if there were errors.
+
+    @return EFalse if there were no problems in FS.
+*/
+TBool CScanDrive::ProblemsDiscovered() const
+{
+    return IsDirError() || iFoundProblems;
+}
+
+/**
+    Sets the flag indicating than there are errors in filesystem structure
+    See ProblemsDiscovered()
+*/
+void CScanDrive::IndicateErrorsFound()
+{
+    iFoundProblems = ETrue;
+}
+
+
+
+/**
+Start point for scan drive also fixes up errors 
+
+@return The result of the scan
+@leave 
+*/
+TInt CScanDrive::StartL()
+	{
+	__PRINT1(_L("CScanDrive::StartL(), drive:%d"), iMount->DriveNumber());
+
+    //-- used for measuring time
+    TTime   timeStart;
+    TTime   timeEnd;
+    timeStart.UniversalTime(); //-- take start time
+
+
+	ReadMediaFatL();
+
+	CheckDirStructureL();
+#if defined(DEBUG_SCANDRIVE)
+	CompareFatsL();
+#endif
+	if(IsDirError())
+		FixupDirErrorL();
+
+	WriteNewFatsL();
+#if defined(DEBUG_SCANDRIVE)
+	PrintErrors();
+#endif
+   
+
+
+    timeEnd.UniversalTime(); //-- take end time
+    const TInt msScanTime = (TInt)( (timeEnd.MicroSecondsFrom(timeStart)).Int64() / K1mSec);
+    (void)msScanTime;
+    
+    __PRINT1(_L("CScanDrive: Directories visisted = %d\n"),iDirsChecked);
+    __PRINT1(_L("#@@@ CScanDrive time taken:%d ms "), msScanTime);
+
+	return KErrNone;
+	}
+
+/**
+Fix errors detected by the drive scan
+ 
+@leave System wide error code
+*/
+void CScanDrive::FixupDirErrorL()
+	{
+	if(!IsDirError())
+		return;
+	
+    if(iDirError==EScanMatchingEntry)
+		{
+		FindSameStartClusterL();
+		FixMatchingEntryL();
+		}
+	else
+		{
+        FixPartEntryL();
+        }
+
+    IndicateErrorsFound(); //-- indicate that we have found errors
+	}
+
+/**
+Find positions of entries with same start cluster for error correction, searches
+the whole volume. Starts at the root directory. 
+
+@leave System wide error code
+*/
+void CScanDrive::FindSameStartClusterL()
+	{
+	TInt err=FindStartClusterL(iMount->RootIndicator());
+	if(err==KErrNone)
+		return;
+	for(TInt i=0;i<KMaxArrayDepth && iClusterListArray[i]!=NULL;++i)
+		{
+		RArray<TInt>* clusterList=iClusterListArray[i];
+		for(TInt j=0;j<clusterList->Count();++j)
+			{
+			iRecursiveDepth=0;
+			err=FindStartClusterL((*clusterList)[j]);
+			if(err==KErrNone)
+				return;
+			}
+		}
+	__ASSERT_ALWAYS(err==KErrNone,User::Leave(KErrNotFound));
+	}
+
+/**
+Scan through directory structure looking for start cluster found in iMatching
+
+@param aDirCluster Start cluster for scan to start
+@return System wide error value
+@leave 
+*/
+TInt CScanDrive::FindStartClusterL(TInt aDirCluster)
+	{
+	__PRINT1(_L("CScanDrive::FindStartCluster dirCluster=%d"),aDirCluster);
+	__ASSERT_ALWAYS(aDirCluster>=iMount->RootIndicator(),User::Leave(KErrCorrupt));
+	if(++iRecursiveDepth==KMaxScanDepth)
+		{
+		--iRecursiveDepth;
+		return(KErrNotFound);
+		}
+	TEntryPos entryPos(aDirCluster,0);
+	TInt dirEntries=0;
+	FOREVER
+		{
+		TFatDirEntry entry;
+		ReadDirEntryL(entryPos,entry);
+		if(entry.IsParentDirectory()||entry.IsCurrentDirectory()||entry.IsErased())
+			{
+			if(IsEndOfRootDir(entryPos))
+				break;
+			MoveToNextEntryL(entryPos);
+			continue;
+			}
+		if(entry.IsEndOfDirectory())
+			break;
+		TBool isComplete;
+		TEntryPos vfatPos=entryPos;
+		isComplete=MoveToVFatEndL(entryPos,entry,dirEntries);
+		__ASSERT_ALWAYS(isComplete,User::Leave(KErrBadName));
+		TInt err=CheckEntryClusterL(entry,vfatPos);
+		if(err==KErrNone)
+			{
+			--iRecursiveDepth;
+			return(err);
+			}
+		if(IsEndOfRootDir(entryPos))
+			break;
+		MoveToNextEntryL(entryPos);
+		}
+	--iRecursiveDepth;
+	return(KErrNotFound);
+	}
+
+/**
+Procces aEntry to find matching start cluster
+
+@param aEntry Directory entry to check
+@param aEntryPos Position of directory to check
+@return System wide error value
+@leave 
+*/
+TInt CScanDrive::CheckEntryClusterL(const TFatDirEntry& aEntry, const TEntryPos& aEntryPos)
+	{
+	__PRINT(_L("CScanDrive::CheckEntryClusterL"));
+	if(iMount->StartCluster(aEntry)==iMatching.iStartCluster)
+		{
+		TBool complete=AddMatchingEntryL(aEntryPos);
+		if(complete)
+			return(KErrNone);
+		}
+	else if(aEntry.Attributes()&KEntryAttDir)
+		return(FindStartClusterL(iMount->StartCluster(aEntry)));
+	return(KErrNotFound);
+	}
+
+/**
+Checks directory strucutre for errors, can be considered the start point of the scan.  
+Handles recursion depth to avoid stack overflow.
+
+@leave System wide error code
+*/
+void CScanDrive::CheckDirStructureL()
+	{
+	CheckDirL(iMount->RootIndicator());
+	// Due to recursive nature of CheckDirL when a depth of
+	// KMaxScanDepth is reached clusters are stored in a list
+	// and passed into CheckDirL afresh
+	for(TInt i=0;i<KMaxArrayDepth && iClusterListArray[i]!=NULL;++i)
+		{
+		RArray<TInt>* clusterList=iClusterListArray[i];
+		++iListArrayIndex;
+		for(TInt j=0;j<clusterList->Count();++j)
+			{
+			iRecursiveDepth=0;
+			CheckDirL((*clusterList)[j]);
+			}
+		}
+	}
+
+
+/**
+Function is called recursively with Process entry untill the whole volume has been scanned.
+Each directory entry is scanned for errors, these are recorded for later fixing. 
+
+@param aCluster Directory cluster to start checking
+@leave System wide error codes
+*/
+void CScanDrive::CheckDirL(TInt aCluster)
+	{
+	__PRINT1(_L("CScanDrive::CheckDirL aCluster=%d"),aCluster);
+	__ASSERT_ALWAYS(aCluster>=0,User::Leave(KErrCorrupt));
+	// check depth of recursion
+	if(++iRecursiveDepth==KMaxScanDepth)
+		{
+		AddToClusterListL(aCluster);
+		--iRecursiveDepth;
+		return;
+		}
+#if defined(DEBUG_SCANDRIVE)
+	++iDirsChecked;
+#endif
+	TEntryPos entryPos(aCluster,0);
+	TInt dirEntries=0;
+	FOREVER
+		{
+		TFatDirEntry entry;
+		ReadDirEntryL(entryPos,entry);
+		if(!iMount->IsEndOfClusterCh(entryPos.iCluster))
+			++dirEntries;
+		if(entry.IsParentDirectory()||entry.IsCurrentDirectory()||entry.IsErased())
+			{
+			if(IsEndOfRootDir(entryPos))
+				break;
+			MoveToNextEntryL(entryPos);
+			continue;
+			}
+		if(entry.IsEndOfDirectory())
+			{
+			if(aCluster)	
+				WriteClusterChainL(aCluster,dirEntries<<KSizeOfFatDirEntryLog2);
+			break;
+			}
+		TEntryPos origPos=entryPos;
+		TFatDirEntry origEntry=entry;
+		TInt origDirEntries=dirEntries;
+		TBool isComplete;
+		isComplete=MoveToVFatEndL(entryPos,entry,dirEntries);
+		// Only assume that this is a corrupted VFAT entry if the VFAT attributes are set; 
+		// assuming a non-VFAT corrupted entry is a VFAT entry is dangerous as we then assume that the 
+		// first byte is a count of entries to skip, thus completely invalidating the next <n> directories.
+		if (!isComplete && origEntry.IsVFatEntry())
+			{
+			AddPartialVFatL(origPos,origEntry);
+			if(entryPos.iCluster!=KEndOfDirectory)
+				{
+				TInt toMove=origEntry.NumFollowing()-(dirEntries-origDirEntries);
+				if(toMove)
+					MovePastEntriesL(entryPos,entry,toMove,dirEntries);
+				}
+			else
+				{
+				// we fell off the end of the directory file, so just strip this
+				// incomplete long file name entry
+				dirEntries = origDirEntries;
+				}
+			}
+		else
+			ProcessEntryL(entry);
+		if(IsEndOfRootDir(entryPos))
+			break;
+		MoveToNextEntryL(entryPos);
+		}
+	--iRecursiveDepth;
+	}
+
+
+/**
+Process non trivial entries, such as files, if they are correct by filling out their 
+cluster allocation in the bit packed Fat table. If it comes accross a directory 
+CheckDirL will be called.
+
+@param aEntry Directory entry to check
+@leave System wide error code
+*/
+void CScanDrive::ProcessEntryL(const TFatDirEntry& aEntry)
+	{
+	__PRINT(_L("CScanDrive::ProcessEntryL"));
+	TInt entryAtt=aEntry.Attributes();
+
+	__ASSERT_ALWAYS(!(entryAtt&~KEntryAttMaskSupported)&&!aEntry.IsErased(),User::Leave(KErrCorrupt));
+	if(!(entryAtt&(KEntryAttDir|KEntryAttVolume)) && iMount->StartCluster(aEntry)>0)
+		WriteClusterChainL(iMount->StartCluster(aEntry),(TUint) aEntry.Size());
+	else if(entryAtt&KEntryAttDir)
+		CheckDirL(iMount->StartCluster(aEntry));
+	}
+
+/**
+Writes out the cluster chain for a correct file or directory, checks that the cluster 
+has not already been used and that the correct number of clusters are allocated for the 
+size of file. Registers cluster as used if correct
+
+@param aCluster Cluster chain start point
+@param aSizeInBytes Size of the file or directory in bytes
+@leave System wide error values
+*/
+void CScanDrive::WriteClusterChainL(TInt aCluster,TUint aSizeInBytes)
+//
+// Mark off in the new fat the clusters used by entry with start cluster of aCluster
+//
+	{
+	__PRINT1(_L("CScanDrive::WriteClusterChainL starting at %d"),aCluster);
+	__ASSERT_ALWAYS(aCluster>0,User::Leave(KErrCorrupt));
+	TInt clusterCount;
+	if(aSizeInBytes==0)
+		clusterCount=1;
+	else
+		clusterCount = (TInt) (( TInt64(aSizeInBytes) + TInt64((1<<iMount->ClusterSizeLog2())-1) ) >> iMount->ClusterSizeLog2());
+	TInt startCluster=aCluster;
+	while(clusterCount)
+		{
+		if(AlreadyUsedL(aCluster))
+			{
+			__ASSERT_ALWAYS(!IsDirError()&&iMatching.iStartCluster==0&&aCluster==startCluster,User::Leave(KErrCorrupt));
+			iMatching.iStartCluster=aCluster;
+			iDirError=EScanMatchingEntry;		//ERROR POINT
+            IndicateErrorsFound(); //-- indicate that we have found errors
+			return;
+			}
+		if(clusterCount==1)
+			{
+			if(!iMount->IsEndOfClusterCh(ReadFatL(aCluster)))
+				{
+				//This is a genuine truncation
+				iTruncationCluster = aCluster;								
+                }
+			SetUsedL(aCluster);
+			return;
+			}
+		else
+			{
+			TInt clusterVal=ReadFatL(aCluster);
+			__ASSERT_ALWAYS(!IsEofF(clusterVal) && clusterVal!=0,User::Leave(KErrCorrupt));
+			SetUsedL(aCluster);
+			aCluster=clusterVal;
+			--clusterCount;
+			}
+		}
+	}
+
+/**
+Move to dos entry, checking all vfat entry ID numbers are in sequence.
+Assumes aEntry is not erased
+
+@param aPos Position of the entry to move from, returns with new position
+@param aEntry The Dos entry after the Vfat entries on return
+@param aDirLength Running total of the length of the directory in entries
+@leave System wide error codes
+@return EFalse if not valid vfat entries or dos entry, else returns ETrue
+*/
+TBool CScanDrive::MoveToVFatEndL(TEntryPos& aPos,TFatDirEntry& aEntry,TInt& aDirLength)
+	{
+	__PRINT2(_L("CScanDrive::MoveToVFatEndL cluster=%d,pos=%d"),aPos.iCluster,aPos.iPos);
+	if(!aEntry.IsVFatEntry())
+		return IsDosEntry(aEntry);
+	TInt toFollow=aEntry.NumFollowing();
+	__ASSERT_ALWAYS(toFollow>0&&!aEntry.IsErased(),User::Leave(KErrCorrupt));
+	FOREVER
+		{
+		MoveToNextEntryL(aPos);
+		ReadDirEntryL(aPos,aEntry);
+		++aDirLength;
+		--toFollow;
+		if(!toFollow)
+			break;
+		if(!IsValidVFatEntry(aEntry,toFollow))
+			return(EFalse);
+		}
+	return(IsDosEntry(aEntry));
+	}
+
+/**
+Check if an entry is valid VFat
+
+@param aEntry Entry to check
+@param aPrevNum Number into VFat entries for a dos entry to ensure in correct position
+@return ETrue if aEntry is a valid vfat entry
+*/
+TBool CScanDrive::IsValidVFatEntry(const TFatDirEntry& aEntry,TInt aPrevNum)const
+	{
+	if(aEntry.IsErased()||!aEntry.IsVFatEntry())
+		return(EFalse);
+	return(aEntry.NumFollowing()==aPrevNum);
+	}
+
+/**
+Check if an entry is a Dos entry
+
+@param aEntry Entry to check
+@return ETrue if aEntry is a dos entry
+*/
+TBool CScanDrive::IsDosEntry(const TFatDirEntry& aEntry)const
+	{
+	TBool res = !(aEntry.Attributes()&~KEntryAttMaskSupported) && !aEntry.IsErased() && !aEntry.IsVFatEntry() && !aEntry.IsEndOfDirectory();
+	return res;
+	} 
+
+/**
+Add partial entry to iPartEntry under the error condition of not all Vfat entries 
+being present
+
+@param aStartPos Position of the Dos entry associated with the VFat entries
+@param aEntry Directory Entry of the Dos entry associated with the VFat entries
+@leave KErrCorrupt Occurs if the entry is not valid
+*/
+void CScanDrive::AddPartialVFatL(const TEntryPos& aStartPos, const TFatDirEntry& aEntry)
+	{
+	__PRINT2(_L("CScanDrive::AddPartialVFatL cluster=%d pos=%d"),aStartPos.iCluster,aStartPos.iPos);
+	__ASSERT_ALWAYS(!IsDirError(),User::Leave(KErrCorrupt));
+	iPartEntry.iEntryPos=aStartPos;
+	iPartEntry.iEntry=aEntry;
+	iDirError=EScanPartEntry;
+	}
+
+/**
+Add entry position to iMatching
+
+@param aEntryPos Position of the entry with the matching entry
+@leave KErrCorrupt if the start cluster is 0 or more that two matching entries occurs
+@return 
+*/
+TBool CScanDrive::AddMatchingEntryL(const TEntryPos& aEntryPos)
+	{
+	__PRINT2(_L("CScanDrive::AddMatchingEntryL cluster=%d pos=%d"),aEntryPos.iCluster,aEntryPos.iPos);
+	__ASSERT_ALWAYS(iMatching.iStartCluster>0 && iMatching.iCount<KMaxMatchingEntries,User::Leave(KErrCorrupt));
+	iMatching.iEntries[iMatching.iCount++]=aEntryPos;
+	return iMatching.iCount==KMaxMatchingEntries;
+	}
+
+
+static inline TBool BoolXOR(TBool a1, TBool a2)
+    {
+    if(!a1 && !a2)        
+        return EFalse;
+    else if(a1 && a2)
+        return EFalse;
+    else
+        return ETrue;
+    }
+
+
+/**
+Scan for differnces in the new and old FAT table writing them to media if discovered
+
+@leave System wide error codes
+*/
+void CScanDrive::WriteNewFatsL()
+    {
+	
+    __PRINT1(_L("CScanDrive::WriteNewFatsL() drv:%d"),iMount->DriveNumber());
+
+    TUint32 nClustersFixed = 0; //-- fixed clusters count
+    TUint32 nBadClusters   = 0; //-- bad cluster count
+    TUint32 dirtyFatSector = 0; //-- FAT table media sector with not-flushed data
+
+    const TUint32 KSectorSzLog2 = iMount->SectorSizeLog2(); //-- Log2(media Sector Size)
+    
+    TUint32 diffPos;
+    if(iMediaFatBits.Diff(iScanFatBits, diffPos))
+    {//-- there is a difference between FATs' bit representation
+    
+        ASSERT(diffPos >= KFatFirstSearchCluster);
+
+        const TUint32 maxClusters = iScanFatBits.Size();
+    
+        for(TUint32 i=diffPos; i<maxClusters; ++i)
+	        {
+            if(BoolXOR(iMediaFatBits[i], iScanFatBits[i]))
+                {//-- difference in the cluster "i" between a real FAT and what ScanDrive restored.
+          
+                //-- indicate that there are some problems in FAT. and we probably wrote something there.
+                IndicateErrorsFound(); 
+                
+                //-- skip BAD cluster, can't mark it as unused.
+                if(iMount->IsBadCluster(ReadFatL(i)))
+                    {
+                    ++nBadClusters;
+                    continue;
+                    }
+         
+                //-- here we found a lost cluster. Its FAT entry will be replaced with KSpareCluster. In the case of multiple lost clusters FAT table will
+                //-- be flushed on media sector basis. It is much faster than flushing FAT after every write and will
+                //-- guarantee that FAT won't be corrupted if the media driver provides atomic sector write. 
+                if(nClustersFixed == 0)
+                    {//-- this is the first lost cluster entry we found
+                    
+                    //-- relative FAT media sector for the 'i' entry. The real value doesn't matter, 
+                    //-- we will just be flushing FAT before writing to the different FAT media sector.
+                    dirtyFatSector = iMount->FAT().PosInBytes(i) >> KSectorSzLog2; 
+                    
+                    iMount->FAT().WriteL(i, KSpareCluster); //-- fix lost cluster
+                    }
+                else
+                    {
+                    const TUint32 fatSec = iMount->FAT().PosInBytes(i) >> KSectorSzLog2; 
+
+                    if(fatSec != dirtyFatSector)
+                        {//-- we are going to write to a differrent media sector
+                        iMount->FAT().FlushL();
+                        iMount->FAT().WriteL(i, KSpareCluster); //-- fix lost cluster
+                        dirtyFatSector = fatSec;
+                        }
+                    else
+                        {//-- write to the same FAT media sector without flushing
+                        iMount->FAT().WriteL(i, KSpareCluster); //-- fix lost cluster
+                        }
+                    
+                    }
+
+                ++nClustersFixed;
+        
+                }//if(BoolXOR(iMediaFatBits[i], iScanFatBits[i])
+
+            }//for(TInt i=KFatFirstSearchCluster; i<maxClusters; ++i)
+
+    }//if(iMediaFatBits.Diff(iScanFatBits, diffPos))
+
+
+    if(nClustersFixed)
+        iMount->FAT().FlushL();
+    
+    //------
+
+    if(iTruncationCluster != 0)
+        {
+	    iMount->FAT().WriteFatEntryEofL(iTruncationCluster); 
+		iMount->FAT().FlushL();
+		
+        //-- indicate that there are some problems in FAT. and we probably wrote something there.
+        IndicateErrorsFound(); //-- indicate that we have found errors
+
+        ++nClustersFixed;
+        }
+    
+    __PRINT2(_L("CScanDrive::WriteNewFatsL() fixed:%d, bad:%d"), nClustersFixed, nBadClusters);
+    }
+
+/**
+Read the ID stored in reserved2 in the Dos entry or associated with the Dos entry of the 
+Entry at the position passed in. This is used to find which version of two matching entries 
+should be kept.
+
+@param aVFatPos Position of an entry to read ID from
+@leave System wide error codes
+@return The ID found in reserved2 field of dos entry 
+*/
+TInt CScanDrive::GetReservedidL(TEntryPos aVFatPos)
+	{
+	__PRINT(_L("CScanDrive::GetReservedidL"));
+	TFatDirEntry entry;
+	ReadDirEntryL(aVFatPos,entry);
+	if(!IsDosEntry(entry))
+		{
+		TInt toMove=entry.NumFollowing();
+		while(toMove--)
+			MoveToNextEntryL(aVFatPos);
+		ReadDirEntryL(aVFatPos,entry);
+		}
+	return(entry.RuggedFatEntryId());
+	}
+
+/**
+Erase part entry found in iPartEntry
+
+@leave System wide error code
+*/
+void CScanDrive::FixPartEntryL()
+	{
+	__PRINT2(_L("CScanDrive::FixPartEntryL cluster=%d,pos=%d"),iPartEntry.iEntryPos.iCluster,iPartEntry.iEntryPos.iPos);
+	iMount->EraseDirEntryL(iPartEntry.iEntryPos,iPartEntry.iEntry);
+    IndicateErrorsFound(); //-- indicate that we have found errors
+	}
+	
+/**
+Delete entry with largest value in the reserved2 section(bytes 20 and 21) of dos entry
+
+@leave System wide error code
+*/
+void CScanDrive::FixMatchingEntryL()
+	{
+	__PRINT1(_L("CScanDrive::FixMatchingEntryL() start cluster=%d"),iMatching.iStartCluster);
+	__ASSERT_ALWAYS(iMatching.iCount==KMaxMatchingEntries,User::Leave(KErrCorrupt));
+	TInt idOne=GetReservedidL(iMatching.iEntries[0]);
+	TInt idTwo=GetReservedidL(iMatching.iEntries[1]);
+	TFatDirEntry entry;
+	TInt num=idOne>idTwo?0:1;
+	ReadDirEntryL(iMatching.iEntries[num],entry);
+	iMount->EraseDirEntryL(iMatching.iEntries[num],entry);
+    IndicateErrorsFound(); //-- indicate that we have found errors
+	}
+/**
+Move past specified number of entries
+
+@param aEntryPos Start position to move from, updated as move takes place
+@param aEntry Directory entry moved to
+@param aToMove Number of entries to move through
+@param aDirEntries Number of entries moved, updated as move takes place
+@leave System wide error code
+*/
+void CScanDrive::MovePastEntriesL(TEntryPos& aEntryPos,TFatDirEntry& aEntry,TInt aToMove,TInt& aDirEntries)
+	{
+	while(aToMove-- && aEntryPos.iCluster!=KEndOfDirectory)
+		{
+		MoveToNextEntryL(aEntryPos);
+		++aDirEntries;
+		}
+	ReadDirEntryL(aEntryPos,aEntry);
+	}
+
+/**
+Adds aCluster to cluster list array so that it may be revisited later, avoids stack 
+over flow
+
+@param aCluster Directory cluster number to add to the list
+@leave KErrNoMemory If allocation fails
+*/
+void CScanDrive::AddToClusterListL(TInt aCluster)
+	{
+	if(iListArrayIndex>=KMaxArrayDepth)
+		return;
+	if(iClusterListArray[iListArrayIndex]==NULL)
+		iClusterListArray[iListArrayIndex]=new(ELeave) RArray<TInt>(KClusterListGranularity);
+	iClusterListArray[iListArrayIndex]->Append(aCluster);
+	}
+
+
+#if defined(DEBUG_SCANDRIVE)
+/**
+Used for debug purposes only, compares new Fat and first Fat table, displays any differences
+and there meaning
+
+@leave System wide error codes
+*/
+void CScanDrive::CompareFatsL() const 
+    {
+	__PRINT(_L("CScanDrive::CompareFatsL()"));
+		
+   
+    TUint32 diffPos;
+    if(!iMediaFatBits.Diff(iScanFatBits, diffPos))
+        return; //-- FATs are identical
+    
+    //-- there is a difference between the real FAT and reconstructed one. Find the mismaching bit and fix FAT. 
+    const TInt clusters = iMount->UsableClusters();
+    ASSERT(diffPos < (TUint32)clusters);
+                        
+    TInt scanusedcnt=0;
+	TInt mediausedcnt=0;
+	
+    for(TInt i=diffPos; i<clusters; ++i)
+	    {
+        const TBool bRealFatEntry = iMediaFatBits[i];
+        const TBool bNewFatEntry  = iScanFatBits[i];
+
+		if(BoolXOR(bRealFatEntry, bNewFatEntry))
+		    {
+			if(bRealFatEntry && !bNewFatEntry)
+			    {	
+                __PRINT1(_L("Lost cluster=%d\n"),i);
+                }
+			else if((bRealFatEntry && !IsEofF(ReadFatL(i))) && (i==iTruncationCluster))
+			    {
+            	__PRINT1(_L("Hanging cluster = %d\n"),i);
+                }
+			else if(!bRealFatEntry && bNewFatEntry)
+                {
+				__PRINT1(_L("Unflushed cluster = %d\n"),i);
+                }
+			else
+				User::Leave(KErrCorrupt);
+		    }
+		
+        if(bRealFatEntry)
+			mediausedcnt++;
+
+		if(bNewFatEntry)
+			scanusedcnt++;
+        }        	
+
+    __PRINT2(_L("Scan Fat Used=%d, Media Fat Used=%d \n"),scanusedcnt,mediausedcnt);
+    }	
+
+/**
+For debug purposes, print errors found as debug output
+*/
+void CScanDrive::PrintErrors()
+    {
+	__PRINT1(_L("Directories visisted = %d\n"),iDirsChecked);
+
+	if(iDirError==EScanPartEntry)
+	    {
+    	__PRINT2(_L("Part entry-dir cluster=%d,dir pos=%d,\n"),iPartEntry.iEntryPos.iCluster,iPartEntry.iEntryPos.iPos);
+        }
+	else if(iDirError==EScanMatchingEntry)
+	    {
+		__PRINT1(_L("Matching cluster - cluster no=%d\n"),iMatching.iStartCluster);
+		__PRINT2(_L("\tcluster 1 - dir cluster=%d,dir pos=%d\n"),iMatching.iEntries[0].iCluster,iMatching.iEntries[0].iPos);
+		__PRINT2(_L("\tcluster 2 - dir cluster=%d,dir pos=%d\n"),iMatching.iEntries[1].iCluster,iMatching.iEntries[1].iPos);
+	    }
+    }
+#endif
+
+/**
+Read a FAT directory entry from disk, either reads directly from the main cache or
+from the cluster buffer if scan drive is running in a seperate thread.
+
+@param aPos Media position of entry to read
+@param aDirEntry Contents of directory entry read
+@leave System wide error code 
+*/
+void CScanDrive::ReadDirEntryL(const TEntryPos& aPos,TFatDirEntry& aDirEntry)
+    {
+	__PRINT(_L("CScanDrive::ReadDirEntryL"));
+	if (iMount->IsEndOfClusterCh(aPos.iCluster))
+		{
+		Mem::FillZ(&aDirEntry,sizeof(TFatDirEntry));
+		return;
+		}
+
+	iMount->ReadDirEntryL(aPos, aDirEntry);
+    }
+
+
+/**
+Move to next directory entry, if anEntry is at the end of the cluster, and we are not 
+the root dir, move it to the next cluster in the chain.
+
+@param aPos Current directory position up dated to position of next entry.
+@leave System wide error codes
+
+*/
+void CScanDrive::MoveToNextEntryL(TEntryPos& aPos)
+	{
+	//__PRINT(_L("CScanDrive::MoveToNextEntryL"));
+	iMount->MoveToNextEntryL(aPos);
+	}	
+
+/**
+Read a cluster from the Media Fat if scan run in a seperate thread read from scan fat table
+otherwise read from mount owned Fat table
+
+@param aClusterNum Cluster to read
+@leave System wide error code
+@return Value of cluster read from Fat
+*/
+TUint32 CScanDrive::ReadFatL(TInt aClusterNum) const
+	{
+	return iMount->FAT().ReadL(aClusterNum);			
+    }
+
+
+
+
+
+
+
+
+
+
+