userlibandfileserver/fileserver/sfat32/sl_scan32.cpp
changeset 36 538db54a451d
parent 33 0173bcd7697c
child 87 2f92ad2dc5db
child 90 947f0dc9f7a8
--- a/userlibandfileserver/fileserver/sfat32/sl_scan32.cpp	Tue Jan 19 13:48:03 2010 +0000
+++ b/userlibandfileserver/fileserver/sfat32/sl_scan32.cpp	Mon Jan 18 21:31:10 2010 +0200
@@ -21,37 +21,38 @@
  @internalTechnology
 */
 
-//#define DEBUG_SCANDRIVE
-
 #include "sl_std.h"
 #include "sl_scandrv.h"
 
+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
+
 
 
-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
-
+/**
+    CScanDrive factory method
+    @param  aMount the owning mount
+*/
+CScanDrive* CScanDrive::NewL(CFatMountCB* aMount)
+	{
+	if(!aMount)
+		{
+        ASSERT(0);
+        User::Leave(KErrArgument);
+        }
 
-/**
-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;
+	
+    return self;
 	}
 
 
 CScanDrive::~CScanDrive()
 	{
-	for(TInt i=0;i<KMaxArrayDepth && iClusterListArray[i]!=NULL;++i)
+	for(TUint i=0; i<KMaxArrayDepth && iClusterListArray[i]!=NULL; ++i)
 		{
 		iClusterListArray[i]->Close();
 		delete iClusterListArray[i];
@@ -63,24 +64,28 @@
 	}
 
 /**
-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
+    Creates the structure of this class.
+    @param aMount The owning mount
 */
 void CScanDrive::ConstructL(CFatMountCB* aMount)
     {
-	iMount=aMount;
+    ASSERT(aMount);
+
+    //--- setting up 
+    iMount=aMount;
+    iGenericError = ENoErrors;
+    iDirError     = ENoDirError;  
+    iMaxClusters  = iMount->UsableClusters()+KFatFirstSearchCluster; //-- UsableClusters() doesn't count first 2 unused clusers
+    //------------------------------
 	
-    //-- 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
-    
+    //-- 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 = MaxClusters();
+
     CleanupClosePushL(iMediaFatBits);
     CleanupClosePushL(iScanFatBits);
 
     iMediaFatBits.CreateL(KClustersNum);
-    iScanFatBits.CreateL(KClustersNum);;
+    iScanFatBits.CreateL(KClustersNum);
 
     CleanupStack::Pop(&iScanFatBits);
     CleanupStack::Pop(&iMediaFatBits);
@@ -88,18 +93,18 @@
 
 //----------------------------------------------------------------------------------------------------
 /**
-    FAT type-agnnostic parser. Reads whole FAT and sets up a bit vector.
+    FAT type-agnostic 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;
-    
-
+    const TInt KMaxClusters = MaxClusters();
 
     iMediaFatBits.Fill(0);
 
-    for(TInt i=KFatFirstSearchCluster; i<MaxClusters; ++i)
+    __PRINT1(_L("CScanDrive::DoParseFatL(), clusters:%d"), KMaxClusters);
+
+    for(TInt i=KFatFirstSearchCluster; i<KMaxClusters; ++i)
 	    {
         const TUint32 nFatEntry = ReadFatL(i);
        
@@ -143,9 +148,12 @@
 */
 void CScanDrive::DoParseFat32L()
     {
-    ASSERT(iMount->FatType() == EFat32);
+    const TInt    KNumClusters = MaxClusters();
+    
+    __PRINT1(_L("CScanDrive::DoParseFat32L(), clusters:%d"), KNumClusters);
 
-    const TUint32 KNumClusters  = iMount->UsableClusters()+KFatFirstSearchCluster;
+    ASSERT(iMount->FatType() == EFat32);
+    
     const TUint32 KFat1StartPos = iMount->StartOfFatInBytes();
     const TUint32 KFatSize      = KNumClusters * sizeof(TFat32Entry); //-- usable size of one FAT.
 
@@ -183,6 +191,8 @@
     CleanupStack::PopAndDestroy(&buf); 
     }
 
+
+
 //----------------------------------------------------------------------------------------------------
 /**
     Sets up a bit list representation of the media fat
@@ -192,63 +202,23 @@
     {
     ASSERT(iMount->ConsistentState());
     
+    TInt nRes;
+
     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
+    @return True if a directory error has been found
 */
 TBool CScanDrive::IsDirError() const
 	{
@@ -256,77 +226,103 @@
 	}
 
 /**
-    After StartL() and finishing allows us to know if there were any problems at all.
+    After StartL() and finishing allows us to know if there were any problems discovered at all.
     The client may wish to remount the filesystem if there were errors.
 
-    @return EFalse if there were no problems in FS.
+    @return The code describing the problem.
 */
-TBool CScanDrive::ProblemsDiscovered() const
+CScanDrive::TGenericError CScanDrive::ProblemsDiscovered() const
 {
-    return IsDirError() || iFoundProblems;
+
+    if(IsDirError()) 
+        return EScanDriveDirError;
+    else
+        return iGenericError;
 }
 
 /**
     Sets the flag indicating than there are errors in filesystem structure
     See ProblemsDiscovered()
+
+    @param  aError a code describing the error
 */
-void CScanDrive::IndicateErrorsFound()
+void CScanDrive::IndicateErrorsFound(TGenericError aError)
 {
-    iFoundProblems = ETrue;
+    ASSERT(aError != ENoErrors);
+    iGenericError = aError;
 }
 
 
-
+//----------------------------------------------------------------------------------------------------
 /**
-Start point for scan drive also fixes up errors 
-
-@return The result of the scan
-@leave 
+    Start the scanner. The this calss description about what it actually does.
+    @param  aMode specifies the operational mode.
 */
-TInt CScanDrive::StartL()
+void CScanDrive::StartL(TScanDriveMode aMode)
 	{
-	__PRINT1(_L("CScanDrive::StartL(), drive:%d"), iMount->DriveNumber());
+	__PRINT2(_L("CScanDrive::StartL(%d), drive:%d"), aMode, iMount->DriveNumber());
+    iScanDriveMode = aMode;
 
     //-- used for measuring time
     TTime   timeStart;
     TTime   timeEnd;
+    
     timeStart.UniversalTime(); //-- take start time
 
-
 	ReadMediaFatL();
 
+        //timeEnd.UniversalTime(); //-- take end time
+        //elapsedTime = (TInt)( (timeEnd.MicroSecondsFrom(timeStart)).Int64() / K1mSec);
+        //__PRINT1(_L("#@@@ CScanDrive #1:%d ms "), elapsedTime);
+
 	CheckDirStructureL();
-#if defined(DEBUG_SCANDRIVE)
-	CompareFatsL();
-#endif
-	if(IsDirError())
-		FixupDirErrorL();
+
+    //-- uncomments a line below if you need to compare real and restored FAT tables and print out all differences
+    //CompareFatsL(EFalse);
+
+        //timeEnd.UniversalTime(); //-- take end time
+        //elapsedTime = (TInt)( (timeEnd.MicroSecondsFrom(timeStart)).Int64() / K1mSec);
+        //__PRINT1(_L("#@@@ CScanDrive #2:%d ms "), elapsedTime);
 
-	WriteNewFatsL();
-#if defined(DEBUG_SCANDRIVE)
+    if(CheckDiskMode())
+        {//-- in check disk mode it is nesessarily just to detech FS errors
+        CompareFatsL(ETrue); //-- will stop on the first error found
+        }
+    else
+        {//-- In ScanDrive mode we need to find and fix Rugged FAT artefacts.
+     
+        if(IsDirError())
+		    FixupDirErrorL();
+
+	    CompareAndFixFatsL();
+        }
+
 	PrintErrors();
-#endif
-   
 
 
     timeEnd.UniversalTime(); //-- take end time
-    const TInt msScanTime = (TInt)( (timeEnd.MicroSecondsFrom(timeStart)).Int64() / K1mSec);
-    (void)msScanTime;
-    
+    const TInt elapsedTime = (TInt)( (timeEnd.MicroSecondsFrom(timeStart)).Int64() / K1mSec);
+    (void)elapsedTime;
+
     __PRINT1(_L("CScanDrive: Directories visisted = %d\n"),iDirsChecked);
-    __PRINT1(_L("#@@@ CScanDrive time taken:%d ms "), msScanTime);
+    __PRINT1(_L("#@@@ CScanDrive time taken:%d ms "), elapsedTime);
+    
+    
 
-	return KErrNone;
+	return;
 	}
 
+//----------------------------------------------------------------------------------------------------
 /**
-Fix errors detected by the drive scan
+    Fix errors detected by the drive scan
  
-@leave System wide error code
+    @leave System wide error code
 */
 void CScanDrive::FixupDirErrorL()
 	{
-	if(!IsDirError())
+	ASSERT(!CheckDiskMode());
+    
+    if(!IsDirError())
 		return;
 	
     if(iDirError==EScanMatchingEntry)
@@ -339,21 +335,23 @@
         FixPartEntryL();
         }
 
-    IndicateErrorsFound(); //-- indicate that we have found errors
+    IndicateErrorsFound(EScanDriveDirError); //-- 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. 
+    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
+    @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)
+	
+    for(TUint i=0;i<KMaxArrayDepth && iClusterListArray[i]!=NULL;++i)
 		{
 		RArray<TInt>* clusterList=iClusterListArray[i];
 		for(TInt j=0;j<clusterList->Count();++j)
@@ -364,15 +362,17 @@
 				return;
 			}
 		}
