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