70 void CScanDrive::ConstructL(CFatMountCB* aMount) |
70 void CScanDrive::ConstructL(CFatMountCB* aMount) |
71 { |
71 { |
72 ASSERT(aMount); |
72 ASSERT(aMount); |
73 |
73 |
74 //--- setting up |
74 //--- setting up |
75 iMount = aMount; |
75 iMount=aMount; |
76 iGenericError = ENoErrors; |
76 iGenericError = ENoErrors; |
77 iDirError = ENoDirError; |
77 iDirError = ENoDirError; |
78 iHangingClusters = 0; |
78 iMaxClusters = iMount->UsableClusters()+KFatFirstSearchCluster; //-- UsableClusters() doesn't count first 2 unused clusers |
79 iMaxClusters = iMount->UsableClusters()+KFatFirstSearchCluster; //-- UsableClusters() doesn't count first 2 unused clusers |
|
80 //------------------------------ |
79 //------------------------------ |
81 |
80 |
82 //-- create bit vectors that will represent FAT on media and reconstructed by ScanDrive. Each bit in the vector represents 1 FAT cluster. |
81 //-- create bit vectors that will represent FAT on media and reconstructed by ScanDrive. Each bit in the vector represents 1 FAT cluster. |
83 const TUint32 KClustersNum = MaxClusters(); |
82 const TUint32 KClustersNum = MaxClusters(); |
84 |
83 |
108 for(TInt i=KFatFirstSearchCluster; i<KMaxClusters; ++i) |
107 for(TInt i=KFatFirstSearchCluster; i<KMaxClusters; ++i) |
109 { |
108 { |
110 const TUint32 nFatEntry = ReadFatL(i); |
109 const TUint32 nFatEntry = ReadFatL(i); |
111 |
110 |
112 //-- each '1' bit represents a used cluster |
111 //-- each '1' bit represents a used cluster |
113 if(nFatEntry != KSpareCluster) |
112 if(nFatEntry != KSpareCluster) |
114 iMediaFatBits.SetBit(i); |
113 iMediaFatBits.SetBit(i); |
115 } |
114 } |
116 } |
115 } |
117 |
116 |
118 //---------------------------------------------------------------------------------------------------- |
117 //---------------------------------------------------------------------------------------------------- |
124 void CScanDrive::DoParseFat32Buf(const TPtrC8& aBuf, TUint32& aCurrFatEntry) |
123 void CScanDrive::DoParseFat32Buf(const TPtrC8& aBuf, TUint32& aCurrFatEntry) |
125 { |
124 { |
126 ASSERT((aBuf.Size() & (sizeof(TFat32Entry)-1)) == 0); |
125 ASSERT((aBuf.Size() & (sizeof(TFat32Entry)-1)) == 0); |
127 |
126 |
128 const TInt KNumEntries = aBuf.Size() >> KFat32EntrySzLog2; |
127 const TInt KNumEntries = aBuf.Size() >> KFat32EntrySzLog2; |
129 const TFat32Entry* const pFatEntry = (const TFat32Entry*)(aBuf.Ptr()); |
128 const TFat32Entry* const pFatEntry = (const TFat32Entry*)(aBuf.Ptr()); |
130 |
129 |
131 for(TInt i=0; i<KNumEntries; ++i) |
130 for(TInt i=0; i<KNumEntries; ++i) |
132 { |
131 { |
133 if(aCurrFatEntry >= KFatFirstSearchCluster) |
132 if(aCurrFatEntry >= KFatFirstSearchCluster) |
134 { |
133 { |
160 |
159 |
161 const TUint32 KFatBufSz = 32*K1KiloByte; //-- buffer size for FAT reading. 32K seems to be optimal size |
160 const TUint32 KFatBufSz = 32*K1KiloByte; //-- buffer size for FAT reading. 32K seems to be optimal size |
162 |
161 |
163 iMediaFatBits.Fill(0); |
162 iMediaFatBits.Fill(0); |
164 |
163 |
165 RBuf8 fatParseBuf; |
164 RBuf8 buf; |
166 CleanupClosePushL(fatParseBuf); |
165 CleanupClosePushL(buf); |
167 |
166 |
168 //-- allocate memory for FAT parse buffer |
167 //-- allocate memory for FAT parse buffer |
169 fatParseBuf.CreateMaxL(KFatBufSz); |
168 buf.CreateMaxL(KFatBufSz); |
170 |
169 |
171 //-- read FAT directly from the media into the large buffer and parse it |
170 //-- read FAT directly from the media into the large buffer and parse it |
172 TUint32 rem = KFatSize; |
171 TUint32 rem = KFatSize; |
173 TUint32 mediaPos = KFat1StartPos; |
172 TUint32 mediaPos = KFat1StartPos; |
174 TUint32 currFatEntry = 0; |
173 TUint32 currFatEntry = 0; |
175 |
174 |
176 while(rem) |
175 while(rem) |
177 { |
176 { |
178 const TUint32 bytesToRead=Min(rem, KFatBufSz); |
177 const TUint32 bytesToRead=Min(rem, KFatBufSz); |
179 TPtrC8 ptrData(fatParseBuf.Ptr(), bytesToRead); |
178 TPtrC8 ptrData(buf.Ptr(), bytesToRead); |
180 |
179 |
181 //-- read portion of the FAT into buffer |
180 //-- read portion of the FAT into buffer |
182 User::LeaveIfError(iMount->LocalDrive()->Read(mediaPos, bytesToRead, fatParseBuf)); |
181 User::LeaveIfError(iMount->LocalDrive()->Read(mediaPos, bytesToRead, buf)); |
183 |
182 |
184 //-- parse the buffer and populate bit vector |
183 //-- parse the buffer and populate bit vector |
185 DoParseFat32Buf(ptrData, currFatEntry); |
184 DoParseFat32Buf(ptrData, currFatEntry); |
186 |
185 |
187 mediaPos += bytesToRead; |
186 mediaPos += bytesToRead; |
188 rem -= bytesToRead; |
187 rem -= bytesToRead; |
189 } |
188 } |
190 |
189 |
191 fatParseBuf.Close(); |
190 buf.Close(); |
192 CleanupStack::PopAndDestroy(&fatParseBuf); |
191 CleanupStack::PopAndDestroy(&buf); |
193 } |
192 } |
194 |
193 |
195 |
194 |
196 |
195 |
197 //---------------------------------------------------------------------------------------------------- |
196 //---------------------------------------------------------------------------------------------------- |
240 else |
239 else |
241 return iGenericError; |
240 return iGenericError; |
242 } |
241 } |
243 |
242 |
244 /** |
243 /** |
245 Sets the flag indicating that there are errors in filesystem structure |
244 Sets the flag indicating than there are errors in filesystem structure |
246 See ProblemsDiscovered() |
245 See ProblemsDiscovered() |
247 |
246 |
248 @param aError a code describing the error |
247 @param aError a code describing the error |
249 */ |
248 */ |
250 void CScanDrive::IndicateErrorsFound(TGenericError aError) |
249 void CScanDrive::IndicateErrorsFound(TGenericError aError) |
476 */ |
476 */ |
477 void CScanDrive::CheckDirStructureL() |
477 void CScanDrive::CheckDirStructureL() |
478 { |
478 { |
479 CheckDirL(iMount->RootIndicator()); |
479 CheckDirL(iMount->RootIndicator()); |
480 // Due to recursive nature of CheckDirL when a depth of |
480 // Due to recursive nature of CheckDirL when a depth of |
481 // KMaxScanDepth is reached, clusters are stored in a list |
481 // KMaxScanDepth is reached clusters are stored in a list |
482 // and passed into CheckDirL afresh |
482 // and passed into CheckDirL afresh |
483 |
483 |
484 for(TUint i=0;i<KMaxArrayDepth && iClusterListArray[i]!=NULL;++i) |
484 for(TUint i=0;i<KMaxArrayDepth && iClusterListArray[i]!=NULL;++i) |
485 { |
485 { |
486 RArray<TInt>* clusterList=iClusterListArray[i]; |
486 RArray<TInt>* clusterList=iClusterListArray[i]; |
658 |
658 |
659 while(clusterCount) |
659 while(clusterCount) |
660 { |
660 { |
661 if(IsClusterUsedL(aCluster)) |
661 if(IsClusterUsedL(aCluster)) |
662 {//-- this cluster already seems to belong to some other object; crosslinked cluster chain. Can't fix it. |
662 {//-- this cluster already seems to belong to some other object; crosslinked cluster chain. Can't fix it. |
663 __PRINT1(_L("CScanDrive::RecordClusterChainL #1 %d"),aCluster); |
663 __PRINT1(_L("CScanDrive::RecordClusterChainL #1 %d"),aCluster); |
664 |
664 |
665 if(CheckDiskMode()) |
665 if(CheckDiskMode()) |
666 {//-- in check disk mode this is an FS error; Indicate error and abort further scanning |
666 {//-- in check disk mode this is a FS error; Indicate error and abort furter scanning |
667 __PRINT(_L("CScanDrive::RecordClusterChainL #1.1")); |
667 __PRINT(_L("CScanDrive::RecordClusterChainL #1.1")); |
668 IndicateErrorsFound(EClusterAlreadyInUse); |
668 IndicateErrorsFound(EClusterAlreadyInUse); |
669 User::Leave(KErrCorrupt); |
669 User::Leave(KErrCorrupt); |
670 } |
670 } |
671 |
671 |
672 |
672 |
685 |
685 |
686 |
686 |
687 if(clusterCount==1) |
687 if(clusterCount==1) |
688 {//-- we have reached the end of the cluster chain |
688 {//-- we have reached the end of the cluster chain |
689 if(!iMount->IsEndOfClusterCh(ReadFatL(aCluster))) |
689 if(!iMount->IsEndOfClusterCh(ReadFatL(aCluster))) |
690 { |
690 {//-- seems to be a rugged FAT artefact; File truncation/extension had failed before and now file length is less than |
691 // According to the directory entry, we have reached the end of the cluster chain, |
691 //-- the corresponding cluster chain shall be. It will be truncated to the size recorded in file DOS entry. |
692 // whereas in the media FAT, it is not. |
692 iTruncationCluster = aCluster; |
693 // This is a rugged FAT artefact; hanging cluster chain: |
|
694 // A cluster chain which is longer in the FAT table than is recorded in the corresponding directory entry |
|
695 // or not terminated by an EOC entry in FAT. |
|
696 // This is caused by: |
|
697 // - File truncation failing. |
|
698 // - OR file expanding failing during flushing to the media FAT. |
|
699 |
693 |
700 if(CheckDiskMode()) |
694 if(CheckDiskMode()) |
701 {//-- in check disk mode this is an FS error; Indicate error and abort further scanning |
695 {//-- in check disk mode this is a FS error; Indicate error and abort furter scanning |
702 __PRINT1(_L("CScanDrive::RecordClusterChainL #2 Hanging cluster=%d"),aCluster); |
696 __PRINT1(_L("CScanDrive::RecordClusterChainL #2 %d"),aCluster); |
703 IndicateErrorsFound(EInvalidEntrySize); |
697 IndicateErrorsFound(EInvalidEntrySize); |
704 User::Leave(KErrCorrupt); |
698 User::Leave(KErrCorrupt); |
705 } |
699 } |
706 |
|
707 // The chain will be truncated to the size recorded in the file's DOS entry and |
|
708 // the remaining lost cluster chain will be fixed later in CompareAndFixFatsL(). |
|
709 FixHangingClusterChainL(aCluster); |
|
710 } |
700 } |
711 |
701 |
712 //__PRINT1(_L("#--: %d -> EOC"), aCluster); |
702 //__PRINT1(_L("#--: %d -> EOC"), aCluster); |
713 MarkClusterUsedL(aCluster); |
703 MarkClusterUsedL(aCluster); |
714 return; |
704 return; |
771 break; |
761 break; |
772 |
762 |
773 if(!IsValidVFatEntry(aEntry,toFollow)) |
763 if(!IsValidVFatEntry(aEntry,toFollow)) |
774 return(EFalse); |
764 return(EFalse); |
775 } |
765 } |
776 // A sequence of VFat entries must end with a Dos entry to be valid. |
766 |
777 return(IsDosEntry(aEntry)); |
767 return(IsDosEntry(aEntry)); |
778 } |
768 } |
779 |
769 |
780 //---------------------------------------------------------------------------------------------------- |
770 //---------------------------------------------------------------------------------------------------- |
781 /** |
771 /** |
782 Check if an entry is valid VFat |
772 Check if an entry is valid VFat |
853 } |
843 } |
854 |
844 |
855 |
845 |
856 //---------------------------------------------------------------------------------------------------- |
846 //---------------------------------------------------------------------------------------------------- |
857 /** |
847 /** |
858 Scan for differences in the new and old FAT table writing them to media if discovered |
848 Scan for differnces in the new and old FAT table writing them to media if discovered |
859 It is supposed to be called in 'ScanDrive' mode only |
849 It is supposed to be called in 'ScanDrive' mode only |
860 |
850 |
861 @leave System wide error codes |
851 @leave System wide error codes |
862 */ |
852 */ |
863 void CScanDrive::CompareAndFixFatsL() |
853 void CScanDrive::CompareAndFixFatsL() |
893 { |
883 { |
894 ++nBadClusters; |
884 ++nBadClusters; |
895 continue; |
885 continue; |
896 } |
886 } |
897 |
887 |
898 //-- Here we found a lost cluster. Its FAT entry will be replaced with KSpareCluster. |
888 //-- here we found a lost cluster. Its FAT entry will be replaced with KSpareCluster. In the case of multiple lost clusters FAT table will |
899 //-- In the case of multiple lost clusters FAT table will be flushed on media sector basis. |
889 //-- be flushed on media sector basis. It is much faster than flushing FAT after every write and will |
900 //-- It is much faster than flushing FAT after every write and will guarantee |
890 //-- guarantee that FAT won't be corrupted if the media driver provides atomic sector write. |
901 //-- that FAT won't be corrupted if the media driver provides atomic sector write. |
|
902 if(nClustersFixed == 0) |
891 if(nClustersFixed == 0) |
903 {//-- this is the first lost cluster entry we found |
892 {//-- this is the first lost cluster entry we found |
904 |
893 |
905 //-- relative FAT media sector for the 'i' entry. The real value doesn't matter, |
894 //-- relative FAT media sector for the 'i' entry. The real value doesn't matter, |
906 //-- we will just be flushing FAT before writing to the different FAT media sector. |
895 //-- we will just be flushing FAT before writing to the different FAT media sector. |
911 else |
900 else |
912 { |
901 { |
913 const TUint32 fatSec = iMount->FAT().PosInBytes(i) >> KSectorSzLog2; |
902 const TUint32 fatSec = iMount->FAT().PosInBytes(i) >> KSectorSzLog2; |
914 |
903 |
915 if(fatSec != dirtyFatSector) |
904 if(fatSec != dirtyFatSector) |
916 {//-- we are going to write to a different media sector |
905 {//-- we are going to write to a differrent media sector |
917 iMount->FAT().FlushL(); |
906 iMount->FAT().FlushL(); |
918 iMount->FAT().WriteL(i, KSpareCluster); //-- fix lost cluster |
907 iMount->FAT().WriteL(i, KSpareCluster); //-- fix lost cluster |
919 dirtyFatSector = fatSec; |
908 dirtyFatSector = fatSec; |
920 } |
909 } |
921 else |
910 else |
937 if(nClustersFixed) |
926 if(nClustersFixed) |
938 iMount->FAT().FlushL(); |
927 iMount->FAT().FlushL(); |
939 |
928 |
940 //------ |
929 //------ |
941 |
930 |
942 |
931 if(iTruncationCluster != 0) |
943 // Add the number of hanging clusters fixed by ScanDrive |
932 { |
944 nClustersFixed += iHangingClusters; |
933 iMount->FAT().WriteFatEntryEofL(iTruncationCluster); |
945 |
934 iMount->FAT().FlushL(); |
946 __PRINT3(_L("CScanDrive::WriteNewFatsL() fixed clusters=%d,hanging clusters=%d,bad clusters=%d"),nClustersFixed,iHangingClusters,nBadClusters); |
935 |
|
936 //-- indicate that there are some problems in FAT. and we probably wrote something there. |
|
937 IndicateErrorsFound(EScanDriveDirError); //-- indicate that we have found errors |
|
938 |
|
939 ++nClustersFixed; |
|
940 } |
|
941 |
|
942 __PRINT2(_L("CScanDrive::WriteNewFatsL() fixed:%d, bad:%d"), nClustersFixed, nBadClusters); |
947 } |
943 } |
948 |
944 |
949 //---------------------------------------------------------------------------------------------------- |
945 //---------------------------------------------------------------------------------------------------- |
950 /** |
946 /** |
951 Read the "Rugged FAT" ID, stored in reserved2 in the Dos entry or associated with the Dos entry of the |
947 Read the "Rugged FAT" ID, stored in reserved2 in the Dos entry or associated with the Dos entry of the |
1014 |
1010 |
1015 iMount->EraseDirEntryL(iMatching.iEntries[num],entry); |
1011 iMount->EraseDirEntryL(iMatching.iEntries[num],entry); |
1016 |
1012 |
1017 IndicateErrorsFound(EScanDriveDirError); //-- indicate that we have found errors |
1013 IndicateErrorsFound(EScanDriveDirError); //-- indicate that we have found errors |
1018 } |
1014 } |
1019 |
|
1020 //---------------------------------------------------------------------------------------------------- |
|
1021 /** |
|
1022 Fix a hanging cluster chain. |
|
1023 Writes EOF to the corresponding FAT entry, making this cluster chain length correspond to the |
|
1024 real file size recorded in the directory entry. |
|
1025 The remainder of the chain will be cleaned up later in CompareAndFixFatsL(). |
|
1026 |
|
1027 @leave System wide error code |
|
1028 */ |
|
1029 void CScanDrive::FixHangingClusterChainL(TUint32 aFatEofIndex) |
|
1030 { |
|
1031 __PRINT1(_L("CScanDrive::FixHangingClusterL() Hanging cluster=%d"), aFatEofIndex); |
|
1032 |
|
1033 iMount->FAT().WriteFatEntryEofL(aFatEofIndex); |
|
1034 iMount->FAT().FlushL(); |
|
1035 iHangingClusters++; |
|
1036 |
|
1037 // Indicate that we have found an error |
|
1038 IndicateErrorsFound(EScanDriveDirError); |
|
1039 } |
|
1040 |
|
1041 |
1015 |
1042 //---------------------------------------------------------------------------------------------------- |
1016 //---------------------------------------------------------------------------------------------------- |
1043 /** |
1017 /** |
1044 Move past specified number of entries |
1018 Move past specified number of entries |
1045 |
1019 |
1111 const TBool bNewFatEntry = iScanFatBits[i]; |
1085 const TBool bNewFatEntry = iScanFatBits[i]; |
1112 |
1086 |
1113 if(BoolXOR(bRealFatEntry, bNewFatEntry)) |
1087 if(BoolXOR(bRealFatEntry, bNewFatEntry)) |
1114 {//-- mismatch between FAT on the media and the FAT bitmap restored by walking directory structure |
1088 {//-- mismatch between FAT on the media and the FAT bitmap restored by walking directory structure |
1115 |
1089 |
1116 if(bRealFatEntry) |
1090 if(bRealFatEntry) |
1117 {//-- FAT[i] on the media is marked as occupied, but restored FAT bitmap shows that it is free |
1091 {//-- FAT[i] on the media is marked as occupied, but retored FAT bitmap shows that it is free |
1118 if(iMount->IsBadCluster(ReadFatL(i))) |
1092 if(iMount->IsBadCluster(ReadFatL(i))) |
1119 continue; //-- this is a BAD cluster it can't be occupied by the FS object, OK. |
1093 continue; //-- this is a BAD cluster it can't be occupied by the FS object, OK. |
1120 |
1094 |
1121 __PRINT2(_L("FAT[%d] = %d\n"), i, ReadFatL(i)); |
1095 __PRINT2(_L("FAT[%d] = %d\n"), i, ReadFatL(i)); |
1122 |
1096 __PRINT1(_L("iTruncationCluster = %d\n"), iTruncationCluster); |
1123 //-- this is a Rugged FAT artefact; a lost cluster |
1097 |
1124 __PRINT1(_L("Lost cluster=%d\n"),i); |
1098 //-- this is a lost cluster |
1125 |
1099 if(!IsEofF(ReadFatL(i)) && (i==iTruncationCluster)) |
1126 IndicateErrorsFound(EBadClusterValue); |
1100 {//-- seems to be a Rugged FAT ertefact |
1127 } |
1101 __PRINT1(_L("Hanging cluster = %d\n"),i); |
1128 else |
1102 } |
1129 {//-- FAT[i] on the media is marked as free, but restored FAT bitmap shows that it is occupied by some object |
1103 else |
1130 IndicateErrorsFound(EClusterAlreadyInUse); |
1104 { |
1131 __PRINT1(_L("Unflushed cluster = %d\n"),i); |
1105 __PRINT1(_L("Lost cluster=%d\n"),i); |
1132 } |
1106 } |
1133 |
1107 |
1134 if(aStopOnFirstErrorFound) |
1108 |
1135 break; //-- not asked to check for errors further |
1109 IndicateErrorsFound(EBadClusterValue); |
|
1110 } |
|
1111 else |
|
1112 {//-- FAT[i] on the media is marked as free, but retored FAT bitmap shows that it is occupied by some object |
|
1113 IndicateErrorsFound(EClusterAlreadyInUse); |
|
1114 __PRINT1(_L("Unflushed cluster = %d\n"),i); |
|
1115 } |
|
1116 |
|
1117 if(aStopOnFirstErrorFound) |
|
1118 break; //-- not asked to check for errors further |
1136 |
1119 |
1137 } |
1120 } |
1138 |
1121 |
1139 if(bRealFatEntry) |
1122 if(bRealFatEntry) |
1140 mediausedcnt++; |
1123 mediausedcnt++; |
1202 //__PRINT(_L("CScanDrive::MoveToNextEntryL")); |
1185 //__PRINT(_L("CScanDrive::MoveToNextEntryL")); |
1203 iMount->MoveToNextEntryL(aPos); |
1186 iMount->MoveToNextEntryL(aPos); |
1204 } |
1187 } |
1205 |
1188 |
1206 /** |
1189 /** |
1207 Read a cluster from the Media Fat if scan run in a separate thread read from scan Fat table |
1190 Read a cluster from the Media Fat if scan run in a seperate thread read from scan fat table |
1208 otherwise read from mount owned Fat table |
1191 otherwise read from mount owned Fat table |
1209 |
1192 |
1210 @param aClusterNum Cluster to read |
1193 @param aClusterNum Cluster to read |
1211 @return Value of cluster read from Fat |
1194 @return Value of cluster read from Fat |
1212 */ |
1195 */ |
1213 TUint32 CScanDrive::ReadFatL(TUint aClusterNum) |
1196 TUint32 CScanDrive::ReadFatL(TUint aClusterNum) |
1214 { |
1197 { |
1215 if(aClusterNum < KFatFirstSearchCluster || aClusterNum >= MaxClusters()) |
1198 if(aClusterNum < KFatFirstSearchCluster || aClusterNum >= MaxClusters()) |
1216 { |
1199 { |
1217 __PRINT1(_L("CScanDrive::ReadFatL() bad cluster:%d\n"),aClusterNum); |
1200 __PRINT1(_L("CScanDrive::ReadFatL() bad cluster:%d\n"),aClusterNum); |
1218 IndicateErrorsFound(EBadClusterNumber); |
1201 IndicateErrorsFound(EBadClusterNumber); |