+
 	__ASSERT_ALWAYS(err==KErrNone,User::Leave(KErrNotFound));
 	}
 
+//----------------------------------------------------------------------------------------------------
 /**
-Scan through directory structure looking for start cluster found in iMatching
+    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 
+    @param aDirCluster Start cluster for scan to start
+    @return System wide error value
+    @leave 
 */
 TInt CScanDrive::FindStartClusterL(TInt aDirCluster)
 	{
@@ -402,32 +402,36 @@
 		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
+    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 
+    @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)
+	if((TUint)iMount->StartCluster(aEntry)==iMatching.iStartCluster)
 		{
 		TBool complete=AddMatchingEntryL(aEntryPos);
 		if(complete)
@@ -435,14 +439,16 @@
 		}
 	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.
+    Checks directory structure for errors, can be considered the start point of the scan.  
+    Handles recursion depth to avoid stack overflow.
 
-@leave System wide error code
+    @leave System wide error code
 */
 void CScanDrive::CheckDirStructureL()
 	{
@@ -450,7 +456,8 @@
 	// 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)
+
+	for(TUint i=0;i<KMaxArrayDepth && iClusterListArray[i]!=NULL;++i)
 		{
 		RArray<TInt>* clusterList=iClusterListArray[i];
 		++iListArrayIndex;
@@ -463,17 +470,18 @@
 	}
 
 
+//----------------------------------------------------------------------------------------------------
 /**
-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. 
+    This 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
+    @param aCluster Directory cluster to start checking
+    @leave System wide error codes
 */
-void CScanDrive::CheckDirL(TInt aCluster)
+void CScanDrive::CheckDirL(TUint32 aCluster)
 	{
 	__PRINT1(_L("CScanDrive::CheckDirL aCluster=%d"),aCluster);
-	__ASSERT_ALWAYS(aCluster>=0,User::Leave(KErrCorrupt));
+
 	// check depth of recursion
 	if(++iRecursiveDepth==KMaxScanDepth)
 		{
@@ -481,9 +489,9 @@
 		--iRecursiveDepth;
 		return;
 		}
-#if defined(DEBUG_SCANDRIVE)
+
 	++iDirsChecked;
-#endif
+
 	TEntryPos entryPos(aCluster,0);
 	TInt dirEntries=0;
 	FOREVER
@@ -492,31 +500,46 @@
 		ReadDirEntryL(entryPos,entry);
 		if(!iMount->IsEndOfClusterCh(entryPos.iCluster))
 			++dirEntries;
-		if(entry.IsParentDirectory()||entry.IsCurrentDirectory()||entry.IsErased())
+
+		if(entry.IsParentDirectory() || entry.IsCurrentDirectory() || entry.IsErased())
 			{
 			if(IsEndOfRootDir(entryPos))
 				break;
+
 			MoveToNextEntryL(entryPos);
 			continue;
 			}
+
 		if(entry.IsEndOfDirectory())
 			{
 			if(aCluster)	
-				WriteClusterChainL(aCluster,dirEntries<<KSizeOfFatDirEntryLog2);
+				RecordClusterChainL(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; 
+		
+		const TBool isComplete = MoveToVFatEndL(entryPos,entry,dirEntries);
+        
+        if(!isComplete && CheckDiskMode())
+            {//-- broken VFAT entryset; in CheckDisk mode this is the FS error, abort further activity
+                IndicateErrorsFound(EInvalidEntrySize);
+                User::Leave(KErrCorrupt);
+            }
+
+        // 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())
+		
+        //-- this code seems to deal with one of the Rugged FAT artefacts: partially deleted VFAT entryset, when DOS entry is deleted first
+        //-- and delettion of VFAT ones had failed
+        if(!isComplete && origEntry.IsVFatEntry())
 			{
 			AddPartialVFatL(origPos,origEntry);
-			if(entryPos.iCluster!=KEndOfDirectory)
+			if(!IsEofF(entryPos.iCluster))
 				{
 				TInt toMove=origEntry.NumFollowing()-(dirEntries-origDirEntries);
 				if(toMove)
@@ -530,22 +553,26 @@
 				}
 			}
 		else
-			ProcessEntryL(entry);
+			{
+            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.
+    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
+    @param aEntry Directory entry to check
+    @leave System wide error code
 */
 void CScanDrive::ProcessEntryL(const TFatDirEntry& aEntry)
 	{
@@ -553,82 +580,116 @@
 	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());
+	
+    if(!(entryAtt&(KEntryAttDir|KEntryAttVolume)) && iMount->StartCluster(aEntry)>0)
+		{//-- this is a file with length >0. Check that its cluster chain corresponds to its size
+        RecordClusterChainL(iMount->StartCluster(aEntry),(TUint) aEntry.Size());
+        }
 	else if(entryAtt&KEntryAttDir)
-		CheckDirL(iMount->StartCluster(aEntry));
+		{//-- this is the directory, walk into it
+        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
+    Walks 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
+    @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
-//
+void CScanDrive::RecordClusterChainL(TInt aCluster, TUint aSizeInBytes)
 	{
-	__PRINT1(_L("CScanDrive::WriteClusterChainL starting at %d"),aCluster);
-	__ASSERT_ALWAYS(aCluster>0,User::Leave(KErrCorrupt));
-	TInt clusterCount;
-	if(aSizeInBytes==0)
+	__PRINT2(_L("CScanDrive::RecordClusterChainL() cl:%d, sz:%d") ,aCluster, aSizeInBytes);
+	__ASSERT_ALWAYS(aCluster>0, User::Leave(KErrCorrupt));
+	
+    TUint clusterCount;
+	
+    if(aSizeInBytes==0)
 		clusterCount=1;
 	else
-		clusterCount = (TInt) (( TInt64(aSizeInBytes) + TInt64((1<<iMount->ClusterSizeLog2())-1) ) >> iMount->ClusterSizeLog2());
+		{
+        const TUint64 tmp = aSizeInBytes + Pow2_64(iMount->ClusterSizeLog2()) - 1;
+        clusterCount = (TUint) (tmp >> iMount->ClusterSizeLog2());
+        }
+
 	TInt startCluster=aCluster;
 	while(clusterCount)
 		{
-		if(AlreadyUsedL(aCluster))
-			{
-			__ASSERT_ALWAYS(!IsDirError()&&iMatching.iStartCluster==0&&aCluster==startCluster,User::Leave(KErrCorrupt));
+        if(IsClusterUsedL(aCluster))
+			{//-- this cluster already seems to belong to some other object; crosslinked cluster chain. Can't fix it.
+			if(CheckDiskMode())
+                {//-- in check disk mode this is a FS error; Indicate error and abort furter scanning
+                __PRINT1(_L("CScanDrive::RecordClusterChainL #1 %d"),aCluster); 
+                IndicateErrorsFound(EClusterAlreadyInUse);
+                User::Leave(KErrCorrupt);
+                }
+            
+            __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
+            IndicateErrorsFound(EScanDriveDirError); //-- indicate that we have found errors
 			return;
 			}
-		if(clusterCount==1)
+
+		
+        if(clusterCount==1)
 			{
 			if(!iMount->IsEndOfClusterCh(ReadFatL(aCluster)))
-				{
-				//This is a genuine truncation
+				{//-- seems to be a rugged FAT artefact; File truncation had failed before and now file length is less than
+                 //-- the corresponding cluster chain shall be. It will be truncated.
 				iTruncationCluster = aCluster;								
+                
+                if(CheckDiskMode())
+                    {//-- in check disk mode this is a FS error; Indicate error and abort furter scanning
+                    __PRINT1(_L("CScanDrive::RecordClusterChainL #2 %d"),aCluster); 
+                    IndicateErrorsFound(EInvalidEntrySize);
+                    User::Leave(KErrCorrupt);
+                    }
                 }
-			SetUsedL(aCluster);
+
+            //__PRINT1(_L("#--: %d -> EOC"), aCluster); 
+            MarkClusterUsedL(aCluster);
 			return;
 			}
 		else
 			{
-			TInt clusterVal=ReadFatL(aCluster);
-			__ASSERT_ALWAYS(!IsEofF(clusterVal) && clusterVal!=0,User::Leave(KErrCorrupt));
-			SetUsedL(aCluster);
+			const TUint clusterVal=ReadFatL(aCluster);
+
+            //__PRINT2(_L("#--: %d -> %d"), aCluster, clusterVal); 
+			
+            __ASSERT_ALWAYS(!IsEofF(clusterVal) && clusterVal !=KSpareCluster, User::Leave(KErrCorrupt));
+			MarkClusterUsedL(aCluster);
 			aCluster=clusterVal;
 			--clusterCount;
 			}
-		}
+		
+        }//while(clusterCount)
 	}
 
+//----------------------------------------------------------------------------------------------------
 /**
-Move to dos entry, checking all vfat entry ID numbers are in sequence.
-Assumes aEntry is not erased
+    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
+    @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));
+	__ASSERT_ALWAYS(toFollow>0 && !aEntry.IsErased(), User::Leave(KErrCorrupt));
+
 	FOREVER
 		{
 		MoveToNextEntryL(aPos);
@@ -637,31 +698,36 @@
 		--toFollow;
 		if(!toFollow)
 			break;
-		if(!IsValidVFatEntry(aEntry,toFollow))
+		
+        if(!IsValidVFatEntry(aEntry,toFollow))
 			return(EFalse);
 		}
-	return(IsDosEntry(aEntry));
+	
+    return(IsDosEntry(aEntry));
 	}
 
+//----------------------------------------------------------------------------------------------------
 /**
-Check if an entry is valid VFat
+    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
+    @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 EFalse;
+
 	return(aEntry.NumFollowing()==aPrevNum);
 	}
 
+//----------------------------------------------------------------------------------------------------
 /**
-Check if an entry is a Dos entry
+    Check if an entry is a Dos entry
 
-@param aEntry Entry to check
-@return ETrue if aEntry is a dos entry
+    @param aEntry Entry to check
+    @return ETrue if aEntry is a dos entry
 */
 TBool CScanDrive::IsDosEntry(const TFatDirEntry& aEntry)const
 	{
@@ -669,13 +735,13 @@
 	return res;
 	} 
 
+//----------------------------------------------------------------------------------------------------
 /**
-Add partial entry to iPartEntry under the error condition of not all Vfat entries 
-being present
+    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
+    @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)
 	{
@@ -686,12 +752,13 @@
 	iDirError=EScanPartEntry;
 	}
 
+//----------------------------------------------------------------------------------------------------
 /**
-Add entry position to iMatching
+    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 
+    @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)
 	{
@@ -702,15 +769,18 @@
 	}
 
 
+//----------------------------------------------------------------------------------------------------
 /**
-Scan for differnces in the new and old FAT table writing them to media if discovered
+    Scan for differnces in the new and old FAT table writing them to media if discovered
+    It is supposed to be called in 'ScanDrive' mode only
 
-@leave System wide error codes
+    @leave System wide error codes
 */
-void CScanDrive::WriteNewFatsL()
+void CScanDrive::CompareAndFixFatsL()
     {
-	
-    __PRINT1(_L("CScanDrive::WriteNewFatsL() drv:%d"),iMount->DriveNumber());
+    __PRINT1(_L("CScanDrive::CompareAndFixFatsL() drv:%d"),iMount->DriveNumber());
+
+    ASSERT(!CheckDiskMode());
 
     TUint32 nClustersFixed = 0; //-- fixed clusters count
     TUint32 nBadClusters   = 0; //-- bad cluster count
@@ -732,7 +802,7 @@
                 {//-- 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(); 
+                IndicateErrorsFound(EScanDriveDirError); 
                 
                 //-- skip BAD cluster, can't mark it as unused.
                 if(iMount->IsBadCluster(ReadFatL(i)))
@@ -790,7 +860,7 @@
 		iMount->FAT().FlushL();
 		
         //-- indicate that there are some problems in FAT. and we probably wrote something there.
-        IndicateErrorsFound(); //-- indicate that we have found errors
+        IndicateErrorsFound(EScanDriveDirError); //-- indicate that we have found errors
 
         ++nClustersFixed;
         }
@@ -798,14 +868,15 @@
     __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.
+    Read the "Rugged FAT" 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 
+    @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)
 	{
@@ -822,47 +893,54 @@
 	return(entry.RuggedFatEntryId());
 	}
 
+//----------------------------------------------------------------------------------------------------
 /**
-Erase part entry found in iPartEntry
-
-@leave System wide error code
+    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
+	ASSERT(!CheckDiskMode());
+    iMount->EraseDirEntryL(iPartEntry.iEntryPos,iPartEntry.iEntry);
+    IndicateErrorsFound(EScanDriveDirError); //-- indicate that we have found errors
 	}
 	
+//----------------------------------------------------------------------------------------------------
 /**
-Delete entry with largest value in the reserved2 section(bytes 20 and 21) of dos entry
+    Delete entry with largest value in the reserved2 section(bytes 20 and 21) of dos entry
 
-@leave System wide error code
+    @leave System wide error code
 */
 void CScanDrive::FixMatchingEntryL()
 	{
-	__PRINT1(_L("CScanDrive::FixMatchingEntryL() start cluster=%d"),iMatching.iStartCluster);
+	
+    __PRINT1(_L("CScanDrive::FixMatchingEntryL() start cluster=%d"),iMatching.iStartCluster);
 	__ASSERT_ALWAYS(iMatching.iCount==KMaxMatchingEntries,User::Leave(KErrCorrupt));
-	TInt idOne=GetReservedidL(iMatching.iEntries[0]);
+	ASSERT(!CheckDiskMode());
+
+    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
+    IndicateErrorsFound(EScanDriveDirError); //-- 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
+//----------------------------------------------------------------------------------------------------
+/**
+    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)
+	while(aToMove-- && !IsEofF(aEntryPos.iCluster))
 		{
 		MoveToNextEntryL(aEntryPos);
 		++aDirEntries;
@@ -870,33 +948,38 @@
 	ReadDirEntryL(aEntryPos,aEntry);
 	}
 
+//----------------------------------------------------------------------------------------------------
 /**
-Adds aCluster to cluster list array so that it may be revisited later, avoids stack 
-over flow
+    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
+    @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
+    Used in "CheckDisk" mode mostly. Compares first FAT table on the media with the FAT bitmap restored by walking the directory structure.
+    Displays any differences and records an error if found.
+    
+    @param  aStopOnFirstErrorFound if ETrue will stop after discovering first error (FATs discrepancy)
+    
+    @leave  System wide error codes
 */
-void CScanDrive::CompareFatsL() const 
+void CScanDrive::CompareFatsL(TBool aStopOnFirstErrorFound)  
     {
-	__PRINT(_L("CScanDrive::CompareFatsL()"));
+	__PRINT1(_L("CScanDrive::CompareFatsL(%d)"), aStopOnFirstErrorFound);
 		
    
     TUint32 diffPos;
@@ -904,34 +987,51 @@
         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();
+    const TUint clusters = iMount->UsableClusters();
     ASSERT(diffPos < (TUint32)clusters);
                         
-    TInt scanusedcnt=0;
-	TInt mediausedcnt=0;
+    TUint scanusedcnt=0;
+	TUint mediausedcnt=0;
 	
-    for(TInt i=diffPos; i<clusters; ++i)
+    for(TUint 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);
+		    {//-- mismatch between FAT on the media and the FAT bitmap restored by walking directory structure
+
+			    if(bRealFatEntry)
+                {//-- FAT[i] on the media is marked as occupied, but retored FAT bitmap shows that it is free
+                    if(iMount->IsBadCluster(ReadFatL(i)))
+                        continue; //-- this is a BAD cluster it can't be occupied by the FS object, OK.
+
+                    __PRINT2(_L("FAT[%d] = %d\n"), i, ReadFatL(i));        
+                    __PRINT1(_L("iTruncationCluster = %d\n"), iTruncationCluster);        
+                    
+                    //-- this is a lost cluster
+                    if(!IsEofF(ReadFatL(i)) && (i==iTruncationCluster))
+                        {//-- seems to be a Rugged FAT ertefact
+                        __PRINT1(_L("Hanging cluster = %d\n"),i);        
+                        }
+                    else
+                        {
+                        __PRINT1(_L("Lost cluster=%d\n"),i);
+                        }
+                    
+                    
+                    IndicateErrorsFound(EBadClusterValue);
                 }
-			else if((bRealFatEntry && !IsEofF(ReadFatL(i))) && (i==iTruncationCluster))
-			    {
-            	__PRINT1(_L("Hanging cluster = %d\n"),i);
+                else
+                {//-- FAT[i] on the media is marked as free, but retored FAT bitmap shows that it is occupied by some object
+                    IndicateErrorsFound(EClusterAlreadyInUse);
+                    __PRINT1(_L("Unflushed cluster = %d\n"),i);
                 }
-			else if(!bRealFatEntry && bNewFatEntry)
-                {
-				__PRINT1(_L("Unflushed cluster = %d\n"),i);
-                }
-			else
-				User::Leave(KErrCorrupt);
-		    }
+
+             if(aStopOnFirstErrorFound)
+                 break; //-- not asked to check for errors further
+
+            }
 		
         if(bRealFatEntry)
 			mediausedcnt++;
@@ -943,11 +1043,13 @@
     __PRINT2(_L("Scan Fat Used=%d, Media Fat Used=%d \n"),scanusedcnt,mediausedcnt);
     }	
 
+//----------------------------------------------------------------------------------------------------
 /**
-For debug purposes, print errors found as debug output
+    For debug purposes, print errors found as debug output
 */
 void CScanDrive::PrintErrors()
     {
+#if defined(_DEBUG)    
 	__PRINT1(_L("Directories visisted = %d\n"),iDirsChecked);
 
 	if(iDirError==EScanPartEntry)
@@ -960,8 +1062,10 @@
 		__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
     }
-#endif
+
+
 
 /**
 Read a FAT directory entry from disk, either reads directly from the main cache or
@@ -973,7 +1077,7 @@
 */
 void CScanDrive::ReadDirEntryL(const TEntryPos& aPos,TFatDirEntry& aDirEntry)
     {
-	__PRINT(_L("CScanDrive::ReadDirEntryL"));
+	//__PRINT(_L("CScanDrive::ReadDirEntryL"));
 	if (iMount->IsEndOfClusterCh(aPos.iCluster))
 		{
 		Mem::FillZ(&aDirEntry,sizeof(TFatDirEntry));
@@ -985,12 +1089,10 @@
 
 
 /**
-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.
+    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
-
+    @param aPos Current directory position up dated to position of next entry.
 */
 void CScanDrive::MoveToNextEntryL(TEntryPos& aPos)
 	{
@@ -999,16 +1101,86 @@
 	}	
 
 /**
-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
+    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
+    @return Value of cluster read from Fat
+*/
+TUint32 CScanDrive::ReadFatL(TUint aClusterNum) 
+	{
+	if(aClusterNum < KFatFirstSearchCluster || aClusterNum >= MaxClusters())
+        {
+        IndicateErrorsFound(EBadClusterNumber);
+        User::Leave(KErrCorrupt);
+        }
+
+    //-- actually, ReadL() can leave with some error code, that won't be reflected in IndicateErrorsFound().
+    //-- it's possible to improve but is it worth it?
+    return iMount->FAT().ReadL(aClusterNum);			
+    }
+
+
+/**
+    Set a cluster as visited in the bit packed scan Fat
+    @param aCluster Cluster number
+*/
+void CScanDrive::MarkClusterUsedL(TUint aClusterNum)
+	{
+	if(aClusterNum < KFatFirstSearchCluster || aClusterNum >= MaxClusters())
+        {
+        IndicateErrorsFound(EBadClusterNumber);
+        User::Leave(KErrCorrupt);
+        }
+
+    iScanFatBits.SetBit(aClusterNum);
+	}
+
 
-@param aClusterNum Cluster to read
-@leave System wide error code
-@return Value of cluster read from Fat
+/**
+    Query whether a cluster is already set as used 
+    @param aCluster Cluster to query
+*/
+TBool CScanDrive::IsClusterUsedL(TUint aClusterNum) 
+	{
+	if(aClusterNum < KFatFirstSearchCluster || aClusterNum >= MaxClusters())
+        {
+        IndicateErrorsFound(EBadClusterNumber);
+        User::Leave(KErrCorrupt);
+        }
+
+    return iScanFatBits[aClusterNum];
+	}
+
+/**
+    @param aPos Position in a directory cluster
+    @return  ETrue if aPos is the last entry in the root directory
 */
-TUint32 CScanDrive::ReadFatL(TInt aClusterNum) const
+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->FAT().ReadL(aClusterNum);			
+    return iMount->IsEndOfClusterCh(aVal);
+	}
+
+/** @return max. number of clusters on the volume being scanned */
+TUint32 CScanDrive::MaxClusters() const
+    {
+        ASSERT(iMaxClusters);
+        return iMaxClusters;
+    }
+
+/** @return ETrue in we are operating in "CheckDisk" mode*/
+TBool CScanDrive::CheckDiskMode() const 
+    {
+    return iScanDriveMode == ECheckDisk;
     }
 
 
@@ -1019,5 +1191,3 @@
 
 
 
-
-