Update contrib.
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".
8 // Initial Contributors:
9 // Nokia Corporation - initial contribution.
14 // f32\sfat\sl_scan.cpp
15 // ScanDrive code, specific for EFAT.FSY
24 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
25 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
27 //!! WARNING!! DO NOT edit this file !! '\sfat' component is obsolete and is not being used. '\sfat32'replaces it
29 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
30 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
33 //#define DEBUG_SCANDRIVE
36 #include "sl_scandrv.h"
38 const TInt KEndOfDirectory = 0xFFFF; ///< End of directory marker
39 const TInt KMaxScanDepth = 20; ///< Maximum scan depth of to avoid stack over flow
40 const TInt KClusterListGranularity = 8; ///< Granularity of cluster list used for storage of clusters when KMaxScanDepth is reached
45 @param aMount The owning mount
47 CScanDrive* CScanDrive::NewL(CFatMountCB* aMount)
50 User::Leave(KErrArgument);
51 CScanDrive* self=new (ELeave) CScanDrive();
52 CleanupStack::PushL(self);
53 self->ConstructL(aMount);
59 CScanDrive::CScanDrive()
67 CScanDrive::~CScanDrive()
73 for(TInt i=0;i<KMaxArrayDepth && iClusterListArray[i]!=NULL;++i)
75 iClusterListArray[i]->Close();
76 delete iClusterListArray[i];
81 void CScanDrive::ConstructL(CFatMountCB* aMount)
83 // Create the new fat and initalise
87 iNewFat=CCheckFatTable::NewL(aMount);
88 iNewFat->InitializeL();
92 TBool CScanDrive::AlreadyExistsL(TInt aCluster)const
94 // Return ETrue if aCluster in the new fat contains a non-zero entry
97 return(iNewFat->ReadL(aCluster)!=0);
101 TBool CScanDrive::IsEndOfRootDir(const TEntryPos& aPos)const
103 // Return ETrue if aPos is the last entry in the root directory
106 return(iMount->IsRootDir(aPos)&&(iMount->StartOfRootDirInBytes()+aPos.iPos==(iMount->RootDirEnd()-KSizeOfFatDirEntry)));
110 @param aVal Value of the cluster to be tested
111 @return ETrue if aVal is the end of cluster marker
113 TBool CScanDrive::IsEofF(TInt aVal)const
115 return iMount->IsEndOfClusterCh(aVal);
119 @return True if a directory error has been found
121 TBool CScanDrive::IsDirError()const
123 return(iDirError!=0);
129 After StartL() and finishing allows us to know if there were any problems at all.
130 The client may wish to remount the filesystem if there were errors.
132 @return EFalse if there were no problems in FS.
134 TBool CScanDrive::ProblemsDiscovered() const
136 return IsDirError() || iFoundProblems;
140 Sets the flag indicating than there are errors in filesystem structure
141 See ProblemsDiscovered()
143 void CScanDrive::IndicateErrorsFound()
145 iFoundProblems = ETrue;
151 Start point for scan drive also fixes up errors
153 @return The result of the scan
156 TInt CScanDrive::StartL()
158 __PRINT(_L("CScanDrive::StartL"));
159 // check directory structure
160 CheckDirStructureL();
161 #if defined(DEBUG_SCANDRIVE)
164 // fix error in directory structure
169 #if defined(DEBUG_SCANDRIVE)
176 Fix errors detected by the drive scan
178 @leave System wide error code
180 void CScanDrive::FixupDirErrorL()
184 if(iDirError==EScanMatchingEntry)
186 FindSameStartClusterL();
194 IndicateErrorsFound(); //-- indicate that we have found errors
198 Find positions of entries with same start cluster for error correction, searches
199 the whole volume. Starts at the root directory.
201 @leave System wide error code
203 void CScanDrive::FindSameStartClusterL()
205 TInt err=FindStartClusterL(0);
208 for(TInt i=0;i<KMaxArrayDepth && iClusterListArray[i]!=NULL;++i)
210 RArray<TInt>* clusterList=iClusterListArray[i];
211 for(TInt j=0;j<clusterList->Count();++j)
214 err=FindStartClusterL((*clusterList)[j]);
219 __ASSERT_ALWAYS(err==KErrNone,User::Leave(KErrNotFound));
222 Scan through directory structure looking for start cluster found in iMatching
224 @param aDirCluster Start cluster for scan to start
225 @return System wide error value
228 TInt CScanDrive::FindStartClusterL(TInt aDirCluster)
230 __PRINT1(_L("CScanDrive::FindStartCluster dirCluster=%d"),aDirCluster);
231 __ASSERT_ALWAYS(aDirCluster>=0,User::Leave(KErrCorrupt));
232 if(++iRecursiveDepth==KMaxScanDepth)
235 return(KErrNotFound);
237 TEntryPos entryPos(aDirCluster,0);
242 iMount->ReadDirEntryL(entryPos,entry);
243 if(entry.IsParentDirectory()||entry.IsCurrentDirectory()||entry.IsErased())
245 if(IsEndOfRootDir(entryPos))
247 iMount->MoveToNextEntryL(entryPos);
250 if(entry.IsEndOfDirectory())
253 TEntryPos vfatPos=entryPos;
254 isComplete=MoveToVFatEndL(entryPos,entry,dirEntries);
255 __ASSERT_ALWAYS(isComplete,User::Leave(KErrBadName));
256 TInt err=CheckEntryClusterL(entry,vfatPos);
262 if(IsEndOfRootDir(entryPos))
264 iMount->MoveToNextEntryL(entryPos);
267 return(KErrNotFound);
271 Procces aEntry to find matching start cluster
273 @param aEntry Directory entry to check
274 @param aEntryPos Position of directory to check
275 @return System wide error value
278 TInt CScanDrive::CheckEntryClusterL(const TFatDirEntry& aEntry, const TEntryPos& aEntryPos)
280 __PRINT(_L("CScanDrive::CheckEntryClusterL"));
281 if(iMount->StartCluster(aEntry)==iMatching.iStartCluster)
283 TBool complete=AddMatchingEntryL(aEntryPos);
287 else if(aEntry.Attributes()&KEntryAttDir)
288 return(FindStartClusterL(iMount->StartCluster(aEntry)));
289 return(KErrNotFound);
293 Checks directory strucutre for errors, can be considered the start point of the scan.
294 Handles recursion depth to avoid stack overflow.
296 @leave System wide error code
298 void CScanDrive::CheckDirStructureL()
300 CheckDirL(iMount->RootIndicator());
301 // Due to recursive nature of CheckDirL when a depth of
302 // KMaxScanDepth is reached clusters are stored in a list
303 // and passed into CheckDirL afresh
304 for(TInt i=0;i<KMaxArrayDepth && iClusterListArray[i]!=NULL;++i)
306 RArray<TInt>* clusterList=iClusterListArray[i];
308 for(TInt j=0;j<clusterList->Count();++j)
311 CheckDirL((*clusterList)[j]);
316 Function is called recursively with Process entry untill the whole volume has been scanned.
317 Each directory entry is scanned for errors, these are recorded for later fixing.
319 @param aCluster Directory cluster to start checking
320 @leave System wide error codes
322 void CScanDrive::CheckDirL(TInt aCluster)
324 __PRINT1(_L("CScanDrive::CheckDirL aCluster=%d"),aCluster);
325 __ASSERT_ALWAYS(aCluster>=0,User::Leave(KErrCorrupt));
326 // check depth of recursion
327 if(++iRecursiveDepth==KMaxScanDepth)
329 AddToClusterListL(aCluster);
333 #if defined(DEBUG_SCANDRIVE)
336 TEntryPos entryPos(aCluster,0);
341 iMount->ReadDirEntryL(entryPos,entry);
342 if(!iMount->IsEndOfClusterCh(entryPos.iCluster))
344 if(entry.IsParentDirectory()||entry.IsCurrentDirectory()||entry.IsErased())
346 if(IsEndOfRootDir(entryPos))
348 iMount->MoveToNextEntryL(entryPos);
351 if(entry.IsEndOfDirectory())
354 WriteClusterChainL(aCluster,dirEntries<<KSizeOfFatDirEntryLog2);
357 TEntryPos origPos=entryPos;
358 TFatDirEntry origEntry=entry;
359 TInt origDirEntries=dirEntries;
361 isComplete=MoveToVFatEndL(entryPos,entry,dirEntries);
362 // Only assume that this is a corrupted VFAT entry if the VFAT attributes are set;
363 // assuming a non-VFAT corrupted entry is a VFAT entry is dangerous as we then assume that the
364 // first byte is a count of entries to skip, thus completely invalidating the next <n> directories.
365 if (!isComplete && origEntry.IsVFatEntry())
367 AddPartialVFatL(origPos,origEntry);
368 if(entryPos.iCluster!=KEndOfDirectory)
370 TInt toMove=origEntry.NumFollowing()-(dirEntries-origDirEntries);
372 MovePastEntriesL(entryPos,entry,toMove,dirEntries);
376 // we fell off the end of the directory file, so just strip this
377 // incomplete long file name entry
378 dirEntries = origDirEntries;
382 ProcessEntryL(entry);
383 if(IsEndOfRootDir(entryPos))
385 iMount->MoveToNextEntryL(entryPos);
391 Process non trivial entries, such as files, if they are correct by filling out their
392 cluster allocation in the bit packed Fat table. If it comes accross a directory
393 CheckDirL will be called.
395 @param aEntry Directory entry to check
396 @leave System wide error code
398 void CScanDrive::ProcessEntryL(const TFatDirEntry& aEntry)
400 __PRINT(_L("CScanDrive::ProcessEntryL"));
401 TInt entryAtt=aEntry.Attributes();
402 __ASSERT_ALWAYS(!(entryAtt&~KEntryAttMaskSupported)&&!aEntry.IsErased(),User::Leave(KErrCorrupt));
403 if(!(entryAtt&(KEntryAttDir|KEntryAttVolume)) && iMount->StartCluster(aEntry)>0)
404 WriteClusterChainL(iMount->StartCluster(aEntry),aEntry.Size());
405 else if(entryAtt&KEntryAttDir)
406 CheckDirL(iMount->StartCluster(aEntry));
410 Writes out the cluster chain for a correct file or directory, checks that the cluster
411 has not already been used and that the correct number of clusters are allocated for the
412 size of file. Registers cluster as used if correct
414 @param aCluster Cluster chain start point
415 @param aSizeInBytes Size of the file or directory in bytes
416 @leave System wide error values
418 void CScanDrive::WriteClusterChainL(TInt aCluster,TInt aSizeInBytes)
420 // Mark off in the new fat the clusters used by entry with start cluster of aCluster
424 IndicateErrorsFound(); //-- indicate that we have found errors
426 __PRINT1(_L("CScanDrive::WriteClusterChainL starting at %d"),aCluster);
427 __ASSERT_ALWAYS(aCluster>0 && aSizeInBytes>=0,User::Leave(KErrCorrupt));
432 clusterCount=(aSizeInBytes+(1<<iMount->ClusterSizeLog2())-1)>>iMount->ClusterSizeLog2();
433 TInt startCluster=aCluster;
436 if(AlreadyExistsL(aCluster))
438 __ASSERT_ALWAYS(!IsDirError()&&iMatching.iStartCluster==0&&aCluster==startCluster,User::Leave(KErrCorrupt));
439 iMatching.iStartCluster=aCluster;
440 iDirError=EScanMatchingEntry;
445 iNewFat->WriteFatEntryEofFL(aCluster);
451 clusterVal=iMount->FAT().ReadL(aCluster);
452 __ASSERT_ALWAYS(!IsEofF(clusterVal) && clusterVal!=0,User::Leave(KErrCorrupt));
453 iNewFat->WriteL(aCluster,clusterVal);
461 Move to dos entry, checking all vfat entry ID numbers are in sequence.
462 Assumes aEntry is not erased
464 @param aPos Position of the entry to move from, returns with new position
465 @param aEntry The Dos entry after the Vfat entries on return
466 @param aDirLength Running total of the length of the directory in entries
467 @leave System wide error codes
468 @return EFalse if not valid vfat entries or dos entry, else returns ETrue
470 TBool CScanDrive::MoveToVFatEndL(TEntryPos& aPos,TFatDirEntry& aEntry,TInt& aDirLength)
472 __PRINT2(_L("CScanDrive::MoveToVFatEndL cluster=%d,pos=%d"),aPos.iCluster,aPos.iPos);
473 if(!aEntry.IsVFatEntry())
474 return IsDosEntry(aEntry);
475 TInt toFollow=aEntry.NumFollowing();
476 __ASSERT_ALWAYS(toFollow>0&&!aEntry.IsErased(),User::Leave(KErrCorrupt));
479 iMount->MoveToNextEntryL(aPos);
480 iMount->ReadDirEntryL(aPos,aEntry);
485 if(!IsValidVFatEntry(aEntry,toFollow))
488 return(IsDosEntry(aEntry));
492 Check if an entry is valid VFat
494 @param aEntry Entry to check
495 @param aPrevNum Number into VFat entries for a dos entry to ensure in correct position
496 @return ETrue if aEntry is a valid vfat entry
498 TBool CScanDrive::IsValidVFatEntry(const TFatDirEntry& aEntry, TInt aPrevNum)const
500 if(aEntry.IsErased()||!aEntry.IsVFatEntry())
502 return(aEntry.NumFollowing()==aPrevNum);
506 Check if an entry is a Dos entry
508 @param aEntry Entry to check
509 @return ETrue if aEntry is a dos entry
511 TBool CScanDrive::IsDosEntry(const TFatDirEntry& aEntry)const
513 TBool res = !(aEntry.Attributes()&~KEntryAttMaskSupported) && !aEntry.IsErased() && !aEntry.IsVFatEntry() && !aEntry.IsEndOfDirectory();
518 Add partial entry to iPartEntry under the error condition of not all Vfat entries
521 @param aStartPos Position of the Dos entry associated with the VFat entries
522 @param aEntry Directory Entry of the Dos entry associated with the VFat entries
523 @leave KErrCorrupt Occurs if the entry is not valid
525 void CScanDrive::AddPartialVFatL(const TEntryPos& aStartPos, const TFatDirEntry& aEntry)
527 __PRINT2(_L("CScanDrive::AddPartialVFatL cluster=%d pos=%d"),aStartPos.iCluster,aStartPos.iPos);
528 __ASSERT_ALWAYS(!IsDirError(),User::Leave(KErrCorrupt));
529 iPartEntry.iEntryPos=aStartPos;
530 iPartEntry.iEntry=aEntry;
531 iDirError=EScanPartEntry;
535 Add entry position to iMatching
537 @param aEntryPos Position of the entry with the matching entry
538 @leave KErrCorrupt if the start cluster is 0 or more that two matching entries occurs
541 TBool CScanDrive::AddMatchingEntryL(const TEntryPos& aEntryPos)
543 __PRINT2(_L("CScanDrive::AddMatchingEntryL cluster=%d pos=%d"),aEntryPos.iCluster,aEntryPos.iPos);
544 __ASSERT_ALWAYS(iMatching.iStartCluster>0 && iMatching.iCount<KMaxMatchingEntries,User::Leave(KErrCorrupt));
545 iMatching.iEntries[iMatching.iCount++]=aEntryPos;
546 return iMatching.iCount==KMaxMatchingEntries;
551 Scan for differnces in the new and old FAT table writing them to media if discovered
553 @leave System wide error codes
555 void CScanDrive::WriteNewFatsL()
557 // Write the new fat table to disk
560 if(iNewFat->FlushL())
561 IndicateErrorsFound(); //-- indicate that we have found errors
564 TInt CScanDrive::GetReservedidL(TEntryPos aVFatPos)
566 // Return the id found in reserved2 field of dos entry
569 __PRINT(_L("CScanDrive::GetReservedidL"));
571 iMount->ReadDirEntryL(aVFatPos,entry);
572 if(!IsDosEntry(entry))
574 TInt toMove=entry.NumFollowing();
576 iMount->MoveToNextEntryL(aVFatPos);
577 iMount->ReadDirEntryL(aVFatPos,entry);
579 return(entry.RuggedFatEntryId());
583 Erase part entry found in iPartEntry
585 @leave System wide error code
587 void CScanDrive::FixPartEntryL()
589 __PRINT2(_L("CScanDrive::FixPartEntryL cluster=%d,pos=%d"),iPartEntry.iEntryPos.iCluster,iPartEntry.iEntryPos.iPos);
590 iMount->EraseDirEntryL(iPartEntry.iEntryPos,iPartEntry.iEntry);
591 IndicateErrorsFound(); //-- indicate that we have found errors
595 Delete entry with largest value in the reserved2 section(bytes 20 and 21) of dos entry
597 @leave System wide error code
599 void CScanDrive::FixMatchingEntryL()
601 __PRINT1(_L("CScanDrive::FixMatchingEntryL() start cluster=%d"),iMatching.iStartCluster);
602 __ASSERT_ALWAYS(iMatching.iCount==KMaxMatchingEntries,User::Leave(KErrCorrupt));
603 TInt idOne=GetReservedidL(iMatching.iEntries[0]);
604 TInt idTwo=GetReservedidL(iMatching.iEntries[1]);
606 TInt num=idOne>idTwo?0:1;
607 iMount->ReadDirEntryL(iMatching.iEntries[num],entry);
608 iMount->EraseDirEntryL(iMatching.iEntries[num],entry);
609 IndicateErrorsFound(); //-- indicate that we have found errors
612 Move past specified number of entries
614 @param aEntryPos Start position to move from, updated as move takes place
615 @param aEntry Directory entry moved to
616 @param aToMove Number of entries to move through
617 @param aDirEntries Number of entries moved, updated as move takes place
618 @leave System wide error code
620 void CScanDrive::MovePastEntriesL(TEntryPos& aEntryPos,TFatDirEntry& aEntry,TInt aToMove,TInt& aDirEntries)
622 while(aToMove-- && aEntryPos.iCluster!=KEndOfDirectory)
624 iMount->MoveToNextEntryL(aEntryPos);
627 iMount->ReadDirEntryL(aEntryPos,aEntry);
631 Adds aCluster to cluster list array so that it may be revisited later, avoids stack
634 @param aCluster Directory cluster number to add to the list
635 @leave KErrNoMemory If allocation fails
637 void CScanDrive::AddToClusterListL(TInt aCluster)
639 if(iListArrayIndex>=KMaxArrayDepth)
641 if(iClusterListArray[iListArrayIndex]==NULL)
642 iClusterListArray[iListArrayIndex]=new(ELeave) RArray<TInt>(KClusterListGranularity);
643 iClusterListArray[iListArrayIndex]->Append(aCluster);
647 #if defined(DEBUG_SCANDRIVE)
648 void CScanDrive::CompareFatsL()
650 // Compare new fat and first fat table
653 __PRINT(_L("CScanDrive::CompareFatsL()"));
655 maxClusters=iMount->UsableClusters();
656 for(TInt i=KFatFirstSearchCluster; i<maxClusters; ++i)
658 TInt realFat=iMount->FAT().ReadL(i);
659 TInt newFat=iNewFat->ReadL(i);
662 if(realFat!=0 && newFat==0)
663 __PRINT1(_L("Lost cluster=%d\n"),i)
664 else if((realFat>0 && !IsEofF(realFat)) && IsEofF(newFat))
665 __PRINT1(_L("Hanging cluster = %d\n"),i)
666 else if(realFat==0 && newFat>0)
667 __PRINT1(_L("Unflushed cluster = %d\n"),i)
669 User::Leave(KErrCorrupt);
676 For debug purposes, print errors found as debug output
679 void CScanDrive::PrintErrors()
681 __PRINT1(_L("Directories visisted = %d\n"),iDirsChecked);
682 if(iDirError==EScanPartEntry)
683 __PRINT2(_L("Part entry-dir cluster=%d,dir pos=%d,\n"),iPartEntry.iEntryPos.iCluster,iPartEntry.iEntryPos.iPos)
684 else if(iDirError==EScanMatchingEntry)
686 __PRINT1(_L("Matching cluster - cluster no=%d\n"),iMatching.iStartCluster);
687 __PRINT2(_L("\tcluster 1 - dir cluster=%d,dir pos=%d\n"),iMatching.iEntries[0].iCluster,iMatching.iEntries[0].iPos);
688 __PRINT2(_L("\tcluster 2 - dir cluster=%d,dir pos=%d\n"),iMatching.iEntries[1].iCluster,iMatching.iEntries[1].iPos);