userlibandfileserver/fileserver/sfat32/sl_scan32.cpp
changeset 189 a5496987b1da
parent 90 947f0dc9f7a8
child 291 206a6eaaeb71
--- a/userlibandfileserver/fileserver/sfat32/sl_scan32.cpp	Wed Jun 23 12:58:21 2010 +0100
+++ b/userlibandfileserver/fileserver/sfat32/sl_scan32.cpp	Thu Jul 01 17:57:33 2010 +0100
@@ -72,10 +72,11 @@
     ASSERT(aMount);
 
     //--- setting up 
-    iMount=aMount;
-    iGenericError = ENoErrors;
-    iDirError     = ENoDirError;  
-    iMaxClusters  = iMount->UsableClusters()+KFatFirstSearchCluster; //-- UsableClusters() doesn't count first 2 unused clusers
+	iMount			 = aMount;
+	iGenericError	 = ENoErrors;
+	iDirError		 = ENoDirError;
+	iHangingClusters = 0;
+	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.
@@ -94,7 +95,7 @@
 //----------------------------------------------------------------------------------------------------
 /**
     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.
+    For FAT12/16 it's OK, because the FAT12/16 is fully cached.
 */
 void CScanDrive::DoParseFatL()
     {
@@ -109,7 +110,7 @@
         const TUint32 nFatEntry = ReadFatL(i);
        
         //-- each '1' bit represents a used cluster 
-        if(nFatEntry != KSpareCluster) 
+        if(nFatEntry != KSpareCluster)
             iMediaFatBits.SetBit(i);
 	    }
     }
@@ -125,7 +126,7 @@
     ASSERT((aBuf.Size() & (sizeof(TFat32Entry)-1)) == 0);
     
     const TInt KNumEntries = aBuf.Size() >> KFat32EntrySzLog2;
-    const TFat32Entry* const pFatEntry = (const TFat32Entry*)(aBuf.Ptr()); 
+    const TFat32Entry* const pFatEntry = (const TFat32Entry*)(aBuf.Ptr());
 
     for(TInt i=0; i<KNumEntries; ++i)
         {
@@ -161,11 +162,11 @@
 
     iMediaFatBits.Fill(0);
 
-    RBuf8 buf;
-    CleanupClosePushL(buf);
+    RBuf8 fatParseBuf;
+    CleanupClosePushL(fatParseBuf);
 
     //-- allocate memory for FAT parse buffer
-    buf.CreateMaxL(KFatBufSz);
+    fatParseBuf.CreateMaxL(KFatBufSz);
 
     //-- read FAT directly from the media into the large buffer and parse it
     TUint32 rem = KFatSize;
@@ -175,10 +176,10 @@
     while(rem)
         {
         const TUint32 bytesToRead=Min(rem, KFatBufSz);
-        TPtrC8 ptrData(buf.Ptr(), bytesToRead);
+        TPtrC8 ptrData(fatParseBuf.Ptr(), bytesToRead);
 
         //-- read portion of the FAT into buffer
-        User::LeaveIfError(iMount->LocalDrive()->Read(mediaPos, bytesToRead, buf)); 
+        User::LeaveIfError(iMount->LocalDrive()->Read(mediaPos, bytesToRead, fatParseBuf)); 
 
         //-- parse the buffer and populate bit vector
         DoParseFat32Buf(ptrData, currFatEntry);
@@ -187,8 +188,8 @@
         rem -= bytesToRead;
         }
 
-    buf.Close();
-    CleanupStack::PopAndDestroy(&buf); 
+    fatParseBuf.Close();
+    CleanupStack::PopAndDestroy(&fatParseBuf); 
     }
 
 
