First public contribution.
1 // Copyright (c) 1996-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 // f32test\server\b_fat32.cpp
22 #include "fat_utils.h"
25 using namespace Fat_Test_Utils;
28 RTest test(_L("B_FAT32"));
30 static RRawDisk TheDisk;
33 static TEntry TheEntry;
34 static TFileName TheFileName;
35 static TBuf<16> TheDrive;
37 static HBufC8* pBuffer1=NULL;
38 static HBufC8* pBuffer2=NULL;
39 static TBuf8<0x800> TheBuffer;
40 static TEntry TheFileInfo;
41 static TVolumeInfo TheVolumeInfo;
42 static TBuf<8> ThePddName;
43 static TFatBootSector TheBootSector;
45 static TInt64 rndSeed;
46 static TFatType gDiskType = EInvalid;
48 static TInt gFatBits = 0;
49 static TInt gBytesPerCluster;
50 static TInt gEntriesPerCluster;
51 static TInt gDataStartBytes;
52 static TInt gRootDirSectors;
53 static TInt gTotalSectors;
54 static TInt gRootDirStart;
55 static TInt gRootSector;
56 static TInt gRootCluster;
57 static TInt gFatTestEntries;
58 static TInt gFatSizeSectors;
59 static TInt gFirstDataSector;
60 static TInt gFirstDataCluster;
61 static TInt gClusterCount;
62 static TInt gEndOfChain; // for FAT12/16/32
64 const TInt KMaxFatEntries = 2048;
65 const TInt KMaxFatSize = KMaxFatEntries * 4;
66 const TInt KDirAttrReadOnly = 0x01;
67 const TInt KDirAttrHidden = 0x02;
68 const TInt KDirAttrSystem = 0x04;
69 const TInt KDirAttrVolumeId = 0x08;
70 const TInt KDirAttrDirectory = 0x10;
71 const TInt KDirAttrArchive = 0x20;
72 const TInt KDirAttrLongName = KDirAttrReadOnly | KDirAttrHidden | KDirAttrSystem | KDirAttrVolumeId;
73 const TInt KDirAttrLongMask = KDirAttrLongName | KDirAttrDirectory | KDirAttrArchive;
74 const TInt KDirLastLongEntry = 0x40;
76 void CreateFatEntry(const TDesC& aDir, TBool aVFatEntry, TDes *apFileName=NULL);
78 #define Error(aMess,aErr) PutError(__FILE__, __LINE__, aMess,aErr)
79 static void PutError(const char* aFile, TInt aLine, const TDesC& aMessage,TInt anErr)
82 TPtrC8 ptr((const TUint8*)aFile);
84 test.Printf(_L("%S failed - %d\n"), &aMessage,anErr);
85 test.Printf(_L("In %S line %d\n"), &buf, aLine);
91 // Position calculation and disk reading routines
92 // Return number of bytes into the FAT
93 static TInt PosInBytes(TInt aFatIndex)
95 TInt fatPosInBytes = -1;
99 fatPosInBytes=aFatIndex<<2;
102 fatPosInBytes=aFatIndex<<1;
105 fatPosInBytes=(aFatIndex*3>>1);
110 return(fatPosInBytes);
113 static TUint32 MaxClusters()
115 // Return the number of data clusters on the disk
118 TUint32 totSec = (TheBootSector.TotalSectors() ? TheBootSector.TotalSectors() : TheBootSector.HugeSectors());
119 TUint32 numSec = totSec - gFirstDataSector;
120 return numSec / TheBootSector.SectorsPerCluster();
123 static TInt ClusterToByte(TInt aCluster)
125 // converts cluster number to byte offset on disk
129 return gRootDirStart;
130 TInt sector = (aCluster - 2) * gBytesPerCluster + gFirstDataSector * TheBootSector.BytesPerSector();
134 TUint32 GetFatEntry(TUint32 aIndex, const TUint8* aFat=NULL)
136 // Read a single FAT entry from disk or FAT copy and return it
139 TInt pos = PosInBytes(aIndex);
145 ptr = (TUint8*)aFat + pos;
148 pos += TheBootSector.ReservedSectors() * TheBootSector.BytesPerSector();
149 TInt r=TheDisk.Open(TheFs,gSessionPath[0]-'A');
151 TPtr8 buf(&data[0], 4);
152 r=TheDisk.Read(pos, buf);
161 val = *(TUint32*)ptr;
164 val = *(TUint16*)ptr;
167 val = *(TUint16*)ptr;
178 void MarkFatEntry(TUint32 aIndex)
180 // Marks a single FAT entry by modifying it's top 4 bits to
183 TInt pos = PosInBytes(aIndex);
184 pos += TheBootSector.ReservedSectors() * TheBootSector.BytesPerSector();
186 TInt r=TheDisk.Open(TheFs,gSessionPath[0]-'A');
189 TPtr8 buf(&data[0], 4);
190 r=TheDisk.Read(pos, buf);
194 r=TheDisk.Write(pos, buf);
199 void DumpBootSector()
201 // Display (in log) TFatBootSector structure
204 RDebug::Print(_L("BytesPerSector = %8d"), TheBootSector.BytesPerSector());
205 RDebug::Print(_L("SectorsPerCluster = %8d (%d bytes)"),
206 TheBootSector.SectorsPerCluster(), gBytesPerCluster);
207 RDebug::Print(_L("ReservedSectors = %8d"), TheBootSector.ReservedSectors());
208 RDebug::Print(_L("NumberOfFats = %8d"), TheBootSector.NumberOfFats());
209 RDebug::Print(_L("RootDirEntries = %8d"), TheBootSector.RootDirEntries());
210 RDebug::Print(_L("TotalSectors = %8d"), TheBootSector.TotalSectors());
211 RDebug::Print(_L("MediaDescriptor = %8d"), TheBootSector.MediaDescriptor());
212 RDebug::Print(_L("FatSectors = %8d"), TheBootSector.FatSectors());
213 RDebug::Print(_L("SectorsPerTrack = %8d"), TheBootSector.SectorsPerTrack());
214 RDebug::Print(_L("NumberOfHeads = %8d"), TheBootSector.NumberOfHeads());
215 RDebug::Print(_L("HiddenSectors = %8d"), TheBootSector.HiddenSectors());
216 RDebug::Print(_L("HugeSectors = %8d"), TheBootSector.HugeSectors());
220 if(TheBootSector.RootDirEntries() == 0) //indicates we have FAT32 volume
222 RDebug::Print(_L("FatSectors32 = %8d"), TheBootSector.FatSectors32());
223 RDebug::Print(_L("FATFlags = %8d"), TheBootSector.FATFlags());
224 RDebug::Print(_L("VersionNumber = %8d"), TheBootSector.VersionNumber());
225 RDebug::Print(_L("RootClusterNum = %8d (0x%08X)"),
226 TheBootSector.RootClusterNum(),
228 RDebug::Print(_L("FSInfoSectorNum = %8d (0x%08X)"),
229 TheBootSector.FSInfoSectorNum(),
230 TheBootSector.FSInfoSectorNum() * TheBootSector.BytesPerSector());
231 RDebug::Print(_L("BkBootRecSector = %8d (0x%08X)"),
232 TheBootSector.BkBootRecSector(),
233 TheBootSector.BkBootRecSector() * TheBootSector.BytesPerSector());
236 TInt fatEntries = gFatSizeSectors*TheBootSector.BytesPerSector();
253 RDebug::Print(_L("ClusterCount = %8d (%ld bytes)"), gClusterCount, ((TInt64)gClusterCount)*gBytesPerCluster);
254 RDebug::Print(_L("FatEntries = %8d (%d sectors)"), fatEntries, gFatSizeSectors);
255 RDebug::Print(_L("RootSector = %8d (0x%08X)"), gRootSector, gRootDirStart);
256 RDebug::Print(_L("FirstDataSector = %8d (0x%08X)"), gFirstDataSector, gDataStartBytes);
259 void DumpFat(const TUint8* aFat=NULL)
261 // Dump to the log all those FAT entries which are non-zero
264 TInt32 max = MaxClusters();
265 if (max > KMaxFatEntries)
266 max = KMaxFatEntries;
267 RDebug::Print(_L("---------------- DUMP OF FAT ---------------"));
268 for (TInt32 i = 0; i < max; i++)
270 TInt32 val = GetFatEntry(i, aFat);
271 TInt32 msk = 0x0FFFFFFF;
286 if ((val & msk) == (0x0FFFFFFF & msk))
287 RDebug::Print(_L(" %8d -> EOC"), i);
288 else if ((val & msk) == (0x0FFFFFF8 & msk))
289 RDebug::Print(_L(" %8d -> Media"), i);
290 else if ((val & msk) == (0x0FFFFFF7 & msk))
291 RDebug::Print(_L(" %8d -> BAD"), i);
293 RDebug::Print(_L(" %8d -> 0x%08X"), i, val);
295 RDebug::Print(_L(" %8d -> %d"), i, val);
297 RDebug::Print(_L("--------------------------------------------"));
300 TDes* DirAttributes(TInt aAttrib)
302 // Return a pointer to a local buffer containing the attribute letters.
305 static TBuf<6> str(_L("------"));
306 static char* atr = "RHSVDA";
307 for (TInt i = 0; i < 6; i++)
308 if ((aAttrib >> i) & 1)
313 TBool IsValidDirChar(TUint8 aChar, TUint8 aMin=0x20)
315 // Test whether a character is valid as part of a short filename, aMin is to
316 // distinguish between first character (which can't be space) and later ones
317 // which can include space but nothing less. Note that E5 is a valid character
318 // in any position, even though it means 'erased' in the first character.
321 const TUint8* inval = (TUint8*)"\x22\x2A\x2B\x2C\x2F\x3A\x3B\x3C\x3D\x3E\x3F\x5B\x5C\x5D\x7C";
324 for (const TUint8* p = inval; *p; p++)
330 TBool IsValidDirEntry(TFatDirEntry* aDir)
332 // Test whether buffer is a valid normal directory entry
335 // top two bits of attributes must be zero
336 if (aDir->iData[11] & 0xC0)
338 // first character must be 0x05 or greater than space
339 if (!IsValidDirChar(aDir->iData[0], 0x21) && aDir->iData[0] != 0x05)
341 // other characters in name must be not less than space
342 for (TInt i = 1; i < 11; i++)
343 if (!IsValidDirChar(aDir->iData[i]))
348 void GetLongNamePart(TDes16& aName, const TUint8* aEntry, TInt aPos, TInt aOffset, TInt aLength)
350 // Extract part of a long name entry into the name buffer.
352 // @param aName buffer to put name
353 // @param aEntry directory entry raw data
354 // @param aPos character in buffer to start name segment
355 // @param aOffset offset in directory entry of the segment
356 // @param aLength number of characters in the segment
359 for (TInt i = 0; i < aLength; i++)
361 TInt at = i * 2 + aOffset;
362 TInt ch = aEntry[at] + aEntry[at+1] * 256;
363 aName[aPos++] = TText(ch);
367 void ExtractNameString(TDes16& aName, const TUint8* aEntry)
369 // Extract a long name part from a directory entry, truncate it at the first
370 // NUL (0) character and put quotes round it.
374 TInt len = aName.Length() - 1;
377 GetLongNamePart(aName, aEntry, 1, 1, 5);
378 GetLongNamePart(aName, aEntry, 6, 14, 6);
379 GetLongNamePart(aName, aEntry, 12, 28, 2);
381 for (i = 0; i < len; i++)
388 TBool DumpDirEntry(TInt aNum, const TUint8* aEntry)
390 // Dump a single directory entry to the log. Return false if it was end of
391 // directory or an invalid entry (and don't display it).
394 TFatDirEntry* d = (TFatDirEntry*)aEntry;
397 // RDebug::Print(_L("%5d: ERASED"), aNum);
399 else if (d->IsEndOfDirectory())
401 RDebug::Print(_L("%5d: END-OF-DIRECTORY"), aNum);
404 else if ((d->Attributes() & KDirAttrLongMask) == KDirAttrLongName)
407 ExtractNameString(name, aEntry);
408 TInt ord = aEntry[0];
409 if (ord & KDirLastLongEntry)
410 RDebug::Print(_L("%5d: %-15S #%-2d LAST"), aNum, &name, ord & ~KDirLastLongEntry);
412 RDebug::Print(_L("%5d: %-15S #%-2d"), aNum, &name, ord & ~KDirLastLongEntry);
414 else if (!IsValidDirEntry(d))
416 RDebug::Print(_L("%5d: not valid"), aNum);
422 name.Copy(d->Name());
423 RDebug::Print(_L("%5d: '%S' %S cluster %d"),
424 aNum, &name, DirAttributes(d->Attributes()), d->StartCluster());
429 void DumpDirCluster(const TUint8* aData, TInt aCluster=0)
431 // Dump directory entries until end of cluster or invalid/end entry found.
435 aData += (aCluster-2) * gBytesPerCluster;
436 for (TInt i = 0; i < gBytesPerCluster; i += KSizeOfFatDirEntry)
438 if (DumpDirEntry(i/KSizeOfFatDirEntry, aData))
439 aData += KSizeOfFatDirEntry;
445 void DumpData(const TUint8* aFat, TInt aStart, TInt aEnd=-1)
447 // Dump clusters from disk (allows dumping of clusters not in our buffers).
448 // Only look at clusters marked as 'used' in the FAT. Note that if aFat is
449 // NULL the FAT entries will also be read from disk (slower but allows for ones
450 // outside our copy in memory).
453 if (aStart > gFatTestEntries)
455 if (aEnd > gFatTestEntries)
456 aEnd = gFatTestEntries;
459 RDebug::Print(_L("--------------- DATA AREA ------------------"));
460 if (aEnd > gFatTestEntries)
461 aEnd = gFatTestEntries;
462 for (TInt cluster = aStart; cluster < aEnd; cluster++)
464 if (GetFatEntry(cluster, aFat) != 0)
466 HBufC8* buf=HBufC8::New(gBytesPerCluster);
468 TPtr8 ptr=buf->Des();
469 TInt r=TheDisk.Open(TheFs,gSessionPath[0]-'A');
471 r=TheDisk.Read(ClusterToByte(cluster), ptr);
474 RDebug::Print(_L("Cluster %d @ 0x%08X:"), cluster, ClusterToByte(cluster));
475 DumpDirCluster(ptr.Ptr());
479 RDebug::Print(_L("--------------------------------------------"));
482 void DumpData(TInt aStart=0, TInt aEnd=0)
484 // Dump clusters from disk (allows dumping of clusters not in our buffers).
485 // Only look at clusters marked as 'used' in the FAT. Note that if aFat is
486 // NULL the FAT entries will also be read from disk (slower but allows for ones
487 // outside our copy in memory).
494 TInt num = (gDiskType == EFat32 ? aEnd*gEntriesPerCluster : TheBootSector.RootDirEntries());
495 TInt pos = gRootDirStart;
497 HBufC8* buf=HBufC8::New(KSizeOfFatDirEntry);
499 TPtr8 ptr=buf->Des();
500 TInt r=TheDisk.Open(TheFs,gSessionPath[0]-'A');
502 RDebug::Print(_L("--------------- ROOT DIR ------------------"));
503 for (TInt i = 0; i < num; i++)
505 r=TheDisk.Read(pos, ptr);
507 if (!DumpDirEntry(ent, ptr.Ptr()))
509 pos += KSizeOfFatDirEntry;
511 RDebug::Print(_L("-------------------------------------------"));
515 else if (aStart == 1)
518 DumpData(NULL, gFirstDataCluster, aEnd);
522 DumpData(NULL, aStart, aEnd);
526 void DumpHex(const TUint8* aData, TInt aLen)
528 // Dump a block of memory to the log in hex.
531 for (TInt base = 0; base < aLen; base += 16)
535 for (off = base; off < aLen && off < base + 16; off++)
537 buf.Append(TText(' '));
538 buf.AppendNumFixedWidth(aData[off], EHex, 2);
540 RDebug::Print(_L("%04X: %S"), base, &buf);
545 //---------------------------------------------------------------------------------------------------------------
547 static void DoReadBootSector(TFatBootSector& aBootSector)
549 TInt nRes = ReadBootSector(TheFs, CurrentDrive(), KBootSectorNum<<KDefaultSectorLog2, aBootSector);
550 test(nRes == KErrNone);
552 if(!aBootSector.IsValid())
554 test.Printf(_L("Wrong bootsector! Dump:\n"));
555 aBootSector.PrintDebugInfo();
559 // Calculate derived variables (fixed for a particular disk format)
561 if (TheBootSector.FatType() == EFat32)
565 gEndOfChain = 0x0FFFFFFF;
567 else if (TheBootSector.FatType() == EFat16)
571 gEndOfChain = 0xFFFF;
577 gEndOfChain = 0x0FFF;
580 gBytesPerCluster = TheBootSector.BytesPerSector() * TheBootSector.SectorsPerCluster();
581 gRootDirSectors = ((TheBootSector.RootDirEntries() * KSizeOfFatDirEntry + TheBootSector.BytesPerSector() - 1) /
582 TheBootSector.BytesPerSector());
583 gEntriesPerCluster = gBytesPerCluster / KSizeOfFatDirEntry;
584 gTotalSectors = (TheBootSector.TotalSectors() ? TheBootSector.TotalSectors() : TheBootSector.HugeSectors());
590 gFatSizeSectors = TheBootSector.FatSectors();
591 gRootSector = TheBootSector.ReservedSectors() + TheBootSector.NumberOfFats() * gFatSizeSectors;
592 gFirstDataSector = gRootSector + gRootDirSectors;
594 gFirstDataCluster = 2;
595 gDataStartBytes = gFirstDataSector * TheBootSector.BytesPerSector();
596 gRootDirStart = gRootSector * TheBootSector.BytesPerSector();
599 gFatSizeSectors = TheBootSector.FatSectors32();
600 gRootSector = TheBootSector.ReservedSectors() + TheBootSector.NumberOfFats() * gFatSizeSectors;
601 gFirstDataSector = gRootSector + gRootDirSectors;
603 gFirstDataCluster = 3;
604 gDataStartBytes = gFirstDataSector * TheBootSector.BytesPerSector();
605 gRootDirStart = (TheBootSector.RootClusterNum() - 2) * gBytesPerCluster + gDataStartBytes;
611 gClusterCount = (gTotalSectors - gFirstDataSector) / TheBootSector.SectorsPerCluster();
613 gFatTestEntries = MaxClusters();
614 if (gFatTestEntries > KMaxFatSize)
615 gFatTestEntries = KMaxFatSize;
619 static TInt CalcShifts(TInt aSize)
621 // Calculate the number of shifts to get >= aSize (aSize should be a power of 2
631 static TInt SectorShifts()
633 // Calculate number of shifts for sector size.
636 return(CalcShifts(TheBootSector.BytesPerSector()));
639 static TInt ClusterShifts()
641 // Calculate number of shifts for cluster size.
644 return(CalcShifts(TheBootSector.BytesPerSector()*TheBootSector.SectorsPerCluster()));
649 // Quick Format the disk
651 static void FormatPack()
655 //-- FAT32 SPC:1; for the FAT32 testing on the emulator
657 fp.iFatType = EFat32;
658 fp.iSecPerCluster = 1;
659 FormatFatDrive(TheFs, CurrentDrive(), ETrue, &fp);
662 FormatFatDrive(TheFs, CurrentDrive(), ETrue);
666 DoReadBootSector(TheBootSector);
672 static void TestReadWrite(TInt64 aPos,TInt aLen,TInt anErr)
674 // Read and write to the disk
677 TPtr8 buffer((TUint8*)pBuffer1->Ptr(),aLen);
678 test.Printf(_L("TestReadWrite pos=0x%lx,len=%d\n"),aPos,aLen);
680 if ((r=TheDisk.Read(aPos,buffer))!=anErr)
682 test.Printf(_L("ERROR: anErr=%d ret=%d\n"),anErr,r);
685 buffer.SetLength(aLen);
686 if ((r=TheDisk.Write(aPos,buffer))!=anErr)
688 test.Printf(_L("ERROR: anErr=%d ret=%d\n"),anErr,r);
693 static TInt ReadWriteWord(TInt64 aPos,TInt aMask,TInt aValue)
695 // Read 2 bytes from aPos and Write over masked bits with aValue
699 TPtr8 buffer((TUint8*)&word,sizeof(word));
701 TInt r=TheDisk.Read(aPos,buffer);
705 word&=((aValue&aMask)|~aMask);
706 word|=(aValue&aMask);
708 r=TheDisk.Write(aPos,buffer);
712 static TInt ReadWriteDWord(TInt64 aPos,TInt aMask,TInt aValue)
714 // Read 4 bytes from aPos and Write over masked bits with aValue
718 TPtr8 buffer((TUint8*)&word,sizeof(word));
720 TInt r=TheDisk.Read(aPos,buffer);
724 word&=((aValue&aMask)|~aMask);
725 word|=(aValue&aMask);
727 r=TheDisk.Write(aPos,buffer);
731 static void FatWrite(TInt aCluster,TInt aValue)
739 const TUint32 KFirstFatSectorPos = TheBootSector.FirstFatSector() * TheBootSector.BytesPerSector();
745 pos=KFirstFatSectorPos+(aCluster<<2);
749 pos=KFirstFatSectorPos+(aCluster<<1);
753 pos=KFirstFatSectorPos+aCluster+(aCluster>>1);
764 TInt r=TheDisk.Open(TheFs,CurrentDrive());
766 test(ReadWriteDWord(pos,mask,aValue)==KErrNone);
770 static void TestRwWord(TInt64 aPos,TInt anErr)
781 test.Printf(_L("Test read and write value to 0x%lx\n"),aPos);
783 if ((r=ReadWriteWord(aPos,mask,value))!=anErr)
785 test.Printf(_L("ERROR: anErr=%d, ret=%d\n"),anErr,r);
789 if (anErr==KErrNone && aPos==0)
792 TPtrC8 writebuf((TUint8*)&wBuf,sizeof(wBuf));
793 test(TheDisk.Write(aPos,writebuf)==KErrNone);
797 test.Printf(_L("Test RWW mask=%04x value%04x\n"),mask,value);
798 if ((r=ReadWriteWord(aPos,mask,value))!=anErr)
800 test.Printf(_L("ERROR: anErr=%d, ret=%d\n"),anErr,r);
804 TPtr8 readBuf((TUint8*)&rBuf,sizeof(rBuf));
805 if ((r=TheDisk.Read(aPos,readBuf))!=KErrNone)
807 test.Printf(_L("ERROR: anErr=%d, ret=%d\n"),anErr,r);
813 if (anErr==KErrNone && aPos==1)
816 TPtrC8 writebuf((TUint8*)&wBuf,sizeof(wBuf));
817 test(TheDisk.Write(aPos,writebuf)==KErrNone);
821 test.Printf(_L("Test RWW mask=%04x value%04x\n"),mask,value);
822 if ((r=ReadWriteWord(aPos,mask,value))!=anErr)
824 test.Printf(_L("ERROR: anErr=%d, ret=%d\n"),anErr,r);
828 TPtr8 readBuf((TUint8*)&rBuf,sizeof(rBuf));
829 if ((r=TheDisk.Read(aPos,readBuf))!=KErrNone)
831 test.Printf(_L("ERROR: anErr=%d, ret=%d\n"),anErr,r);
838 static void TestRwDWord(TInt64 aPos,TInt anErr)
849 test.Printf(_L("Test read and write value to 0x%lx\n"),aPos);
851 if ((r=ReadWriteDWord(aPos,mask,value))!=anErr)
853 test.Printf(_L("ERROR: anErr=%d, ret=%d\n"),anErr,r);
857 if (anErr==KErrNone && aPos==0)
860 TPtrC8 writebuf((TUint8*)&wBuf,sizeof(wBuf));
861 test(TheDisk.Write(aPos,writebuf)==KErrNone);
865 test.Printf(_L("Test RWW mask=%04x value%04x\n"),mask,value);
866 if ((r=ReadWriteDWord(aPos,mask,value))!=anErr)
868 test.Printf(_L("ERROR: anErr=%d, ret=%d\n"),anErr,r);
872 TPtr8 readBuf((TUint8*)&rBuf,sizeof(rBuf));
873 if ((r=TheDisk.Read(aPos,readBuf))!=KErrNone)
875 test.Printf(_L("ERROR: anErr=%d, ret=%d\n"),anErr,r);
878 test(rBuf==0xfe04e614);
881 if (anErr==KErrNone && aPos==1)
884 TPtrC8 writebuf((TUint8*)&wBuf,sizeof(wBuf));
885 test(TheDisk.Write(aPos,writebuf)==KErrNone);
889 test.Printf(_L("Test RWW mask=%04x value%04x\n"),mask,value);
890 if ((r=ReadWriteDWord(aPos,mask,value))!=anErr)
892 test.Printf(_L("ERROR: anErr=%d, ret=%d\n"),anErr,r);
896 TPtr8 readBuf((TUint8*)&rBuf,sizeof(rBuf));
897 if ((r=TheDisk.Read(aPos,readBuf))!=KErrNone)
899 test.Printf(_L("ERROR: anErr=%d, ret=%d\n"),anErr,r);
902 test(rBuf==0xa3a3dead);
907 static TInt ThrottleDirEntries(TInt aDirEntries, TInt aRemainder)
909 // throttle the number of entries needed, since for large cluster
910 // sizes, this can take forever (eg 2GB card -> a cluster size of 32K
911 // -> 1024 entries per cluster
912 const TInt KMaxDirEntries = 2048;
913 test(aRemainder < KMaxDirEntries);
914 TInt maxDirEntries = KMaxDirEntries - aRemainder;
916 if (aDirEntries > maxDirEntries)
918 RDebug::Print(_L("Reducing directory entries from %d to %d"), aDirEntries, maxDirEntries);
919 aDirEntries = maxDirEntries;
925 static void TestLoopedSubDir()
929 test.Printf(_L("Test looped sub-dir\n"));
931 TInt r=TheFs.MkDir(_L("\\D\\"));
932 if (r!=KErrNone && r!=KErrAlreadyExists)
933 Error(_L("Failed to make directory"),r);
934 TheFileName=_L("\\D\\");
937 TInt dirEntriesNeeded = ((TheBootSector.BytesPerSector()*TheBootSector.SectorsPerCluster()/KSizeOfFatDirEntry)-2);
938 dirEntriesNeeded = ThrottleDirEntries(dirEntriesNeeded, 2);
941 //-- generate some number of VFAT dir. entries by creating 8.3 temp. files in a lower case
942 for (i=0;i<dirEntriesNeeded;i++)
944 CreateFatEntry(TheFileName, ETrue);
947 test.Printf(_L("Test dir with no match\n"));
948 FatWrite(gFirstDataCluster,gFirstDataCluster);
949 if ((r=TheDir.Open(TheFs,_L("\\D\\nomatch"),KEntryAttMaskSupported))!=KErrNone)
950 Error(_L("Failed Directory open"),r);
951 if ((r=TheDir.Read(TheEntry))!=KErrCorrupt)
952 Error(_L("Failed Directory read"),r);
955 test.Printf(_L("Test dir with match\n"));
956 if ((r=TheDir.Open(TheFs,_L("\\D\\*.*"),KEntryAttMaskSupported))!=KErrNone)
957 Error(_L("Failed Directory open"),r);
958 if ((r=TheDir.Read(TheEntry))!=KErrNone)
959 Error(_L("Failed Directory read"),r);
962 test.Printf(_L("Test dir without loop\n"));
963 FatWrite(gFirstDataCluster,gEndOfChain);
964 if ((r=TheDir.Open(TheFs,_L("\\D\\nomatch"),KEntryAttMaskSupported))!=KErrNone)
965 Error(_L("Directory open"),r);
966 if ((r=TheDir.Read(TheEntry))!=KErrEof)
967 Error(_L("Reading empty dir returned"),r);
970 test.Printf(_L("Test dir with long filenames\n"));
973 r=TheFs.MkDir(_L("\\D\\"));
974 if (r!=KErrNone && r!=KErrAlreadyExists)
975 Error(_L("Failed to make directory"),r);
976 TheFileName=_L("\\D\\");
978 dirEntriesNeeded = ((TheBootSector.BytesPerSector()*TheBootSector.SectorsPerCluster()/KSizeOfFatDirEntry)-3);
979 dirEntriesNeeded = ThrottleDirEntries(dirEntriesNeeded, 3);
981 //-- generate some number of VFAT dir. entries by creating 8.3 temp. files in a lower case
982 for (i=0;i<dirEntriesNeeded;i++)
984 CreateFatEntry(TheFileName, ETrue);
987 MakeFile(_L("\\D\\longfileName.Long"));
989 test.Printf(_L("Test dir with no match\n"));
990 FatWrite(gFirstDataCluster,gFirstDataCluster);
991 if ((r=TheDir.Open(TheFs,_L("\\D\\nomatch"),KEntryAttMaskSupported))!=KErrNone)
992 Error(_L("Failed Directory open"),r);
993 if ((r=TheDir.Read(TheEntry))!=KErrCorrupt)
994 Error(_L("Failed Directory read"),r);
997 test.Printf(_L("Test dir with match\n"));
998 if ((r=TheDir.Open(TheFs,_L("\\D\\*.*"),KEntryAttMaskSupported))!=KErrNone)
999 Error(_L("Failed Directory open"),r);
1000 if ((r=TheDir.Read(TheEntry))!=KErrNone)
1001 Error(_L("Failed Directory read"),r);
1004 test.Printf(_L("Test dir without loop\n"));
1005 FatWrite(gFirstDataCluster,gEndOfChain);
1006 if ((r=TheDir.Open(TheFs,_L("\\D\\nomatch"),KEntryAttMaskSupported))!=KErrNone)
1007 Error(_L("Directory open"),r);
1009 #if !defined _UNICODE
1010 if ((r=TheDir.Read(TheEntry))!=KErrCorrupt)
1011 Error(_L("Reading empty dir returned"),r);
1016 static void TestLoopedFile()
1021 test.Printf(_L("Test looped file\n"));
1027 test.Next(_L("CreateFile"));
1028 test(TheFile.Replace(TheFs,_L("\\LOOPED1.TMP"),EFileRead|EFileWrite)==KErrNone);
1029 TPtr8 buf=pBuffer1->Des();
1031 test(TheFile.Write(buf,TheBootSector.BytesPerSector()-1)==KErrNone);
1034 test.Next(_L("Write 1 cluster loop"));
1035 FatWrite(gFirstDataCluster,gFirstDataCluster); /* tiny loop */
1036 if ((r=TheFile.Open(TheFs,_L("\\LOOPED1.TMP"),EFileRead|EFileWrite))!=KErrCorrupt)
1037 Error(_L("Error opening corrupt file"),r);
1038 FatWrite(gFirstDataCluster,0);
1039 if ((r=TheFile.Open(TheFs,_L("\\LOOPED1.TMP"),EFileRead|EFileWrite))!=KErrCorrupt)
1040 Error(_L("Error opening corrupt file"),r);
1041 FatWrite(gFirstDataCluster,gEndOfChain);
1042 if ((r=TheFile.Open(TheFs,_L("\\LOOPED1.TMP"),EFileRead|EFileWrite))!=KErrNone)
1043 Error(_L("Error opening file"),r);
1044 if ((r=TheFile.Write(buf,TheBootSector.BytesPerSector()*TheBootSector.SectorsPerCluster()*2-1))!=0)
1045 Error(_L("Error writing to file"),r);
1048 test.Next(_L("Write 2 cluster loop"));
1049 FatWrite(gFirstDataCluster+1,gFirstDataCluster); /* 2 cluster loop */
1050 if ((r=TheFile.Open(TheFs,_L("\\LOOPED1.TMP"),EFileRead|EFileWrite))!=KErrCorrupt)
1051 Error(_L("Error opening corrupt file"),r);
1052 FatWrite(gFirstDataCluster+1,gEndOfChain);
1053 if ((r=TheFile.Open(TheFs,_L("\\LOOPED1.TMP"),EFileRead|EFileWrite))!=KErrNone)
1054 Error(_L("Error opening file"),r);
1058 while (size < gBytesPerCluster * 500)
1060 test.Printf(_L("\rWriting %d "),size);
1061 if ((r=TheFile.Write(buf,len))!=KErrNone)
1063 if (r!=KErrDiskFull)
1064 Error(_L("File write error"),r);
1072 test.Printf(_L("\n"));
1075 RDebug::Print(_L("File created size %d"), size);
1076 TInt clust=((size-1)>>ClusterShifts())+gFirstDataCluster;
1077 FatWrite(clust,gFirstDataCluster);
1078 if ((r=TheFile.Open(TheFs,_L("\\LOOPED1.TMP"),EFileRead|EFileWrite))!=KErrCorrupt)
1079 Error(_L("Error opening corrupt file"),r);
1080 FatWrite(clust,gEndOfChain);
1081 if ((r=TheFs.Delete(_L("\\LOOPED1.TMP")))!=KErrNone)
1082 Error(_L("Error deleting file"),r);
1083 RDebug::Print(_L("File removed"));
1084 r=TheFs.CheckDisk(gSessionPath);
1088 static void TestFatEntry(TUint16 aFileSize,TInt aCorruptFatCluster)
1094 test.Printf(_L("File size=%d, cluster value=0x%x\n"),aFileSize,aCorruptFatCluster);
1097 r=TheFile.Replace(TheFs,_L("\\CORRUPT2.TMP"),EFileRead|EFileWrite);
1099 TheBuffer.SetLength(aFileSize);
1100 Mem::Fill(&TheBuffer[0],aFileSize,'A');
1101 r=TheFile.Write(TheBuffer);
1105 FatWrite(gFirstDataCluster,aCorruptFatCluster);
1108 r=TheFile.Open(TheFs,_L("\\CORRUPT2.TMP"),EFileRead|EFileWrite);
1109 test(r==KErrNone || r==KErrCorrupt);
1112 r=TheFile.Seek(ESeekStart,pos);
1114 r=TheFile.Write(TheBuffer);
1116 if ((gDriveCacheFlags & EFileCacheWriteOn) && (r == KErrNone))
1117 r = TheFile.Flush();
1119 if (r != KErrCorrupt)
1121 test.Printf(_L("Predicted error %d Actual error %d\n"),KErrCorrupt,r);
1122 Error(_L("Failed write"),r);
1127 FatWrite(gFirstDataCluster,gEndOfChain);
1130 r=TheFile.Open(TheFs,_L("\\CORRUPT2.TMP"),EFileRead|EFileWrite);
1132 r=TheFile.Seek(ESeekStart,pos);
1134 r=TheFile.Write(TheBuffer);
1136 if ((gDriveCacheFlags & EFileCacheWriteOn) && (r == KErrNone))
1137 r = TheFile.Flush();
1139 // if the file size <= cluster size then writing last cluster marker to
1140 // cluster 2 should have no effect
1141 if(aFileSize>TheBootSector.SectorsPerCluster()<<SectorShifts())
1145 test.Printf(_L("Predicted error %d Actual error %d\n"),KErrCorrupt,r);
1146 Error(_L("Failed write"),r);
1153 test.Printf(_L("Predicted error %d Actual error %d\n"),KErrNone,r);
1154 Error(_L("Failed write"),r);
1160 static void TestDirEntry(TInt anInitialSize,TInt aWriteLen,TInt aCorruptStartCluster)
1162 // Test directory entry
1165 test.Printf(_L("Initial size=%d, len=%d, start cluster=0x%x\n"),anInitialSize,aWriteLen,aCorruptStartCluster);
1169 test(TheFile.Create(TheFs,_L("\\CORRUPT1.TMP"),EFileRead|EFileWrite)==KErrNone);
1170 TheBuffer.SetLength(anInitialSize);
1171 Mem::Fill(&TheBuffer[0],anInitialSize,'A');
1172 r=TheFile.Write(TheBuffer);
1176 r=TheDisk.Open(TheFs,CurrentDrive());
1178 TPtr8 sectorBuf((TUint8*)pBuffer1->Ptr(),TheBootSector.BytesPerSector());
1179 TInt pos = gRootDirStart;
1180 r=TheDisk.Read(pos,sectorBuf);
1182 TFatDirEntry* pE=(TFatDirEntry*)pBuffer1->Ptr();
1183 while (pE->IsVFatEntry()) // UNICODE entries are VFat by definition
1186 pE->SetStartCluster(aCorruptStartCluster);
1187 test(TheDisk.Write(pos,sectorBuf)==KErrNone);
1190 //-- a small hack to avoid problems with the fact that FAT[1] entry
1191 //-- is now used for marking volume as clean. TheDisk.Close() cause volume remout and
1194 r=TheDisk.Open(TheFs,CurrentDrive());
1199 TPtr8 buffer1(pBuffer1->Des());
1200 r=TheDisk.Read(pos,buffer1);
1203 r=TheFs.Entry(_L("\\CORRUPT1.TMP"),TheEntry);
1204 test(r==KErrNone || r==KErrCorrupt);
1205 TTime saveTime=TheEntry.iModified;
1207 saveTime.HomeTime();
1209 r=TheFile.Open(TheFs,_L("\\CORRUPT1.TMP"),EFileRead|EFileWrite);
1212 TheBuffer.SetLength(aWriteLen);
1213 Mem::Fill(&TheBuffer[0],aWriteLen,'B');
1214 if ((r=TheFile.Write(TheBuffer))!=KErrCorrupt)
1216 test.Printf(_L("Predicted error %d Actual error %d\n"),KErrCorrupt,r);
1217 Error(_L("Failed write"),r);
1222 r=TheDisk.Open(TheFs,CurrentDrive());
1225 TPtr8 buffer2(pBuffer2->Des());
1226 r=TheDisk.Read(pos,buffer2);
1229 //-- this bit is dodgy. The buffers may differ because of volume finalisation stuff
1230 //-- FAT[1] and FSInfo sectors
1231 test(buffer1==buffer2);
1234 r=TheFs.SetModified(_L("\\CORRUPT1.TMP"),saveTime);
1235 test(r==KErrNone || r==KErrCorrupt);
1236 r=TheFs.Entry(_L("\\CORRUPT1.TMP"),TheEntry);
1237 test(r==KErrNone || r==KErrCorrupt);
1240 static void TestBounds()
1242 // Test reading/writing past the end of a drive
1245 test.Next(_L("Test read/write past boundaries"));
1246 test(TheFs.Volume(TheVolumeInfo,CurrentDrive())==KErrNone);
1247 TInt64 size=TheVolumeInfo.iSize;
1248 TInt r=TheDisk.Open(TheFs,CurrentDrive());
1250 TPtr8 buffer(pBuffer1->Des());
1251 TInt64 pos=size - 2*buffer.MaxLength();
1252 TInt inc=buffer.MaxLength();
1255 TPtr8 tempbuf((TUint8*)pBuffer1->Ptr(),inc);
1256 r=TheDisk.Read(pos,tempbuf);
1257 test.Printf(_L("Read %08X:%08X len %d r %d\r"), I64HIGH(pos),I64LOW(pos), inc, r);
1258 test(r==KErrNone || r==KErrCorrupt);
1270 TInt64 maxcalc= TInt64(gTotalSectors) * TInt64(TheBootSector.BytesPerSector());
1272 test.Printf(_L("\n"));
1273 test.Printf(_L("Volume size = %ld\n"), size);
1274 test.Printf(_L("RawDiskSize = %ld\n"), maxcalc);
1275 test.Printf(_L("MaxReadPos = %ld\n"), pos);
1277 TInt64 maxpos = pos;
1279 // check that the calculated raw size of the disk is equal to the MaxReadPos that
1280 // has just been discovered by trial and error
1281 test(maxcalc == maxpos);
1283 for (TInt64 bsize = 1; bsize < 8; bsize++)
1285 test.Printf(_L("\n"));
1286 test.Printf(_L("Buffer size %d\n"), bsize);
1287 for (TInt64 bpos = MAKE_TINT64(0, 0x1000); bpos < MAKE_TINT64(0x3FFFFFFF,0); bpos<<=1)
1289 TInt64 endPos = (bpos + 1);
1290 for (TInt64 lpos = bpos - bsize; lpos <= endPos; lpos++)
1292 TPtr8 temp((TUint8*) (pBuffer1->Ptr()), (TInt) bsize);
1293 TInt expect = (lpos+bsize-1 < maxpos ? KErrNone : KErrCorrupt);
1294 r=TheDisk.Read(lpos, temp);
1295 RDebug::Print(_L("Read %08X:%08X result %d \r"), I64HIGH(lpos), I64LOW(lpos), r);
1301 RDebug::Print(_L("\n"));
1303 TestReadWrite(0L,0,0);
1304 TestReadWrite(0L,1,0);
1305 TestReadWrite(pos-1,1,0);
1306 TestReadWrite(pos-0x100,0x100,0);
1307 TestReadWrite(pos-1,2,KErrCorrupt);
1308 TestReadWrite(pos-0x100,0x101,KErrCorrupt);
1309 TestReadWrite(pos-0xff,0x100,KErrCorrupt);
1310 TestReadWrite(pos,0,0);
1311 TestReadWrite(pos,1,KErrCorrupt);
1313 TestReadWrite(pos-16384,16384,0);
1314 TestReadWrite(pos-16384,16385,KErrCorrupt);
1316 TInt errVal=(pos>32768+0x100) ? KErrNone : KErrCorrupt;
1317 TestReadWrite(32768L,0x100,errVal);
1318 errVal=(pos>32768+0x101) ? KErrNone : KErrCorrupt;
1319 TestReadWrite(32768L,0x101,errVal);
1320 errVal=(pos>32768+0x1ff) ? KErrNone : KErrCorrupt;
1321 TestReadWrite(32768L,0xff,errVal);
1322 errVal=(pos>65000+0x100) ? KErrNone : KErrCorrupt;
1323 TestReadWrite(65000L,0x100,errVal);
1325 errVal=(pos>0x2000000+1) ? KErrNone : KErrCorrupt;
1326 TestReadWrite(0x2000000L,1,errVal);
1330 TestRwWord(pos-2,0);
1331 TestRwWord(pos-1,KErrCorrupt);
1332 TestRwWord(pos,KErrCorrupt);
1333 TestRwWord(pos+1,KErrCorrupt);
1339 TestRwDWord(pos-4,0);
1340 TestRwDWord(pos-3,KErrCorrupt);
1341 TestRwDWord(pos-2,KErrCorrupt);
1342 TestRwDWord(pos-1,KErrCorrupt);
1343 TestRwDWord(pos,KErrCorrupt);
1344 TestRwDWord(pos+1,KErrCorrupt);
1349 static void TestClusters()
1351 test.Next(_L("Test corrupt start cluster"));
1352 // Initial Write Corrupt
1354 TestDirEntry(1024, 513, 0);
1355 TestDirEntry( 512, 512, 0);
1356 TestDirEntry(1024, 513, 1);
1357 TestDirEntry( 512, 512, 1);
1358 TestDirEntry(1024, 513, 0xff0);
1360 test.Printf(_L("Test corrupt chain\n"));
1361 TestFatEntry(1536,0);
1362 TestFatEntry(1536,1);
1364 // TInt fatCacheSize=FatCacheSize();
1365 // TUint16 cluster16=(TUint16)(fatCacheSize/2);
1366 // TUint16 cluster12=(TUint16)((fatCacheSize/3)*2);
1367 // TestFatEntry(1536,cluster12);
1368 // TestFatEntry(1536,cluster16);
1369 TestFatEntry(1536,0xff0);
1370 // don't test when only one cluster for the file
1371 if(1536>gBytesPerCluster)
1372 TestFatEntry(1536,gEndOfChain);
1379 static void TestClusterAllocation()
1381 // Test number of clusters allocated
1384 test.Next(_L("Test number of clusters allocated is correct"));
1391 r=f.Replace(TheFs,_L("\\GOBLIN.TMP"),EFileRead|EFileWrite);
1393 f.SetSize(4*gBytesPerCluster); // 4 Clusters
1396 r=f.Replace(TheFs,_L("\\WIZARD.TMP"),EFileRead|EFileWrite);
1398 f.SetSize(5*gBytesPerCluster); // 5 Clusters
1401 r=f.Replace(TheFs,_L("\\TROLL.TMP"),EFileRead|EFileWrite);
1403 f.SetSize(3*gBytesPerCluster); // 3 Clusters
1406 r=f.Replace(TheFs,_L("\\GNOME.TMP"),EFileRead|EFileWrite);
1408 f.SetSize(10*gBytesPerCluster); // 10 Clusters
1411 r=f.Replace(TheFs,_L("\\CYCLOPS.TMP"),EFileRead|EFileWrite);
1413 f.SetSize(gBytesPerCluster); // 1 Cluster
1416 r=f.Replace(TheFs,_L("\\PIXIE.TMP"),EFileRead|EFileWrite);
1418 f.SetSize(gBytesPerCluster); // 1 Cluster
1421 r=TheDisk.Open(TheFs,CurrentDrive());
1423 TPtr8 sectorBuf((TUint8*)pBuffer1->Ptr(),TheBootSector.BytesPerSector());
1424 TInt pos = gRootDirStart;
1425 test(TheDisk.Read(pos,sectorBuf)==KErrNone);
1428 TFatDirEntry* pE=(TFatDirEntry*)pBuffer1->Ptr();
1429 while (pE->IsVFatEntry()) // UNICODE 8.3 filenames are VFAT by definition
1432 TInt cluster=pE->StartCluster();
1433 TBuf8<15> name=pE->Name();
1434 test(name==_L8("GOBLIN TMP"));
1437 while (pE->IsVFatEntry())
1440 test((pE->StartCluster()-cluster)==4);
1441 cluster=pE->StartCluster();
1443 test(name==_L8("WIZARD TMP"));
1446 while (pE->IsVFatEntry())
1449 test((pE->StartCluster()-cluster)==5);
1450 cluster=pE->StartCluster();
1452 test(name==_L8("TROLL TMP"));
1455 while (pE->IsVFatEntry())
1458 test((pE->StartCluster()-cluster)==3);
1459 cluster=pE->StartCluster();
1461 test(name==_L8("GNOME TMP"));
1464 while (pE->IsVFatEntry())
1467 test ((pE->StartCluster()-cluster)==10);
1468 cluster=pE->StartCluster();
1470 test(name==_L8("CYCLOPS TMP"));
1473 while (pE->IsVFatEntry())
1476 test((pE->StartCluster()-cluster)==1);
1478 test(name==_L8("PIXIE TMP"));
1480 r=TheFs.Delete(_L("\\GOBLIN.TMP"));
1482 r=TheFs.Delete(_L("\\WIZARD.TMP"));
1484 r=TheFs.Delete(_L("\\TROLL.TMP"));
1486 r=TheFs.Delete(_L("\\GNOME.TMP"));
1488 r=TheFs.Delete(_L("\\CYCLOPS.TMP"));
1490 r=TheFs.Delete(_L("\\PIXIE.TMP"));
1498 static void TestMakeDir(const TDesC& aName,TInt aNewClust,TInt aParentClust)
1503 test.Printf(_L("Checking cluster %02d, parent %d: \"%S\"\n"), aNewClust, aParentClust, &aName);
1505 TInt r=TheFs.MkDir(aName);
1506 test(r==KErrNone || r==KErrAlreadyExists);
1508 TInt pos=ClusterToByte(aNewClust);
1509 TPtr8 sectorBuf((TUint8*)pBuffer1->Ptr(),gBytesPerCluster);
1511 r=TheDisk.Open(TheFs,CurrentDrive());
1512 if ((r=TheDisk.Read(pos,sectorBuf))!=KErrNone)
1513 Error(_L("Reading data"),r);
1516 TFatDirEntry* pE=(TFatDirEntry*)pBuffer1->Ptr();
1517 if (pE->Name()[0]!='.' || pE->Name()[1]!=' ')
1519 while (pE->IsVFatEntry())
1521 if (pE->Name()[0]!='.' || pE->Name()[1]!=' ')
1522 Error(_L("Failed to find '.' entry"),KErrNone);
1524 if (pE->StartCluster()!=aNewClust)
1525 Error(_L("Bad directory start cluster"),KErrNone);
1527 if (pE->Name()[0]!='.' || pE->Name()[1]!='.')
1528 Error(_L("Second entry is not '..'"),KErrNone);
1529 if (pE->StartCluster() != ((aParentClust==gRootCluster)?0:aParentClust))
1530 Error(_L("Start cluster of .. is not parent directory"),KErrNone);
1535 static void TestParentDir(TBool aUseVfat)
1538 test.Next(_L("TestParentDir()"));
1540 TInt root = gRootCluster;
1541 TInt cl = gFirstDataCluster;
1546 TestMakeDir(_L("\\P1\\"), cl++, root);
1549 const TInt nDirEntries= gBytesPerCluster / KSizeOfFatDirEntry; //-- number of dir. entries to fill 1 cluster
1550 const TInt nFiles = aUseVfat ? nDirEntries/2 : nDirEntries; //-- number of 8.3 files to fill 1 cluster
1553 for (TInt i=0;i<nFiles;i++)
1555 CreateFatEntry(_L("\\P1\\"), aUseVfat);
1562 TestMakeDir(_L("\\p1\\p2\\"), cl++, p1);
1563 TestMakeDir(_L("\\p1\\p21\\"), cl++, p1);
1564 TestMakeDir(_L("\\p1\\p2\\p3\\"), cl++, p1p2);
1565 TestMakeDir(_L("\\p1\\p2\\p33\\"), cl++, p1p2);
1566 TestMakeDir(_L("\\p1\\p2\\p34\\"), cl++, p1p2);
1567 TestMakeDir(_L("\\p1\\p2\\p35\\"), cl++, p1p2);
1568 TestMakeDir(_L("\\p1\\p2\\p36\\"), cl++, p1p2);
1569 TestMakeDir(_L("\\p1\\p2\\p37\\"), cl++, p1p2);
1570 TestMakeDir(_L("\\p1\\p2\\p38\\"), cl++, p1p2);
1574 TestMakeDir(_L("\\P1\\P2\\"), cl++, p1);
1575 TestMakeDir(_L("\\P1\\P21\\"), cl++, p1);
1576 TestMakeDir(_L("\\P1\\P2\\P3\\"), cl++, p1p2);
1577 TestMakeDir(_L("\\P1\\P2\\P33\\"), cl++, p1p2);
1578 TestMakeDir(_L("\\P1\\P2\\P34\\"), cl++, p1p2);
1579 TestMakeDir(_L("\\P1\\P2\\P35\\"), cl++, p1p2);
1580 TestMakeDir(_L("\\P1\\P2\\P36\\"), cl++, p1p2);
1581 TestMakeDir(_L("\\P1\\P2\\P37\\"), cl++, p1p2);
1582 TestMakeDir(_L("\\P1\\P2\\P38\\"), cl++, p1p2);
1584 TestMakeDir(_L("\\P1\\P2\\P39\\"), cl++, p1p2);
1585 TestMakeDir(_L("\\P1\\P2\\P40\\"), cl++, p1p2);
1586 TestMakeDir(_L("\\P1\\P2\\P41\\"), cl++, p1p2);
1587 TestMakeDir(_L("\\P1\\P2\\P42\\"), cl++, p1p2);
1588 TestMakeDir(_L("\\P1\\P2\\P43\\"), cl++, p1p2);
1589 TestMakeDir(_L("\\P1\\P2\\P44\\"), cl++, p1p2);
1590 TestMakeDir(_L("\\P1\\P2\\P45\\"), cl++, p1p2);
1593 // if sectors/cluster == 1 then the directory \p1\p2\ will now have to
1594 // allocate another cluster
1595 if(TheBootSector.SectorsPerCluster()==1)
1599 TestMakeDir(_L("\\p1\\p2\\p310\\"), cl++, p1p2);
1600 TestMakeDir(_L("\\p1\\p2\\p311\\"), cl++, p1p2);
1601 TestMakeDir(_L("\\p1\\p2\\p312\\"), cl++, p1p2);
1602 TestMakeDir(_L("\\p1\\p2\\p313\\"), cl++, p1p2);
1603 TestMakeDir(_L("\\p1\\p2\\p314\\"), cl++, p1p2);
1604 TestMakeDir(_L("\\p1\\p2\\p315\\"), cl++, p1p2);
1605 TestMakeDir(_L("\\p1\\p2\\p316\\"), cl++, p1p2);
1606 TestMakeDir(_L("\\p1\\p2\\p317\\"), cl++, p1p2);
1610 TestMakeDir(_L("\\P1\\P2\\P310\\"), cl++, p1p2);
1611 TestMakeDir(_L("\\P1\\P2\\P311\\"), cl++, p1p2);
1612 TestMakeDir(_L("\\P1\\P2\\P312\\"), cl++, p1p2);
1613 TestMakeDir(_L("\\P1\\P2\\P313\\"), cl++, p1p2);
1614 TestMakeDir(_L("\\P1\\P2\\P314\\"), cl++, p1p2);
1615 TestMakeDir(_L("\\P1\\P2\\P315\\"), cl++, p1p2);
1616 TestMakeDir(_L("\\P1\\P2\\P316\\"), cl++, p1p2);
1617 TestMakeDir(_L("\\P1\\P2\\P317\\"), cl++, p1p2);
1619 TestMakeDir(_L("\\P1\\P2\\P318\\"), cl++, p1p2);
1620 TestMakeDir(_L("\\P1\\P2\\P319\\"), cl++, p1p2);
1621 TestMakeDir(_L("\\P1\\P2\\P320\\"), cl++, p1p2);
1622 TestMakeDir(_L("\\P1\\P2\\P321\\"), cl++, p1p2);
1623 TestMakeDir(_L("\\P1\\P2\\P322\\"), cl++, p1p2);
1624 TestMakeDir(_L("\\P1\\P2\\P323\\"), cl++, p1p2);
1625 TestMakeDir(_L("\\P1\\P2\\P324\\"), cl++, p1p2);
1626 TestMakeDir(_L("\\P1\\P2\\P325\\"), cl++, p1p2);
1629 // if sectors/cluster <= 2 then the directory \p1\p2\ will have to
1630 // allocate another cluster
1631 if(TheBootSector.SectorsPerCluster()<=2)
1633 TestMakeDir(_L("\\P1\\P2\\P330\\"), cl++, p1p2);
1634 TestMakeDir(_L("\\P11\\"), cl++, root);
1637 static const TInt KMaxFiles=5;
1640 // Test root dir size
1642 static void TestRoot()
1644 test.Next(_L("Test root dir size"));
1646 if (gDiskType == EFat32)
1648 test.Printf(_L("Not possible on FAT32 filesystem\n"));
1653 TInt rootEntries=TheBootSector.RootDirEntries();
1654 test.Printf(_L("Total root entries allowed = %d\n"),rootEntries);
1655 TFileName fileName[KMaxFiles]; // KMaxFiles=5 in this test
1657 TInt numberOfEntries=rootEntries;
1661 //-- generate 8.3 FAT entries, temp files created in upper-case, otherwise it will be 2 vFAT entries
1662 while(numberOfEntries--)
1664 if (numberOfEntries<KMaxFiles)
1665 CreateFatEntry(_L("\\"), EFalse, &fileName[numberOfEntries]);
1667 CreateFatEntry(_L("\\"), EFalse);
1671 r = f.Create(TheFs, _L("\\123456.78"), EFileRead|EFileWrite);
1672 test(r==KErrDirFull);
1677 for (i=0;i<KMaxFiles;i++)
1679 r=TheFs.Delete(fileName[i]);
1683 r=TheFs.SetSessionPath(_L("\\"));
1686 TInt nameLength=(KMaxFiles-1)*13; // -1 for zero terminator
1687 CreateLongName(tempName,gSeed,nameLength*2);
1688 r=f.Create(TheFs,tempName,0); // Needs 9 free entries - there are only 5 available
1689 test(r==KErrDirFull);
1690 tempName.SetLength(nameLength+1);
1691 r=f.Create(TheFs,tempName,0); // Needs 6 free entries - there are only 5 available
1692 test(r==KErrDirFull);
1693 tempName.SetLength(nameLength);
1694 r=f.Create(TheFs,tempName,0); // Needs 5 free entries - there are 5 available
1698 #if 0 // This is the old test that assumed UNICODE builds
1699 // which created VFAT entries even for uppercase 8.3 file names
1701 for (i=0;i<KMaxFiles-2;i++)
1703 r=TheFs.Delete(fileName[i]); // UNICODE build - free 6 entries (delete 3 files)
1707 r=TheFs.SetSessionPath(_L("\\"));
1710 TInt vFatUnitNameSize=13;
1711 TInt nameLength=(KMaxFiles-1)*vFatUnitNameSize-1; //
1712 CreateLongName(tempName,gSeed,nameLength*2);
1713 r=f.Create(TheFs,tempName,0); // Needs 9 free entries
1714 test(r==KErrDirFull);
1716 nameLength=(KMaxFiles)*vFatUnitNameSize;
1717 tempName.SetLength(nameLength+1);
1718 r=f.Create(TheFs,tempName,0); // Needs 7 free entries
1719 test(r==KErrDirFull);
1720 tempName.SetLength(nameLength);
1721 r=f.Create(TheFs,tempName,0); // Needs 6 free entries
1726 TheFs.Delete(tempName);
1727 tempName.SetLength(nameLength-7);
1728 r=f.Create(TheFs,tempName,0);
1732 r=f.Create(TheFs,_L("ASDF"),0);
1733 test(r==KErrDirFull);
1735 TheFs.Delete(tempName);
1736 tempName.SetLength(nameLength-15);
1737 r=f.Create(TheFs,tempName,0);
1741 tempName=_L("testname");
1742 r=f.Create(TheFs,tempName,0);
1743 test(r==KErrDirFull);
1744 tempName.UpperCase();
1745 r=f.Create(TheFs,tempName,0);
1750 r=TheFs.SetSessionPath(gSessionPath);
1754 static void TestVolumeSize()
1756 // Test the volume size is zero when empty
1759 test.Next(_L("Test the volume size"));
1762 TVolumeInfo volInfo;
1763 TInt r=TheFs.Volume(volInfo);
1765 TInt64 calcsize = MAKE_TINT64(0, gClusterCount)*gBytesPerCluster;
1766 if (volInfo.iSize > calcsize)
1768 test.Printf(_L("volInfo.iSize = %ld\n"), volInfo.iSize);
1769 test.Printf(_L("volInfo.iFree = %ld\n"), volInfo.iFree);
1770 test.Printf(_L("calculated = %ld\n"), calcsize);
1771 TInt diff = I64LOW(volInfo.iSize-calcsize);
1772 test.Printf(_L("difference = %d (%d clusters)\n"), diff, diff/gBytesPerCluster);
1775 if (gDiskType == EFat32)
1776 volInfo.iSize -= gBytesPerCluster; // root dir is part of the 'size'
1777 if (volInfo.iSize != volInfo.iFree)
1779 test.Printf(_L("volInfo.iSize = %ld\n"), volInfo.iSize);
1780 test.Printf(_L("volInfo.iFree = %ld\n"), volInfo.iFree);
1781 TInt diff = I64LOW(volInfo.iSize-volInfo.iFree);
1782 test.Printf(_L("difference = %d (%d clusters)\n"), diff, diff/gBytesPerCluster);
1791 for (i=0;i<KMaxFiles;i++)
1793 fileName=_L("\\File");
1794 fileName.AppendNum(i);
1795 r=f[i].Create(TheFs,fileName,0);
1799 TInt maxTotalSize=1048576;
1800 TInt maxFileSize=maxTotalSize/KMaxFiles;
1801 TInt maxIterations=20;
1803 while(maxIterations--)
1805 for (i=0;i<KMaxFiles;i++)
1807 TInt randSize=Math::Rand(gSeed)%maxFileSize;
1808 r=f[i].SetSize(randSize);
1811 test.Printf(_L("Countdown .. %d \r"),maxIterations);
1814 test.Printf(_L("\n"));
1818 for (i=0;i<KMaxFiles;i++)
1823 totalSize+=((size+gBytesPerCluster-1)/gBytesPerCluster)*gBytesPerCluster;
1826 r=TheFs.Volume(volInfo);
1828 if (gDiskType == EFat32)
1829 volInfo.iSize -= gBytesPerCluster; // root dir is part of the 'size'
1830 if (volInfo.iSize-volInfo.iFree!=totalSize)
1832 test.Printf(_L("volInfo.iSize = %ld\n"), volInfo.iSize);
1833 test.Printf(_L("volInfo.iFree = %ld\n"), volInfo.iFree);
1834 test.Printf(_L("totalSize = %ld\n"), totalSize);
1835 TInt diff = I64LOW(volInfo.iSize-volInfo.iFree) - totalSize;
1836 test.Printf(_L("difference = %d (%d clusters)\n"), diff, diff/gBytesPerCluster);
1838 test(volInfo.iSize-volInfo.iFree==totalSize);
1840 for (i=0;i<KMaxFiles;i++)
1843 for (i=0;i<KMaxFiles;i++)
1845 fileName=_L("\\File");
1846 fileName.AppendNum(i);
1847 r=TheFs.Delete(fileName);
1851 r=TheFs.Volume(volInfo);
1852 if (gDiskType == EFat32)
1853 volInfo.iSize -= gBytesPerCluster; // root dir is part of the 'size'
1855 test(volInfo.iSize-volInfo.iFree==0);
1857 MakeDir(gSessionPath);
1859 TInt entries=(gBytesPerCluster/KSizeOfFatDirEntry)*5-2;
1860 entries = ThrottleDirEntries(entries, 2);
1862 TInt clusters = ((entries * KSizeOfFatDirEntry) + gBytesPerCluster-1) / gBytesPerCluster;
1864 //-- create "entries" FAT dir. entries by creating 8.3 files in upper case
1867 CreateFatEntry(gSessionPath, EFalse);
1871 r=TheFs.Volume(volInfo);
1873 if (gDiskType == EFat32)
1874 volInfo.iSize -= gBytesPerCluster; // root dir is part of the 'size'
1875 test.Printf(_L("volInfo.iSize = %ld\n"), volInfo.iSize);
1876 test.Printf(_L("volInfo.iFree = %ld\n"), volInfo.iFree);
1877 if (volInfo.iSize-volInfo.iFree!=clusters*gBytesPerCluster)
1882 test(volInfo.iSize-volInfo.iFree==clusters*gBytesPerCluster);
1884 //-- create 1 FAT dir. entry
1885 CreateFatEntry(gSessionPath, EFalse);
1887 r=TheFs.Volume(volInfo);
1889 if (gDiskType == EFat32)
1890 volInfo.iSize -= gBytesPerCluster; // root dir is part of the 'size'
1891 test.Printf(_L("volInfo.iSize = %ld\n"), volInfo.iSize);
1892 test.Printf(_L("volInfo.iFree = %ld\n"), volInfo.iFree);
1893 if (volInfo.iSize-volInfo.iFree!=(clusters+1)*gBytesPerCluster)
1898 test(volInfo.iSize-volInfo.iFree==(clusters+1)*gBytesPerCluster);
1900 CFileMan* fMan=CFileMan::NewL(TheFs);
1901 r=fMan->RmDir(gSessionPath);
1904 r=TheFs.Volume(volInfo);
1906 if (gDiskType == EFat32)
1907 volInfo.iSize -= gBytesPerCluster; // root dir is part of the 'size'
1908 if (volInfo.iSize-volInfo.iFree!=0)
1913 test(volInfo.iSize-volInfo.iFree==0);
1918 // Writes a standard dos entry to the disk and checks that this can be read
1919 // (in Unicode build)
1921 static void TestUnicodeEntry()
1923 test.Next(_L("Test Unicode entry"));
1925 const TInt KDirEntrySize=32;
1928 DoReadBootSector(TheBootSector);
1929 TInt pos=gRootDirStart;
1931 TBuf8<KDirEntrySize> buffer;
1932 buffer.SetLength(KDirEntrySize);
1934 buffer.Replace(0,11,_L8("TEST1 "));
1936 TInt r=TheDisk.Open(TheFs,CurrentDrive());
1938 r=TheDisk.Write(pos,buffer);
1942 r=TheDir.Open(TheFs,_L("\\"),KEntryAttMaskSupported);
1944 r=TheDir.Read(TheEntry);
1946 test(TheEntry.iName==_L("TEST1"));
1947 r=TheDir.Read(TheEntry);
1951 r=TheFs.SetSessionPath(_L("\\"));
1954 r=TheFs.Entry(_L("TEST1"),e);
1955 if(e.iName!=_L("TEST1"))
1957 test.Printf(_L("e.iName = %S\n"),&e.iName);
1962 static TUint32 GetValue(const TPtrC8& aData, TInt aOffset, TInt aLength)
1965 while (aLength-- > 0)
1966 val = val * 256 + aData[aOffset+aLength];
1970 static void TestDiskIntegrity(TBool aTestOnly=EFalse)
1972 // Does 'sanity checking' on the BPB and other areas
1976 test.Next(_L("Test disk boot area integrity"));
1977 TInt seclen = TheBootSector.BytesPerSector();
1978 HBufC8 *bootp = HBufC8::NewL(seclen);
1979 TPtr8 boot((TUint8*)bootp, seclen);
1980 HBufC8 *backp = HBufC8::NewL(seclen);
1981 TPtr8 back((TUint8*)backp, seclen);
1982 HBufC8 *infop = HBufC8::NewL(seclen);
1983 TPtr8 info((TUint8*)bootp, seclen);
1984 TInt r=TheDisk.Open(TheFs,CurrentDrive());
1986 test.Printf(_L("Error %d opening on %C"), r, (TUint)gDriveToTest);
1988 r=TheDisk.Read(0, boot);
1990 TUint32 val = GetValue(boot, 510, 2);
1991 RDebug::Print(_L("BPB magic number = 0x%X\n"), val);
1992 test(aTestOnly || val == 0xAA55);
1996 RDebug::Print(_L("Jump %02X 0x%02X\n"), boot[0], boot[1]);
1997 test(aTestOnly || boot[2] == 0x90);
2000 RDebug::Print(_L("Jump %02X 0x%02X%02X\n"), boot[0], boot[2], boot[1]);
2003 RDebug::Print(_L("Invalid boot start: %02X %02X %02X\n"), boot[0], boot[1], boot[2]);
2009 test(aTestOnly || TheBootSector.ReservedSectors() >= 1);
2010 test.Printf(_L("BPB sector OK\n"));
2013 test(aTestOnly || TheBootSector.ReservedSectors() >= 1);
2014 test.Printf(_L("BPB sector OK\n"));
2017 test(aTestOnly || TheBootSector.ReservedSectors() >= 1);
2018 test(aTestOnly || TheBootSector.ReservedSectors() > TheBootSector.BkBootRecSector());
2019 test(aTestOnly || TheBootSector.ReservedSectors() > TheBootSector.FSInfoSectorNum());
2020 test.Printf(_L("BPB sector OK\n"));
2021 if (TheBootSector.BkBootRecSector() > 0)
2023 r=TheDisk.Read(TheBootSector.BkBootRecSector()*seclen, back);
2024 test(aTestOnly || r==KErrNone);
2027 RDebug::Print(_L("Boot sector != backup\n"));
2028 RDebug::Print(_L("Sector 0: Boot sector\n"));
2029 DumpHex(boot.Ptr(), seclen);
2030 RDebug::Print(_L("Sector %d: Backup sector\n"), TheBootSector.BkBootRecSector());
2031 DumpHex(back.Ptr(), seclen);
2034 test.Printf(_L("Backup BPB sector OK\n"));
2037 test.Printf(_L("Backup BPB not present\n"));
2038 if (TheBootSector.FSInfoSectorNum() > 0)
2040 r=TheDisk.Read(TheBootSector.FSInfoSectorNum()*seclen, info);
2041 test(aTestOnly || r==KErrNone);
2042 // Test the 'magic numbers' (signatures) as specified
2043 val = GetValue(info, 0, 4);
2044 RDebug::Print(_L("FSI signature 1 = 0x%X\n"), val);
2045 test(aTestOnly || val == 0x41615252);
2046 val = GetValue(info, 484, 4);
2047 RDebug::Print(_L("FSI signature 2 = 0x%X\n"), val);
2048 test(aTestOnly || val == 0x61417272);
2049 val = GetValue(info, 508, 4);
2050 RDebug::Print(_L("FSI magic number = 0x%X\n"), val);
2051 test(aTestOnly || val == 0xAA550000);
2052 // Check the last known free count and the next free cluster value. If
2053 // they are not calculated they should be 0xFFFFFFFF, otherwise must be
2054 // less than the number of clusters.
2055 val = GetValue(info, 488, 4);
2056 RDebug::Print(_L("FSI last free # = 0x%X\n"), val);
2057 test(aTestOnly || val == 0xFFFFFFFF || val <= (TUint32)gClusterCount);
2058 val = GetValue(info, 492, 4);
2059 RDebug::Print(_L("FSI next free # = 0x%X\n"), val);
2060 test(aTestOnly || val == 0xFFFFFFFF || val < (TUint32)gClusterCount);
2061 test.Printf(_L("FSInfo sector OK\n"));
2071 static void TestFATTableEntries()
2073 // Test that reading/writing FAT table entries preserves the upper 4 bits of data.
2076 test.Next(_L("Test reading/writing FAT table entries"));
2083 for (i=0; i <=7; i++)
2085 buf[i] = GetFatEntry(i);
2088 test.Printf(_L("First 8 FAT Entries before signature: \n"));
2089 test.Printf(_L("%08x, %08x, %08x, %08x, %08x, %08x, %08x, %08x\n"),
2090 buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
2092 for (i=0; i <=7; i++)
2097 for (i=0; i <=7; i++)
2099 buf[i] = GetFatEntry(i);
2102 test.Printf(_L("First 8 FAT Entries after signature: \n"));
2103 test.Printf(_L("%08x, %08x, %08x, %08x, %08x, %08x, %08x, %08x\n"),
2104 buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
2107 test(TheFile.Create(TheFs,_L("\\CORRUPT1.TMP"),EFileRead|EFileWrite)==KErrNone);
2109 TheBuffer.SetLength(2048);
2110 Mem::Fill(&TheBuffer[0],2048,'X');
2112 for(i=0; i<=20; i++)
2114 r = TheFile.Write(TheBuffer);
2120 for (i=8; i <=15; i++)
2122 buf[i] = GetFatEntry(i-8);
2125 test.Printf(_L("First 8 FAT Entries after file write: \n"));
2126 test.Printf(_L("%08x, %08x, %08x, %08x, %08x, %08x, %08x, %08x\n"),
2127 buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]);
2129 for (i=0; i<=7; i++)
2131 test((buf[i] & 0xF0000000) == (buf[i+8] & 0xF0000000));
2134 test.Printf(_L("Top 4 bits of first 8 FAT Entries have been preserved.\n"));
2138 //-----------------------------------------------------------------------------
2140 Test that FAT[0] and FAT[1] just after formatting are compliant to FAT specs.
2141 So that this test step shall be called just after the volume formatted.
2143 static void TestFirst2FatEntries()
2145 test.Next(_L("Test FAT[0] and FAT[1] after formatting"));
2148 TBuf8<8> fat1Buf; //-- buffer for FAT[0] & FAT[1] read from 1st FAT copy
2149 TBuf8<8> fatBufCurr;
2151 //-- read first several FAT entries from FAT1
2152 const TUint32 posFat1Start = TheBootSector.FirstFatSector() * TheBootSector.BytesPerSector();
2153 const TUint32 fatSize = TheBootSector.TotalFatSectors() * TheBootSector.BytesPerSector();
2154 const TInt numFATs = TheBootSector.NumberOfFats();
2157 nRes = MediaRawRead(TheFs, CurrentDrive(), posFat1Start, 8, fat1Buf);
2158 test(nRes==KErrNone);
2162 //----------- FAT12 ---------------------
2165 fat1Buf.SetLength(3); //-- FAT12 entry occupies 1.5 bytes
2166 test.Printf(_L("FAT12, first 2 entries: %x %x %x\n"), fat1Buf[0], fat1Buf[1], fat1Buf[2]);
2168 test(fat1Buf[0]==0xF8 && fat1Buf[1]==0xFF && fat1Buf[2]==0xFF); //-- see FAT specs, these are first 2 entries
2170 //-- test that all copies of FAT have the same values in FAT[0] & FAT[1]
2171 for(TInt i=1; i<numFATs; ++i)
2173 nRes = MediaRawRead(TheFs, CurrentDrive(), posFat1Start + i*fatSize, 8, fatBufCurr);
2174 test(nRes==KErrNone);
2176 fatBufCurr.SetLength(3);
2178 if(fatBufCurr != fat1Buf)
2180 test.Printf(_L("1st 2 FAT entries in FAT#%d are different from FAT1!\n"), i);
2189 //----------- FAT16 ---------------------
2192 typedef TUint16 TFat16Entry;
2194 fat1Buf.SetLength(2*sizeof(TFat16Entry));
2195 const TFat16Entry* pFat = (const TFat16Entry*)fat1Buf.Ptr();
2197 const TFat16Entry fatEntry_0 = pFat[0]; //-- do not mask entries
2198 const TFat16Entry fatEntry_1 = pFat[1]; //-- do not mask entries
2200 test.Printf(_L("FAT16[0]=0x%x, FAT16[1]=0x%x\n"), fatEntry_0, fatEntry_1);
2202 test(fatEntry_0 == 0xFFF8); //-- see FAT specs
2203 test(fatEntry_1 == 0xFFFF); //-- the volume shall be clean just after the formatting. It can be 0x7FFF if a write to the volume occured.
2205 //-- test that all copies of FAT have the same values in FAT[0] & FAT[1]
2206 for(TInt i=1; i<numFATs; ++i)
2208 nRes = MediaRawRead(TheFs, CurrentDrive(), posFat1Start + i*fatSize, 8, fatBufCurr);
2209 test(nRes==KErrNone);
2211 fatBufCurr.SetLength(2*sizeof(TFat16Entry));
2213 if(fatBufCurr != fat1Buf)
2215 test.Printf(_L("1st 2 FAT entries in FAT#%d are different from FAT1!\n"), i);
2223 //----------- FAT32 ---------------------
2226 typedef TUint32 TFat32Entry;
2228 fat1Buf.SetLength(2*sizeof(TFat32Entry));
2229 const TFat32Entry* pFat = (const TFat32Entry*)fat1Buf.Ptr();
2231 const TFat32Entry fatEntry_0 = pFat[0]; //-- do not mask entries
2232 const TFat32Entry fatEntry_1 = pFat[1]; //-- do not mask entries
2234 test.Printf(_L("FAT32[0]=0x%x, FAT32[1]=0x%x\n"), fatEntry_0, fatEntry_1);
2236 test(fatEntry_0 == 0x0FFFFFF8); //-- see FAT specs
2237 test(fatEntry_1 == 0x0FFFFFFF); //-- the volume shall be clean just after the formatting. It can be 0x07FFFFFF if a write to the volume occured.
2239 //-- test that all copies of FAT have the same values in FAT[0] & FAT[1]
2240 for(TInt i=1; i<numFATs; ++i)
2242 nRes = MediaRawRead(TheFs, CurrentDrive(), posFat1Start + i*fatSize, 8, fatBufCurr);
2243 test(nRes==KErrNone);
2245 fatBufCurr.SetLength(2*sizeof(TFat32Entry));
2247 if(fatBufCurr != fat1Buf)
2249 test.Printf(_L("1st 2 FAT entries in FAT#%d are different from FAT1!\n"), i);
2260 };//switch(gDiskType)
2268 Exhaustive test of Data alignmemnt calculation
2269 in this code the function
2270 TInt TFatAlignment::AdjustFirstDataSectorAlignment(TInt aBlockSize)
2271 should be exactly the same as
2272 TInt CFatFormatCB::AdjustFirstDataSectorAlignment(TInt aBlockSize)
2277 enum {KDefFatResvdSec = 1, KDefFat32ResvdSec = 32}; ///< default number of FAT32 reserved sectors
2280 void Init(TBool aFat32, TInt aNumberOfFats, TInt aMaxDiskSectors, TInt aSectorsPerCluster, TInt aRootDirEntries);
2281 TUint32 MaxFat32Sectors() const;
2282 TInt MaxFat16Sectors() const;
2283 TInt MaxFat12Sectors() const;
2284 TUint32 RootDirSectors() const;
2285 TInt FirstDataSector() const;
2286 TBool Is32BitFat() const;
2287 TBool Is16BitFat() const;
2289 TInt AdjustFirstDataSectorAlignment(TInt aBlockSize);
2292 TInt iBytesPerSector;
2294 TInt iMaxDiskSectors;
2295 TInt iSectorsPerCluster;
2296 TInt iReservedSectors;
2297 TInt iSectorsPerFat;
2298 TInt iRootDirEntries;
2300 TBool iFat32; // 0 = FAT16, 1 = FAT32
2301 TInt iMaxIterations;
2304 TFatAlignment::TFatAlignment()
2309 void TFatAlignment::Init(TBool aFat32, TInt aNumberOfFats, TInt aMaxDiskSectors, TInt aSectorsPerCluster, TInt aRootDirEntries)
2311 iBytesPerSector = 512;
2313 iNumberOfFats = aNumberOfFats;
2314 iMaxDiskSectors = aMaxDiskSectors;
2315 iSectorsPerCluster = aSectorsPerCluster;
2316 iRootDirEntries = aRootDirEntries;
2318 iReservedSectors = iFat32 ? KDefFat32ResvdSec : KDefFatResvdSec;
2319 iSectorsPerFat = iFat32 ? MaxFat32Sectors() : MaxFat16Sectors();
2322 void TFatAlignment::Display()
2324 RDebug::Print(_L("iFat32 %u iNumberOfFats %u,iMaxDiskSectors %u,iSectorsPerCluster %u,iReservedSectors %u,iSectorsPerFat %u, iRootDirEntries %u, FirstDataSector %08X"),
2335 TInt TFatAlignment::MaxFat16Sectors() const
2338 TInt fatSizeInBytes=(2*iMaxDiskSectors)/iSectorsPerCluster+(iBytesPerSector-1);
2339 return(fatSizeInBytes/iBytesPerSector);
2343 TInt TFatAlignment::MaxFat12Sectors() const
2345 TInt maxDiskClusters=iMaxDiskSectors/iSectorsPerCluster;
2346 TInt fatSizeInBytes=maxDiskClusters+(maxDiskClusters>>1)+(iBytesPerSector-1);
2347 return(fatSizeInBytes/iBytesPerSector);
2351 TUint32 TFatAlignment::MaxFat32Sectors() const
2353 TUint32 calc1 = iMaxDiskSectors - iReservedSectors;
2354 TUint32 calc2 = (256 * iSectorsPerCluster) + iNumberOfFats;
2356 return (calc1 + (calc2 - 1))/calc2;
2361 @return Number of sectors in root directory. 0 for FAT32
2363 TUint32 TFatAlignment::RootDirSectors() const
2365 const TInt KSizeOfFatDirEntry =32; ///< Size in bytes of a Fat directry entry
2367 return ( (iRootDirEntries * KSizeOfFatDirEntry + (iBytesPerSector-1)) / iBytesPerSector );
2370 TInt TFatAlignment::FirstDataSector() const
2372 return( iReservedSectors + iNumberOfFats * iSectorsPerFat + RootDirSectors());
2375 TBool TFatAlignment::Is32BitFat() const
2380 TBool TFatAlignment::Is16BitFat() const
2388 // AdjustFirstDataSectorAlignment()
2389 // Attempts to align the first data sector on an erase block boundary by modifying the
2390 // number of reserved sectors.
2391 TInt TFatAlignment::AdjustFirstDataSectorAlignment(TInt aEraseBlockSizeInSectors)
2393 const TBool bFat16 = Is16BitFat();
2394 const TBool bFat32 = Is32BitFat();
2396 // Save these 2 values in the event of a convergence failure; this should
2397 // hopefully never happen, but we will cater for this in release mode to be safe,
2398 TInt reservedSectorsSaved = iReservedSectors;
2399 TInt sectorsPerFatSaved = iSectorsPerFat;
2401 TInt reservedSectorsOld = 0;
2404 TInt rootDirSectors = (iRootDirEntries * KSizeOfFatDirEntry + (iBytesPerSector-1)) / iBytesPerSector;
2405 TInt fatSectors = 0;
2407 TInt KMaxIterations = 10;
2409 for (n=0; n<KMaxIterations && reservedSectorsOld != iReservedSectors; n++)
2411 reservedSectorsOld = iReservedSectors;
2413 iSectorsPerFat = bFat32 ? MaxFat32Sectors() : bFat16 ? MaxFat16Sectors() : MaxFat12Sectors();
2415 fatSectors = iSectorsPerFat * iNumberOfFats;
2417 // calculate number of blocks
2418 TInt nBlocks = (iReservedSectors + fatSectors + rootDirSectors + aEraseBlockSizeInSectors-1) / aEraseBlockSizeInSectors;
2420 iReservedSectors = (nBlocks * aEraseBlockSizeInSectors) - rootDirSectors - fatSectors;
2423 ASSERT(iReservedSectors >= (TInt) (bFat32 ? KDefFat32ResvdSec : KDefFatResvdSec));
2425 if ((FirstDataSector() & (aEraseBlockSizeInSectors-1)) == 0)
2431 iReservedSectors = reservedSectorsSaved;
2432 iSectorsPerFat = sectorsPerFatSaved;
2438 void TestFirstDataSectorAlignment()
2440 test.Start(_L("Exhaustive test of data alignment calculation"));
2445 TInt iMaxDiskSectors;
2446 TInt iSectorsPerCluster;
2448 TInt iRootDirEntries;
2450 STestVal testVals[] =
2452 {2, 15720448, 32, 16*1024, 0}, // 4GB MoviNand, cluster size = 16K
2453 {2, 106496, 2, 2048, 512}, // diskSize = 54MB, = block size = 1MB
2454 {2, 1048576, 8, 2048, 0}, // diskSize = 512 MB
2455 {2, 1048578, 8, 2048, 0}, // Doesn't converge with original algorithm
2458 TFatAlignment fatAlignment;
2459 TInt numOfTests = sizeof(testVals) / sizeof(STestVal);
2460 for (TInt n=0; n<numOfTests; n++)
2462 STestVal& testVal = testVals[n];
2463 TBool fat32 = testVal.iMaxDiskSectors >= 1048576;
2467 testVal.iNumberOfFats,
2468 testVal.iMaxDiskSectors,
2469 testVal.iSectorsPerCluster,
2470 testVal.iRootDirEntries);
2471 TInt r = fatAlignment.AdjustFirstDataSectorAlignment(testVal.iBlockSize);
2472 test (r == KErrNone);
2473 fatAlignment.Display();
2476 const TInt64 KOneMByte = 1024*1024;
2477 const TInt64 KOneGByte = 1024*KOneMByte;
2478 const TInt64 KLastSizeToTest = 32*KOneGByte;
2487 for (iteration=0, diskSize = 16*KOneMByte; diskSize < KLastSizeToTest; iteration++, diskSize+=512)
2489 TInt diskSizeInSectors = (TInt) (diskSize >> 9);
2491 const TInt KMaxFAT16Entries=0xFFF0; ///< Maximum number of clusters in a Fat16 Fat table, 65520
2493 TBool fat32 = EFalse;
2494 TInt numberOfFats = 2;
2495 TInt rootDirEntries;
2496 TInt sectorsPerCluster;
2497 TInt blockSizeInSectors = 32; // 16K for FAT16
2499 if (diskSizeInSectors<4096) // < 2MB
2502 sectorsPerCluster=1;
2504 else if (diskSizeInSectors<8400) // < 4MB
2507 sectorsPerCluster=2;
2509 else if (diskSizeInSectors<16384) // < 8MB
2512 sectorsPerCluster=4;
2514 else if (diskSizeInSectors<32680) // < 16MB
2517 sectorsPerCluster=8;
2519 else if(diskSizeInSectors<1048576) // >= 16Mb - FAT16 < (1048576) 512MB
2521 TInt minSectorsPerCluster=(diskSizeInSectors+KMaxFAT16Entries-1)/KMaxFAT16Entries;
2523 sectorsPerCluster=1;
2524 while (minSectorsPerCluster>sectorsPerCluster)
2525 sectorsPerCluster<<=1;
2529 rootDirEntries=0; //this is always the case for fat32
2530 if(diskSizeInSectors < 16777216) //8GB in 512byte sectors
2531 sectorsPerCluster=8;
2532 else if(diskSizeInSectors < 33554432) //16GB in 512byte sectors
2533 sectorsPerCluster=16;
2534 else if(diskSizeInSectors < 67108864) //32GB in 512byte sectors
2535 sectorsPerCluster=32;
2537 sectorsPerCluster=64; //Anything >= 32GB uses a 32K cluster size
2538 blockSizeInSectors = 2048; // 1MB for FAT32
2549 TInt r = fatAlignment.AdjustFirstDataSectorAlignment(blockSizeInSectors);
2556 // if (diskSize % 0x08000000 == 0)
2558 // RDebug::Print(_L("Iter %10lX of %10lX"), diskSize, KLastSizeToTest);
2559 // fatAlignment.Display();
2562 RDebug::Print(_L("Total iterations %u"), iteration);
2563 RDebug::Print(_L("Max loop count %u"), fatAlignment.iMaxIterations);
2564 RDebug::Print(_L("successes %d failures %d, success rate %ld"),
2565 successes, failures, (TInt64(successes) * 100) / TInt64(successes + failures));
2566 test (failures == 0);
2571 static void TestZeroLengthFile()
2573 // Test what happens if you write more to a zero length file than
2574 // will fit in the filesystem.
2577 test.Next(_L("Test behaviour of extending a zero length file"));
2583 TVolumeInfo volInfo;
2584 r=TheFs.Volume(volInfo);
2587 TInt64 spaceToUse = volInfo.iFree - gBytesPerCluster; // whole disk except 1 cluster
2589 test.Printf(_L("spaceToUse %ld gClusterCount %d gBytesPerCluster %d\n"), spaceToUse, gClusterCount, gBytesPerCluster);
2590 test.Printf(_L("Before fill, volInfo.iSize %ld volInfo.iFree %ld\n"), volInfo.iSize, volInfo.iFree);
2595 while (spaceToUse > K1GigaByte)
2598 r=f.Temp(TheFs,_L("\\"),tempName,EFileRead|EFileWrite);
2600 r=f.SetSize(K1GigaByte);
2603 spaceToUse -= K1GigaByte;
2607 r=f.Replace(TheFs,_L("\\USESPACE.TMP"),EFileRead|EFileWrite);
2609 r=f.SetSize((TInt)spaceToUse);
2613 r=TheFs.Volume(volInfo);
2615 test.Printf(_L("After fill, volInfo.iSize %ld volInfo.iFree %ld\n"), volInfo.iSize, volInfo.iFree);
2617 test(volInfo.iFree==gBytesPerCluster); // check we have 1 cluster free
2619 r=f.Replace(TheFs,_L("\\FILE.TMP"),EFileRead|EFileWrite);
2621 r=f.SetSize(2*gBytesPerCluster); // 2 clusters (will fail since there's not space)
2622 test(r==KErrDiskFull);
2625 r=TheFs.Volume(volInfo);
2627 test(volInfo.iFree==gBytesPerCluster); // check we still have 1 cluster free
2629 r=f.Replace(TheFs,_L("\\USESPACE.TMP"),EFileRead|EFileWrite); // truncate file to 0
2633 r=TheFs.Volume(volInfo);
2635 test(volInfo.iFree==(spaceToUse+gBytesPerCluster)); // check we've freed up the space from USESPACE plus one cluster
2638 test(TheBootSector.IsValid()); //-- TheBootSector is read after formatting
2639 TInt64 rootDirpos = gRootDirStart;
2642 //-- read 1 sector of the root dir.
2643 r = MediaRawRead(TheFs, CurrentDrive(), rootDirpos, TheBootSector.BytesPerSector(), TheBuffer);
2644 test(r == KErrNone);
2646 const TFatDirEntry* pE=(TFatDirEntry*)TheBuffer.Ptr();
2647 while (tempfiles-- > 0)
2649 while (pE->IsVFatEntry())
2651 test(pE->Size()==(TUint)K1GigaByte);
2655 while (pE->IsVFatEntry())
2658 TBuf8<15> name=pE->Name();
2659 test(name==_L8("USESPACETMP"));
2660 test(pE->StartCluster()==0);
2663 while (pE->IsVFatEntry())
2667 test(name==_L8("FILE TMP"));
2668 test(pE->StartCluster()==0);
2676 // Call tests that may leave
2681 //-- init random generator
2682 rndSeed = Math::Random();
2684 //-- set up console output
2685 Fat_Test_Utils::SetConsole(test.Console());
2690 TInt r=TheFs.CharToDrive(gDriveToTest,drvNum);
2693 if (!Is_Fat(TheFs,drvNum))
2695 test.Printf(_L("CallTestsL: Skipped: test requires FAT filesystem\n"));
2700 //-- print drive information
2701 PrintDrvInfo(TheFs, drvNum);
2703 // check this is not the internal ram drive
2705 r=TheFs.Volume(v, drvNum);
2707 TBool isRamDrive = v.iDrive.iMediaAtt&KMediaAttVariableSize;
2709 gSessionPath[0] = (TText)gDriveToTest;
2710 // verify that the drive is large enough for proper testing
2711 if (v.iSize<512*1024)
2713 test.Printf(_L("CallTestsL: Skipped: test not supported on drives smaller than 512 KB\n"));
2720 test.Printf(_L("TotalSectors = %u (%u bytes)\n"),gTotalSectors,gTotalSectors*TheBootSector.BytesPerSector());
2721 test.Printf(_L("Sector size = %u\n"),TheBootSector.BytesPerSector());
2722 test.Printf(_L("Cluster size = %u sectors\n"),TheBootSector.SectorsPerCluster());
2723 test.Printf(_L("Alloc unit = %u\n"), gBytesPerCluster);
2724 test.Printf(_L("Fat is %u bit\n"), gFatBits);
2725 User::After(200000); // 1/5 secs
2728 TInt bufLen = 16*gBytesPerCluster;
2729 if (bufLen < 16*1024)
2731 pBuffer1=HBufC8::NewL(bufLen);
2732 pBuffer2=HBufC8::NewL(bufLen);
2734 if (pBuffer1==NULL || pBuffer2==NULL)
2735 Error(_L("OOM"),KErrNoMemory);
2738 pBuffer1->Des().Zero();
2739 pBuffer1->Des().SetLength(bufLen);
2741 pBuffer2->Des().Zero();
2742 pBuffer2->Des().SetLength(bufLen);
2746 User::After(200000); // 1/5 secs
2747 test.Printf(_L("*** Tests not valid on internal ram drive %C:\n"), (TUint)gDriveToTest);
2748 User::After(200000); // 1/5 secs
2752 TestZeroLengthFile();
2754 #if defined(__WINS__)
2755 TestFirstDataSectorAlignment();
2758 TestFirst2FatEntries();
2760 TestDiskIntegrity();
2767 TestClusterAllocation();
2770 TestParentDir(EFalse); // Test without VFAT entries
2771 TestParentDir(ETrue); // Test with VFAT entries
2775 TestFATTableEntries();
2786 Generate unique temp file name in upper (FAT entry) or lower case (2 VFAT entries)
2787 @param aFN descriptor for the file name
2788 @param aUpperCase if ETrue, the file name will be in upper case, in a lower case otherwise.
2791 void GenerateTmpFileName(TDes& aFN, TBool aUpperCase)
2793 const TInt rnd = Math::Rand(rndSeed);
2795 aFN.Format(_L("%08x.tmp"), rnd);
2805 Create FAT or VFAT entry in a speciified directory
2807 @param aDir specifies the directory where enntry will be created
2808 @param aVFatEntry if true, VFAT entry will be created (2 FAT entries, actually), otherwise - FAT entry
2809 @param apFileName in !=NULL there will be placed the name of the file created
2811 void CreateFatEntry(const TDesC& aDir, TBool aVFatEntry, TDes *apFileName/*=NULL*/)
2819 GenerateTmpFileName(tmpFN, !aVFatEntry); //-- generates 8.3 file name FAT (1 entry) or VFAT (2 entries)
2820 tmpFN.Insert(0, aDir);
2822 nRes = file.Create(TheFs, tmpFN, EFileRead|EFileWrite);
2824 if(nRes == KErrAlreadyExists)
2825 continue; //-- current random name generator isn't perfect...
2827 if(nRes != KErrNone)
2828 Error(_L("Error creating a file"),nRes);
2832 }while(nRes != KErrNone);
2835 *apFileName = tmpFN;