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