@@ -241,7 +242,7 @@
 }
 
 /**
-    Sets the flag indicating than there are errors in filesystem structure
+    Sets the flag indicating that there are errors in filesystem structure
     See ProblemsDiscovered()
 
     @param  aError a code describing the error
@@ -255,8 +256,9 @@
 
 //----------------------------------------------------------------------------------------------------
 /**
-    Start the scanner. The this calss description about what it actually does.
-    @param  aMode specifies the operational mode.
+    Starts the scanner.
+    
+    @param	aMode	Specifies the operational mode.
 */
 void CScanDrive::StartL(TScanDriveMode aMode)
 	{
@@ -299,7 +301,6 @@
 
 	PrintErrors();
 
-
     timeEnd.UniversalTime(); //-- take end time
     const TInt elapsedTime = (TInt)( (timeEnd.MicroSecondsFrom(timeStart)).Int64() / K1mSec);
     (void)elapsedTime;
@@ -477,7 +478,7 @@
 	{
 	CheckDirL(iMount->RootIndicator());
 	// Due to recursive nature of CheckDirL when a depth of
-	// KMaxScanDepth is reached clusters are stored in a list
+	// KMaxScanDepth is reached, clusters are stored in a list
 	// and passed into CheckDirL afresh
 
 	for(TUint i=0;i<KMaxArrayDepth && iClusterListArray[i]!=NULL;++i)
@@ -517,7 +518,7 @@
 
 	TEntryPos entryPos(aCluster,0);
 	TInt dirEntries=0;
-	FOREVER
+	for(;;)
 		{
 		TFatDirEntry entry;
 		ReadDirEntryL(entryPos,entry);
@@ -659,11 +660,11 @@
 		{
         if(IsClusterUsedL(aCluster))
 			{//-- this cluster already seems to belong to some other object; crosslinked cluster chain. Can't fix it.
-                __PRINT1(_L("CScanDrive::RecordClusterChainL #1 %d"),aCluster); 
+            __PRINT1(_L("CScanDrive::RecordClusterChainL #1 %d"),aCluster); 
             
             if(CheckDiskMode())
-                {//-- in check disk mode this is a FS error; Indicate error and abort furter scanning
-                __PRINT(_L("CScanDrive::RecordClusterChainL #1.1")); 
+                {//-- in check disk mode this is an FS error; Indicate error and abort further scanning
+                __PRINT(_L("CScanDrive::RecordClusterChainL #1.1"));
                 IndicateErrorsFound(EClusterAlreadyInUse);
                 User::Leave(KErrCorrupt);
                 }
@@ -686,16 +687,26 @@
         if(clusterCount==1)
 			{//-- we have reached the end of the cluster chain
 			if(!iMount->IsEndOfClusterCh(ReadFatL(aCluster)))
-				{//-- seems to be a rugged FAT artefact; File truncation/extension had failed before and now file length is less than
-                 //-- the corresponding cluster chain shall be. It will be truncated to the size recorded in file DOS entry.
-				iTruncationCluster = aCluster;								
+				{
+				// According to the directory entry, we have reached the end of the cluster chain,
+				// whereas in the media FAT, it is not.
+				// This is a rugged FAT artefact; hanging cluster chain:
+				// 	A cluster chain which is longer in the FAT table than is recorded in the corresponding directory entry 
+				// 	or not terminated by an EOC entry in FAT.
+				// This is caused by:
+				//  - File truncation failing.
+				//	- OR file expanding failing during flushing to the media FAT.
                 
                 if(CheckDiskMode())
-                    {//-- in check disk mode this is a FS error; Indicate error and abort furter scanning
-                    __PRINT1(_L("CScanDrive::RecordClusterChainL #2 %d"),aCluster); 
+                    {//-- in check disk mode this is an FS error; Indicate error and abort further scanning
+                    __PRINT1(_L("CScanDrive::RecordClusterChainL #2 Hanging cluster=%d"),aCluster);
                     IndicateErrorsFound(EInvalidEntrySize);
                     User::Leave(KErrCorrupt);
                     }
+				
+				// The chain will be truncated to the size recorded in the file's DOS entry and
+				// the remaining lost cluster chain will be fixed later in CompareAndFixFatsL().
+				FixHangingClusterChainL(aCluster);
                 }
 
             //__PRINT1(_L("#--: %d -> EOC"), aCluster); 
@@ -762,8 +773,8 @@
         if(!IsValidVFatEntry(aEntry,toFollow))
 			return(EFalse);
 		}
-	
-    return(IsDosEntry(aEntry));
+	// A sequence of VFat entries must end with a Dos entry to be valid.
+	return(IsDosEntry(aEntry));
 	}
 
 //----------------------------------------------------------------------------------------------------
@@ -844,7 +855,7 @@
 
 //----------------------------------------------------------------------------------------------------
 /**
-    Scan for differnces in the new and old FAT table writing them to media if discovered
+    Scan for differences 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
@@ -884,9 +895,10 @@
                     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. 
+                //-- 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
                     
@@ -901,7 +913,7 @@
                     const TUint32 fatSec = iMount->FAT().PosInBytes(i) >> KSectorSzLog2; 
 
                     if(fatSec != dirtyFatSector)
-                        {//-- we are going to write to a differrent media sector
+                        {//-- we are going to write to a different media sector
                         iMount->FAT().FlushL();
                         iMount->FAT().WriteL(i, KSpareCluster); //-- fix lost cluster
                         dirtyFatSector = fatSec;
@@ -927,18 +939,11 @@
     
     //------
 
-    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(EScanDriveDirError); //-- indicate that we have found errors
-
-        ++nClustersFixed;
-        }
+	
+	// Add the number of hanging clusters fixed by ScanDrive
+	nClustersFixed += iHangingClusters;
     
-    __PRINT2(_L("CScanDrive::WriteNewFatsL() fixed:%d, bad:%d"), nClustersFixed, nBadClusters);
+    __PRINT3(_L("CScanDrive::WriteNewFatsL() fixed clusters=%d,hanging clusters=%d,bad clusters=%d"),nClustersFixed,iHangingClusters,nBadClusters);
     }
 
 //----------------------------------------------------------------------------------------------------
@@ -946,7 +951,6 @@
     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 
@@ -1015,6 +1019,28 @@
 
 //----------------------------------------------------------------------------------------------------
 /**
+	Fix a hanging cluster chain.
+	Writes EOF to the corresponding FAT entry, making this cluster chain length correspond to the 
+	real file size recorded in the directory entry.
+	The remainder of the chain will be cleaned up later in CompareAndFixFatsL().
+	
+	@leave	System wide error code
+*/
+void CScanDrive::FixHangingClusterChainL(TUint32 aFatEofIndex)
+	{
+	__PRINT1(_L("CScanDrive::FixHangingClusterL() Hanging cluster=%d"), aFatEofIndex);
+	
+	iMount->FAT().WriteFatEntryEofL(aFatEofIndex);
+	iMount->FAT().FlushL();
+	iHangingClusters++;
+	
+	// Indicate that we have found an error
+	IndicateErrorsFound(EScanDriveDirError);
+	}
+
+
+//----------------------------------------------------------------------------------------------------
+/**
     Move past specified number of entries
 
     @param aEntryPos Start position to move from, updated as move takes place
@@ -1087,35 +1113,26 @@
 		if(BoolXOR(bRealFatEntry, bNewFatEntry))
 		    {//-- 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.
+			if(bRealFatEntry)
+				{//-- FAT[i] on the media is marked as occupied, but restored 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
-                {//-- 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);
-                }
+				__PRINT2(_L("FAT[%d] = %d\n"), i, ReadFatL(i));
+				
+				//-- this is a Rugged FAT artefact; a lost cluster
+				__PRINT1(_L("Lost cluster=%d\n"),i);
+				
+				IndicateErrorsFound(EBadClusterValue);
+				}
+			else
+				{//-- FAT[i] on the media is marked as free, but restored FAT bitmap shows that it is occupied by some object
+				IndicateErrorsFound(EClusterAlreadyInUse);
+				__PRINT1(_L("Unflushed cluster = %d\n"),i);
+				}
 
-             if(aStopOnFirstErrorFound)
-                 break; //-- not asked to check for errors further
+		 if(aStopOnFirstErrorFound)
+			 break; //-- not asked to check for errors further
 
             }
 		
@@ -1187,13 +1204,13 @@
 	}	
 
 /**
-    Read a cluster from the Media Fat if scan run in a seperate thread read from scan fat table
+    Read a cluster from the Media Fat if scan run in a separate 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) 
+TUint32 CScanDrive::ReadFatL(TUint aClusterNum)
 	{
 	if(aClusterNum < KFatFirstSearchCluster || aClusterNum >= MaxClusters())
         {