diff -r 000000000000 -r a41df078684a kerneltest/f32test/filesystem/fat/b_fat32.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kerneltest/f32test/filesystem/fat/b_fat32.cpp Mon Oct 19 15:55:17 2009 +0100 @@ -0,0 +1,2839 @@ +// Copyright (c) 1996-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of the License "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// f32test\server\b_fat32.cpp +// +// + +#include +#include +#include + +#include "fat_utils.h" +#include "t_server.h" + +using namespace Fat_Test_Utils; + + +RTest test(_L("B_FAT32")); + +static RRawDisk TheDisk; +static RFile TheFile; +static RDir TheDir; +static TEntry TheEntry; +static TFileName TheFileName; +static TBuf<16> TheDrive; + +static HBufC8* pBuffer1=NULL; +static HBufC8* pBuffer2=NULL; +static TBuf8<0x800> TheBuffer; +static TEntry TheFileInfo; +static TVolumeInfo TheVolumeInfo; +static TBuf<8> ThePddName; +static TFatBootSector TheBootSector; + +static TInt64 rndSeed; +static TFatType gDiskType = EInvalid; + +static TInt gFatBits = 0; +static TInt gBytesPerCluster; +static TInt gEntriesPerCluster; +static TInt gDataStartBytes; +static TInt gRootDirSectors; +static TInt gTotalSectors; +static TInt gRootDirStart; +static TInt gRootSector; +static TInt gRootCluster; +static TInt gFatTestEntries; +static TInt gFatSizeSectors; +static TInt gFirstDataSector; +static TInt gFirstDataCluster; +static TInt gClusterCount; +static TInt gEndOfChain; // for FAT12/16/32 + +const TInt KMaxFatEntries = 2048; +const TInt KMaxFatSize = KMaxFatEntries * 4; +const TInt KDirAttrReadOnly = 0x01; +const TInt KDirAttrHidden = 0x02; +const TInt KDirAttrSystem = 0x04; +const TInt KDirAttrVolumeId = 0x08; +const TInt KDirAttrDirectory = 0x10; +const TInt KDirAttrArchive = 0x20; +const TInt KDirAttrLongName = KDirAttrReadOnly | KDirAttrHidden | KDirAttrSystem | KDirAttrVolumeId; +const TInt KDirAttrLongMask = KDirAttrLongName | KDirAttrDirectory | KDirAttrArchive; +const TInt KDirLastLongEntry = 0x40; + +void CreateFatEntry(const TDesC& aDir, TBool aVFatEntry, TDes *apFileName=NULL); + +#define Error(aMess,aErr) PutError(__FILE__, __LINE__, aMess,aErr) +static void PutError(const char* aFile, TInt aLine, const TDesC& aMessage,TInt anErr) + { + TFileName buf; + TPtrC8 ptr((const TUint8*)aFile); + buf.Copy(ptr); + test.Printf(_L("%S failed - %d\n"), &aMessage,anErr); + test.Printf(_L("In %S line %d\n"), &buf, aLine); + test(0); + } + + +// +// Position calculation and disk reading routines +// Return number of bytes into the FAT +static TInt PosInBytes(TInt aFatIndex) + { + TInt fatPosInBytes = -1; + switch (gDiskType) + { + case EFat32: + fatPosInBytes=aFatIndex<<2; + break; + case EFat16: + fatPosInBytes=aFatIndex<<1; + break; + case EFat12: + fatPosInBytes=(aFatIndex*3>>1); + break; + default: + test(0); + } + return(fatPosInBytes); + } + +static TUint32 MaxClusters() + // + // Return the number of data clusters on the disk + // + { + TUint32 totSec = (TheBootSector.TotalSectors() ? TheBootSector.TotalSectors() : TheBootSector.HugeSectors()); + TUint32 numSec = totSec - gFirstDataSector; + return numSec / TheBootSector.SectorsPerCluster(); + } + +static TInt ClusterToByte(TInt aCluster) + // + // converts cluster number to byte offset on disk + // + { + if (aCluster < 2) + return gRootDirStart; + TInt sector = (aCluster - 2) * gBytesPerCluster + gFirstDataSector * TheBootSector.BytesPerSector(); + return sector; + } + + TUint32 GetFatEntry(TUint32 aIndex, const TUint8* aFat=NULL) +// +// Read a single FAT entry from disk or FAT copy and return it +// + { + TInt pos = PosInBytes(aIndex); + + TUint8 data[4]; + TUint8* ptr = data; + + if (aFat) + ptr = (TUint8*)aFat + pos; + else + { + pos += TheBootSector.ReservedSectors() * TheBootSector.BytesPerSector(); + TInt r=TheDisk.Open(TheFs,gSessionPath[0]-'A'); + test(r==KErrNone); + TPtr8 buf(&data[0], 4); + r=TheDisk.Read(pos, buf); + test(r==KErrNone); + TheDisk.Close(); + } + + TUint32 val = 0; + switch (gDiskType) + { + case EFat32: + val = *(TUint32*)ptr; + break; + case EFat16: + val = *(TUint16*)ptr; + break; + case EFat12: + val = *(TUint16*)ptr; + if (aIndex & 1) + val >>= 4; + val &= 0xFFF; + break; + default: + test(0); + } + return val; + } + + void MarkFatEntry(TUint32 aIndex) +// +// Marks a single FAT entry by modifying it's top 4 bits to +// + { + TInt pos = PosInBytes(aIndex); + pos += TheBootSector.ReservedSectors() * TheBootSector.BytesPerSector(); + + TInt r=TheDisk.Open(TheFs,gSessionPath[0]-'A'); + test(r==KErrNone); + TUint8 data[4]; + TPtr8 buf(&data[0], 4); + r=TheDisk.Read(pos, buf); + test(r==KErrNone); + data[3] &= 0x0F; + data[3] |= 0xA0; + r=TheDisk.Write(pos, buf); + test(r==KErrNone); + TheDisk.Close(); + } + + void DumpBootSector() +// +// Display (in log) TFatBootSector structure +// + { + RDebug::Print(_L("BytesPerSector = %8d"), TheBootSector.BytesPerSector()); + RDebug::Print(_L("SectorsPerCluster = %8d (%d bytes)"), + TheBootSector.SectorsPerCluster(), gBytesPerCluster); + RDebug::Print(_L("ReservedSectors = %8d"), TheBootSector.ReservedSectors()); + RDebug::Print(_L("NumberOfFats = %8d"), TheBootSector.NumberOfFats()); + RDebug::Print(_L("RootDirEntries = %8d"), TheBootSector.RootDirEntries()); + RDebug::Print(_L("TotalSectors = %8d"), TheBootSector.TotalSectors()); + RDebug::Print(_L("MediaDescriptor = %8d"), TheBootSector.MediaDescriptor()); + RDebug::Print(_L("FatSectors = %8d"), TheBootSector.FatSectors()); + RDebug::Print(_L("SectorsPerTrack = %8d"), TheBootSector.SectorsPerTrack()); + RDebug::Print(_L("NumberOfHeads = %8d"), TheBootSector.NumberOfHeads()); + RDebug::Print(_L("HiddenSectors = %8d"), TheBootSector.HiddenSectors()); + RDebug::Print(_L("HugeSectors = %8d"), TheBootSector.HugeSectors()); + + //New for FAT32 + + if(TheBootSector.RootDirEntries() == 0) //indicates we have FAT32 volume + { + RDebug::Print(_L("FatSectors32 = %8d"), TheBootSector.FatSectors32()); + RDebug::Print(_L("FATFlags = %8d"), TheBootSector.FATFlags()); + RDebug::Print(_L("VersionNumber = %8d"), TheBootSector.VersionNumber()); + RDebug::Print(_L("RootClusterNum = %8d (0x%08X)"), + TheBootSector.RootClusterNum(), + gRootDirStart); + RDebug::Print(_L("FSInfoSectorNum = %8d (0x%08X)"), + TheBootSector.FSInfoSectorNum(), + TheBootSector.FSInfoSectorNum() * TheBootSector.BytesPerSector()); + RDebug::Print(_L("BkBootRecSector = %8d (0x%08X)"), + TheBootSector.BkBootRecSector(), + TheBootSector.BkBootRecSector() * TheBootSector.BytesPerSector()); + } + + TInt fatEntries = gFatSizeSectors*TheBootSector.BytesPerSector(); + switch (gDiskType) + { + case EFat32: + fatEntries /= 4; + break; + case EFat16: + fatEntries /= 2; + break; + case EFat12: + fatEntries *= 3; + fatEntries /= 2; + break; + default: + test(0); + } + + RDebug::Print(_L("ClusterCount = %8d (%ld bytes)"), gClusterCount, ((TInt64)gClusterCount)*gBytesPerCluster); + RDebug::Print(_L("FatEntries = %8d (%d sectors)"), fatEntries, gFatSizeSectors); + RDebug::Print(_L("RootSector = %8d (0x%08X)"), gRootSector, gRootDirStart); + RDebug::Print(_L("FirstDataSector = %8d (0x%08X)"), gFirstDataSector, gDataStartBytes); + } + + void DumpFat(const TUint8* aFat=NULL) +// +// Dump to the log all those FAT entries which are non-zero +// + { + TInt32 max = MaxClusters(); + if (max > KMaxFatEntries) + max = KMaxFatEntries; + RDebug::Print(_L("---------------- DUMP OF FAT ---------------")); + for (TInt32 i = 0; i < max; i++) + { + TInt32 val = GetFatEntry(i, aFat); + TInt32 msk = 0x0FFFFFFF; + switch (gDiskType) + { + case EFat32: + msk = 0x0FFFFFFF; + break; + case EFat16: + msk = 0xFFFF; + break; + case EFat12: + msk = 0x0FFF; + break; + default: + test(0); + } + if ((val & msk) == (0x0FFFFFFF & msk)) + RDebug::Print(_L(" %8d -> EOC"), i); + else if ((val & msk) == (0x0FFFFFF8 & msk)) + RDebug::Print(_L(" %8d -> Media"), i); + else if ((val & msk) == (0x0FFFFFF7 & msk)) + RDebug::Print(_L(" %8d -> BAD"), i); + else if (val > max) + RDebug::Print(_L(" %8d -> 0x%08X"), i, val); + else if (val != 0) + RDebug::Print(_L(" %8d -> %d"), i, val); + } + RDebug::Print(_L("--------------------------------------------")); + } + + TDes* DirAttributes(TInt aAttrib) +// +// Return a pointer to a local buffer containing the attribute letters. +// + { + static TBuf<6> str(_L("------")); + static char* atr = "RHSVDA"; + for (TInt i = 0; i < 6; i++) + if ((aAttrib >> i) & 1) + str[i] = atr[i]; + return &str; + } + + TBool IsValidDirChar(TUint8 aChar, TUint8 aMin=0x20) +// +// Test whether a character is valid as part of a short filename, aMin is to +// distinguish between first character (which can't be space) and later ones +// which can include space but nothing less. Note that E5 is a valid character +// in any position, even though it means 'erased' in the first character. +// + { + const TUint8* inval = (TUint8*)"\x22\x2A\x2B\x2C\x2F\x3A\x3B\x3C\x3D\x3E\x3F\x5B\x5C\x5D\x7C"; + if (aChar < aMin) + return EFalse; + for (const TUint8* p = inval; *p; p++) + if (aChar == *p) + return EFalse; + return ETrue; + } + + TBool IsValidDirEntry(TFatDirEntry* aDir) +// +// Test whether buffer is a valid normal directory entry +// + { + // top two bits of attributes must be zero + if (aDir->iData[11] & 0xC0) + return EFalse; + // first character must be 0x05 or greater than space + if (!IsValidDirChar(aDir->iData[0], 0x21) && aDir->iData[0] != 0x05) + return EFalse; + // other characters in name must be not less than space + for (TInt i = 1; i < 11; i++) + if (!IsValidDirChar(aDir->iData[i])) + return EFalse; + return ETrue; + } + + void GetLongNamePart(TDes16& aName, const TUint8* aEntry, TInt aPos, TInt aOffset, TInt aLength) +// +// Extract part of a long name entry into the name buffer. +// +// @param aName buffer to put name +// @param aEntry directory entry raw data +// @param aPos character in buffer to start name segment +// @param aOffset offset in directory entry of the segment +// @param aLength number of characters in the segment +// + { + for (TInt i = 0; i < aLength; i++) + { + TInt at = i * 2 + aOffset; + TInt ch = aEntry[at] + aEntry[at+1] * 256; + aName[aPos++] = TText(ch); + } + } + + void ExtractNameString(TDes16& aName, const TUint8* aEntry) +// +// Extract a long name part from a directory entry, truncate it at the first +// NUL (0) character and put quotes round it. +// + { + aName.SetLength(15); + TInt len = aName.Length() - 1; + TText qu = '\''; + aName[0] = qu; + GetLongNamePart(aName, aEntry, 1, 1, 5); + GetLongNamePart(aName, aEntry, 6, 14, 6); + GetLongNamePart(aName, aEntry, 12, 28, 2); + TInt i; + for (i = 0; i < len; i++) + if (aName[i] == 0) + break; + aName[i++] = qu; + aName.SetLength(i); + } + + TBool DumpDirEntry(TInt aNum, const TUint8* aEntry) +// +// Dump a single directory entry to the log. Return false if it was end of +// directory or an invalid entry (and don't display it). +// + { + TFatDirEntry* d = (TFatDirEntry*)aEntry; + if (d->IsErased()) + { + // RDebug::Print(_L("%5d: ERASED"), aNum); + } + else if (d->IsEndOfDirectory()) + { + RDebug::Print(_L("%5d: END-OF-DIRECTORY"), aNum); + return EFalse; + } + else if ((d->Attributes() & KDirAttrLongMask) == KDirAttrLongName) + { + TBuf16<15> name; + ExtractNameString(name, aEntry); + TInt ord = aEntry[0]; + if (ord & KDirLastLongEntry) + RDebug::Print(_L("%5d: %-15S #%-2d LAST"), aNum, &name, ord & ~KDirLastLongEntry); + else + RDebug::Print(_L("%5d: %-15S #%-2d"), aNum, &name, ord & ~KDirLastLongEntry); + } + else if (!IsValidDirEntry(d)) + { + RDebug::Print(_L("%5d: not valid"), aNum); + return EFalse; + } + else + { + TBuf<11> name; + name.Copy(d->Name()); + RDebug::Print(_L("%5d: '%S' %S cluster %d"), + aNum, &name, DirAttributes(d->Attributes()), d->StartCluster()); + } + return ETrue; + } + + void DumpDirCluster(const TUint8* aData, TInt aCluster=0) +// +// Dump directory entries until end of cluster or invalid/end entry found. +// + { + if (aCluster > 2) + aData += (aCluster-2) * gBytesPerCluster; + for (TInt i = 0; i < gBytesPerCluster; i += KSizeOfFatDirEntry) + { + if (DumpDirEntry(i/KSizeOfFatDirEntry, aData)) + aData += KSizeOfFatDirEntry; + else + break; + } + } + + void DumpData(const TUint8* aFat, TInt aStart, TInt aEnd=-1) +// +// Dump clusters from disk (allows dumping of clusters not in our buffers). +// Only look at clusters marked as 'used' in the FAT. Note that if aFat is +// NULL the FAT entries will also be read from disk (slower but allows for ones +// outside our copy in memory). +// + { + if (aStart > gFatTestEntries) + return; + if (aEnd > gFatTestEntries) + aEnd = gFatTestEntries; + if (aEnd <= 0) + aEnd = aStart + 1; + RDebug::Print(_L("--------------- DATA AREA ------------------")); + if (aEnd > gFatTestEntries) + aEnd = gFatTestEntries; + for (TInt cluster = aStart; cluster < aEnd; cluster++) + { + if (GetFatEntry(cluster, aFat) != 0) + { + HBufC8* buf=HBufC8::New(gBytesPerCluster); + test(buf!=NULL); + TPtr8 ptr=buf->Des(); + TInt r=TheDisk.Open(TheFs,gSessionPath[0]-'A'); + test(r==KErrNone); + r=TheDisk.Read(ClusterToByte(cluster), ptr); + test(r==KErrNone); + TheDisk.Close(); + RDebug::Print(_L("Cluster %d @ 0x%08X:"), cluster, ClusterToByte(cluster)); + DumpDirCluster(ptr.Ptr()); + delete buf; + } + } + RDebug::Print(_L("--------------------------------------------")); + } + + void DumpData(TInt aStart=0, TInt aEnd=0) +// +// Dump clusters from disk (allows dumping of clusters not in our buffers). +// Only look at clusters marked as 'used' in the FAT. Note that if aFat is +// NULL the FAT entries will also be read from disk (slower but allows for ones +// outside our copy in memory). +// + { + if (aStart == 0) + { + if (aEnd <= 0) + aEnd = 1; + TInt num = (gDiskType == EFat32 ? aEnd*gEntriesPerCluster : TheBootSector.RootDirEntries()); + TInt pos = gRootDirStart; + TInt ent = 0; + HBufC8* buf=HBufC8::New(KSizeOfFatDirEntry); + test(buf!=NULL); + TPtr8 ptr=buf->Des(); + TInt r=TheDisk.Open(TheFs,gSessionPath[0]-'A'); + test(r==KErrNone); + RDebug::Print(_L("--------------- ROOT DIR ------------------")); + for (TInt i = 0; i < num; i++) + { + r=TheDisk.Read(pos, ptr); + test(r==KErrNone); + if (!DumpDirEntry(ent, ptr.Ptr())) + break; + pos += KSizeOfFatDirEntry; + } + RDebug::Print(_L("-------------------------------------------")); + TheDisk.Close(); + delete buf; + } + else if (aStart == 1) + { + DumpData(0, 1); + DumpData(NULL, gFirstDataCluster, aEnd); + } + else + { + DumpData(NULL, aStart, aEnd); + } + } + + void DumpHex(const TUint8* aData, TInt aLen) +// +// Dump a block of memory to the log in hex. +// + { + for (TInt base = 0; base < aLen; base += 16) + { + TBuf<16*3> buf; + TInt off; + for (off = base; off < aLen && off < base + 16; off++) + { + buf.Append(TText(' ')); + buf.AppendNumFixedWidth(aData[off], EHex, 2); + } + RDebug::Print(_L("%04X: %S"), base, &buf); + } + } + + +//--------------------------------------------------------------------------------------------------------------- + +static void DoReadBootSector(TFatBootSector& aBootSector) +{ + TInt nRes = ReadBootSector(TheFs, CurrentDrive(), KBootSectorNum< KMaxFatSize) + gFatTestEntries = KMaxFatSize; + } + + +static TInt CalcShifts(TInt aSize) +// +// Calculate the number of shifts to get >= aSize (aSize should be a power of 2 +// anyway). +// + { + TInt x=0; + while (aSize>>=1) + x++; + return(x); + } + +static TInt SectorShifts() +// +// Calculate number of shifts for sector size. +// + { + return(CalcShifts(TheBootSector.BytesPerSector())); + } + +static TInt ClusterShifts() +// +// Calculate number of shifts for cluster size. +// + { + return(CalcShifts(TheBootSector.BytesPerSector()*TheBootSector.SectorsPerCluster())); + } + + +// +// Quick Format the disk +// +static void FormatPack() + { + + #if 0 + //-- FAT32 SPC:1; for the FAT32 testing on the emulator + TFatFormatParam fp; + fp.iFatType = EFat32; + fp.iSecPerCluster = 1; + FormatFatDrive(TheFs, CurrentDrive(), ETrue, &fp); + #else + + FormatFatDrive(TheFs, CurrentDrive(), ETrue); + + #endif + + DoReadBootSector(TheBootSector); + + } + + + +static void TestReadWrite(TInt64 aPos,TInt aLen,TInt anErr) +// +// Read and write to the disk +// + { + TPtr8 buffer((TUint8*)pBuffer1->Ptr(),aLen); + test.Printf(_L("TestReadWrite pos=0x%lx,len=%d\n"),aPos,aLen); + TInt r; + if ((r=TheDisk.Read(aPos,buffer))!=anErr) + { + test.Printf(_L("ERROR: anErr=%d ret=%d\n"),anErr,r); + test(EFalse); + } + buffer.SetLength(aLen); + if ((r=TheDisk.Write(aPos,buffer))!=anErr) + { + test.Printf(_L("ERROR: anErr=%d ret=%d\n"),anErr,r); + test(EFalse); + } + } + +static TInt ReadWriteWord(TInt64 aPos,TInt aMask,TInt aValue) +// +// Read 2 bytes from aPos and Write over masked bits with aValue +// + { + TUint16 word; + TPtr8 buffer((TUint8*)&word,sizeof(word)); + + TInt r=TheDisk.Read(aPos,buffer); + if (r!=KErrNone) + return(r); + + word&=((aValue&aMask)|~aMask); + word|=(aValue&aMask); + + r=TheDisk.Write(aPos,buffer); + return(r); + } + +static TInt ReadWriteDWord(TInt64 aPos,TInt aMask,TInt aValue) +// +// Read 4 bytes from aPos and Write over masked bits with aValue +// + { + TUint32 word; + TPtr8 buffer((TUint8*)&word,sizeof(word)); + + TInt r=TheDisk.Read(aPos,buffer); + if (r!=KErrNone) + return(r); + + word&=((aValue&aMask)|~aMask); + word|=(aValue&aMask); + + r=TheDisk.Write(aPos,buffer); + return(r); + } + +static void FatWrite(TInt aCluster,TInt aValue) +// +// +// + { + TInt pos=0; + TInt mask=0; + + const TUint32 KFirstFatSectorPos = TheBootSector.FirstFatSector() * TheBootSector.BytesPerSector(); + + switch (gDiskType) + { + case EFat32: + mask=0xffffffff; + pos=KFirstFatSectorPos+(aCluster<<2); + break; + case EFat16: + mask=0xffff; + pos=KFirstFatSectorPos+(aCluster<<1); + break; + case EFat12: + mask=0x0fff; + pos=KFirstFatSectorPos+aCluster+(aCluster>>1); + if (aCluster & 1) + { + mask=0xfff0; + aValue<<=4; + } + break; + default: + test(0); + } + + TInt r=TheDisk.Open(TheFs,CurrentDrive()); + test(r==KErrNone); + test(ReadWriteDWord(pos,mask,aValue)==KErrNone); + TheDisk.Close(); + } + +static void TestRwWord(TInt64 aPos,TInt anErr) +// +// +// + { + TInt r; + TUint16 wBuf; + TUint16 rBuf; + TUint16 mask=0; + TUint16 value=0; + + test.Printf(_L("Test read and write value to 0x%lx\n"),aPos); + + if ((r=ReadWriteWord(aPos,mask,value))!=anErr) + { + test.Printf(_L("ERROR: anErr=%d, ret=%d\n"),anErr,r); + test(EFalse); + } + + if (anErr==KErrNone && aPos==0) + { + wBuf=0xff00; + TPtrC8 writebuf((TUint8*)&wBuf,sizeof(wBuf)); + test(TheDisk.Write(aPos,writebuf)==KErrNone); + + mask=0x0505; + value=0xa4a4; + test.Printf(_L("Test RWW mask=%04x value%04x\n"),mask,value); + if ((r=ReadWriteWord(aPos,mask,value))!=anErr) + { + test.Printf(_L("ERROR: anErr=%d, ret=%d\n"),anErr,r); + test(EFalse); + } + + TPtr8 readBuf((TUint8*)&rBuf,sizeof(rBuf)); + if ((r=TheDisk.Read(aPos,readBuf))!=KErrNone) + { + test.Printf(_L("ERROR: anErr=%d, ret=%d\n"),anErr,r); + test(EFalse); + } + test(rBuf==0xfe04); + } + + if (anErr==KErrNone && aPos==1) + { + wBuf=0xff00; + TPtrC8 writebuf((TUint8*)&wBuf,sizeof(wBuf)); + test(TheDisk.Write(aPos,writebuf)==KErrNone); + + mask=0xffff; + value=0xa3a3; + test.Printf(_L("Test RWW mask=%04x value%04x\n"),mask,value); + if ((r=ReadWriteWord(aPos,mask,value))!=anErr) + { + test.Printf(_L("ERROR: anErr=%d, ret=%d\n"),anErr,r); + test(EFalse); + } + + TPtr8 readBuf((TUint8*)&rBuf,sizeof(rBuf)); + if ((r=TheDisk.Read(aPos,readBuf))!=KErrNone) + { + test.Printf(_L("ERROR: anErr=%d, ret=%d\n"),anErr,r); + test(EFalse); + } + test(rBuf==0xa3a3); + } + } + +static void TestRwDWord(TInt64 aPos,TInt anErr) +// +// +// + { + TInt r; + TUint32 wBuf; + TUint32 rBuf; + TUint32 mask=0; + TUint32 value=0; + + test.Printf(_L("Test read and write value to 0x%lx\n"),aPos); + + if ((r=ReadWriteDWord(aPos,mask,value))!=anErr) + { + test.Printf(_L("ERROR: anErr=%d, ret=%d\n"),anErr,r); + test(EFalse); + } + + if (anErr==KErrNone && aPos==0) + { + wBuf=0xff00ff00; + TPtrC8 writebuf((TUint8*)&wBuf,sizeof(wBuf)); + test(TheDisk.Write(aPos,writebuf)==KErrNone); + + mask = 0x0505195c; + value = 0xa4a4c634; + test.Printf(_L("Test RWW mask=%04x value%04x\n"),mask,value); + if ((r=ReadWriteDWord(aPos,mask,value))!=anErr) + { + test.Printf(_L("ERROR: anErr=%d, ret=%d\n"),anErr,r); + test(EFalse); + } + + TPtr8 readBuf((TUint8*)&rBuf,sizeof(rBuf)); + if ((r=TheDisk.Read(aPos,readBuf))!=KErrNone) + { + test.Printf(_L("ERROR: anErr=%d, ret=%d\n"),anErr,r); + test(EFalse); + } + test(rBuf==0xfe04e614); + } + + if (anErr==KErrNone && aPos==1) + { + wBuf=0xff0000ff; + TPtrC8 writebuf((TUint8*)&wBuf,sizeof(wBuf)); + test(TheDisk.Write(aPos,writebuf)==KErrNone); + + mask=0xffffffff; + value=0xa3a3dead; + test.Printf(_L("Test RWW mask=%04x value%04x\n"),mask,value); + if ((r=ReadWriteDWord(aPos,mask,value))!=anErr) + { + test.Printf(_L("ERROR: anErr=%d, ret=%d\n"),anErr,r); + test(EFalse); + } + + TPtr8 readBuf((TUint8*)&rBuf,sizeof(rBuf)); + if ((r=TheDisk.Read(aPos,readBuf))!=KErrNone) + { + test.Printf(_L("ERROR: anErr=%d, ret=%d\n"),anErr,r); + test(EFalse); + } + test(rBuf==0xa3a3dead); + } + } + + +static TInt ThrottleDirEntries(TInt aDirEntries, TInt aRemainder) + { + // throttle the number of entries needed, since for large cluster + // sizes, this can take forever (eg 2GB card -> a cluster size of 32K + // -> 1024 entries per cluster + const TInt KMaxDirEntries = 2048; + test(aRemainder < KMaxDirEntries); + TInt maxDirEntries = KMaxDirEntries - aRemainder; + + if (aDirEntries > maxDirEntries) + { + RDebug::Print(_L("Reducing directory entries from %d to %d"), aDirEntries, maxDirEntries); + aDirEntries = maxDirEntries; + } + + return aDirEntries; + } + +static void TestLoopedSubDir() +// +// + { + test.Printf(_L("Test looped sub-dir\n")); + FormatPack(); + TInt r=TheFs.MkDir(_L("\\D\\")); + if (r!=KErrNone && r!=KErrAlreadyExists) + Error(_L("Failed to make directory"),r); + TheFileName=_L("\\D\\"); + + TInt i=0; + TInt dirEntriesNeeded = ((TheBootSector.BytesPerSector()*TheBootSector.SectorsPerCluster()/KSizeOfFatDirEntry)-2); + dirEntriesNeeded = ThrottleDirEntries(dirEntriesNeeded, 2); + + + //-- generate some number of VFAT dir. entries by creating 8.3 temp. files in a lower case + for (i=0;iDes(); + + test(TheFile.Write(buf,TheBootSector.BytesPerSector()-1)==KErrNone); + TheFile.Close(); + + test.Next(_L("Write 1 cluster loop")); + FatWrite(gFirstDataCluster,gFirstDataCluster); /* tiny loop */ + if ((r=TheFile.Open(TheFs,_L("\\LOOPED1.TMP"),EFileRead|EFileWrite))!=KErrCorrupt) + Error(_L("Error opening corrupt file"),r); + FatWrite(gFirstDataCluster,0); + if ((r=TheFile.Open(TheFs,_L("\\LOOPED1.TMP"),EFileRead|EFileWrite))!=KErrCorrupt) + Error(_L("Error opening corrupt file"),r); + FatWrite(gFirstDataCluster,gEndOfChain); + if ((r=TheFile.Open(TheFs,_L("\\LOOPED1.TMP"),EFileRead|EFileWrite))!=KErrNone) + Error(_L("Error opening file"),r); + if ((r=TheFile.Write(buf,TheBootSector.BytesPerSector()*TheBootSector.SectorsPerCluster()*2-1))!=0) + Error(_L("Error writing to file"),r); + TheFile.Close(); + + test.Next(_L("Write 2 cluster loop")); + FatWrite(gFirstDataCluster+1,gFirstDataCluster); /* 2 cluster loop */ + if ((r=TheFile.Open(TheFs,_L("\\LOOPED1.TMP"),EFileRead|EFileWrite))!=KErrCorrupt) + Error(_L("Error opening corrupt file"),r); + FatWrite(gFirstDataCluster+1,gEndOfChain); + if ((r=TheFile.Open(TheFs,_L("\\LOOPED1.TMP"),EFileRead|EFileWrite))!=KErrNone) + Error(_L("Error opening file"),r); + + TInt len=16384; + TInt size=0L; + while (size < gBytesPerCluster * 500) + { + test.Printf(_L("\rWriting %d "),size); + if ((r=TheFile.Write(buf,len))!=KErrNone) + { + if (r!=KErrDiskFull) + Error(_L("File write error"),r); + len>>=1; + if (len==0) + break; + } + else + size+=len; + } + test.Printf(_L("\n")); + TheFile.Close(); + + RDebug::Print(_L("File created size %d"), size); + TInt clust=((size-1)>>ClusterShifts())+gFirstDataCluster; + FatWrite(clust,gFirstDataCluster); + if ((r=TheFile.Open(TheFs,_L("\\LOOPED1.TMP"),EFileRead|EFileWrite))!=KErrCorrupt) + Error(_L("Error opening corrupt file"),r); + FatWrite(clust,gEndOfChain); + if ((r=TheFs.Delete(_L("\\LOOPED1.TMP")))!=KErrNone) + Error(_L("Error deleting file"),r); + RDebug::Print(_L("File removed")); + r=TheFs.CheckDisk(gSessionPath); + test(r==KErrNone); + } + +static void TestFatEntry(TUint16 aFileSize,TInt aCorruptFatCluster) +// +// Test fat entry +// + { + TInt r; + test.Printf(_L("File size=%d, cluster value=0x%x\n"),aFileSize,aCorruptFatCluster); + FormatPack(); + + r=TheFile.Replace(TheFs,_L("\\CORRUPT2.TMP"),EFileRead|EFileWrite); + test(r==KErrNone); + TheBuffer.SetLength(aFileSize); + Mem::Fill(&TheBuffer[0],aFileSize,'A'); + r=TheFile.Write(TheBuffer); + test(r==KErrNone); + TheFile.Close(); + + FatWrite(gFirstDataCluster,aCorruptFatCluster); + + TInt pos=0; + r=TheFile.Open(TheFs,_L("\\CORRUPT2.TMP"),EFileRead|EFileWrite); + test(r==KErrNone || r==KErrCorrupt); + if (r==KErrNone) + { + r=TheFile.Seek(ESeekStart,pos); + test(r==KErrNone); + r=TheFile.Write(TheBuffer); + + if ((gDriveCacheFlags & EFileCacheWriteOn) && (r == KErrNone)) + r = TheFile.Flush(); + + if (r != KErrCorrupt) + { + test.Printf(_L("Predicted error %d Actual error %d\n"),KErrCorrupt,r); + Error(_L("Failed write"),r); + } + TheFile.Close(); + } + + FatWrite(gFirstDataCluster,gEndOfChain); + + pos=0; + r=TheFile.Open(TheFs,_L("\\CORRUPT2.TMP"),EFileRead|EFileWrite); + test(r==KErrNone); + r=TheFile.Seek(ESeekStart,pos); + test(r==KErrNone); + r=TheFile.Write(TheBuffer); + + if ((gDriveCacheFlags & EFileCacheWriteOn) && (r == KErrNone)) + r = TheFile.Flush(); + + // if the file size <= cluster size then writing last cluster marker to + // cluster 2 should have no effect + if(aFileSize>TheBootSector.SectorsPerCluster()<Ptr(),TheBootSector.BytesPerSector()); + TInt pos = gRootDirStart; + r=TheDisk.Read(pos,sectorBuf); + test(r==KErrNone); + TFatDirEntry* pE=(TFatDirEntry*)pBuffer1->Ptr(); + while (pE->IsVFatEntry()) // UNICODE entries are VFat by definition + pE++; + + pE->SetStartCluster(aCorruptStartCluster); + test(TheDisk.Write(pos,sectorBuf)==KErrNone); + + + //-- a small hack to avoid problems with the fact that FAT[1] entry + //-- is now used for marking volume as clean. TheDisk.Close() cause volume remout and + //-- the data + TheDisk.Close(); + r=TheDisk.Open(TheFs,CurrentDrive()); + test(r==KErrNone); + + + pos=0; + TPtr8 buffer1(pBuffer1->Des()); + r=TheDisk.Read(pos,buffer1); + test(r==KErrNone); + TheDisk.Close(); + r=TheFs.Entry(_L("\\CORRUPT1.TMP"),TheEntry); + test(r==KErrNone || r==KErrCorrupt); + TTime saveTime=TheEntry.iModified; + if (r!=KErrNone) + saveTime.HomeTime(); + + r=TheFile.Open(TheFs,_L("\\CORRUPT1.TMP"),EFileRead|EFileWrite); + if (r==KErrNone) + { + TheBuffer.SetLength(aWriteLen); + Mem::Fill(&TheBuffer[0],aWriteLen,'B'); + if ((r=TheFile.Write(TheBuffer))!=KErrCorrupt) + { + test.Printf(_L("Predicted error %d Actual error %d\n"),KErrCorrupt,r); + Error(_L("Failed write"),r); + } + TheFile.Close(); + } + + r=TheDisk.Open(TheFs,CurrentDrive()); + test(r==KErrNone); + pos=0; + TPtr8 buffer2(pBuffer2->Des()); + r=TheDisk.Read(pos,buffer2); + test(r==KErrNone); + + //-- this bit is dodgy. The buffers may differ because of volume finalisation stuff + //-- FAT[1] and FSInfo sectors + test(buffer1==buffer2); + TheDisk.Close(); + + r=TheFs.SetModified(_L("\\CORRUPT1.TMP"),saveTime); + test(r==KErrNone || r==KErrCorrupt); + r=TheFs.Entry(_L("\\CORRUPT1.TMP"),TheEntry); + test(r==KErrNone || r==KErrCorrupt); + } + +static void TestBounds() +// +// Test reading/writing past the end of a drive +// + { + test.Next(_L("Test read/write past boundaries")); + test(TheFs.Volume(TheVolumeInfo,CurrentDrive())==KErrNone); + TInt64 size=TheVolumeInfo.iSize; + TInt r=TheDisk.Open(TheFs,CurrentDrive()); + test(r==KErrNone); + TPtr8 buffer(pBuffer1->Des()); + TInt64 pos=size - 2*buffer.MaxLength(); + TInt inc=buffer.MaxLength(); + FOREVER + { + TPtr8 tempbuf((TUint8*)pBuffer1->Ptr(),inc); + r=TheDisk.Read(pos,tempbuf); + test.Printf(_L("Read %08X:%08X len %d r %d\r"), I64HIGH(pos),I64LOW(pos), inc, r); + test(r==KErrNone || r==KErrCorrupt); + if (r==KErrNone) + pos+=inc; + else + { + inc>>=1; + if (inc==0) + break; + } + test(pos<2*size); + } + + TInt64 maxcalc= TInt64(gTotalSectors) * TInt64(TheBootSector.BytesPerSector()); + + test.Printf(_L("\n")); + test.Printf(_L("Volume size = %ld\n"), size); + test.Printf(_L("RawDiskSize = %ld\n"), maxcalc); + test.Printf(_L("MaxReadPos = %ld\n"), pos); + + TInt64 maxpos = pos; + + // check that the calculated raw size of the disk is equal to the MaxReadPos that + // has just been discovered by trial and error + test(maxcalc == maxpos); + + for (TInt64 bsize = 1; bsize < 8; bsize++) + { + test.Printf(_L("\n")); + test.Printf(_L("Buffer size %d\n"), bsize); + for (TInt64 bpos = MAKE_TINT64(0, 0x1000); bpos < MAKE_TINT64(0x3FFFFFFF,0); bpos<<=1) + { + TInt64 endPos = (bpos + 1); + for (TInt64 lpos = bpos - bsize; lpos <= endPos; lpos++) + { + TPtr8 temp((TUint8*) (pBuffer1->Ptr()), (TInt) bsize); + TInt expect = (lpos+bsize-1 < maxpos ? KErrNone : KErrCorrupt); + r=TheDisk.Read(lpos, temp); + RDebug::Print(_L("Read %08X:%08X result %d \r"), I64HIGH(lpos), I64LOW(lpos), r); + test(r==expect); + } + } + } + + RDebug::Print(_L("\n")); + + TestReadWrite(0L,0,0); + TestReadWrite(0L,1,0); + TestReadWrite(pos-1,1,0); + TestReadWrite(pos-0x100,0x100,0); + TestReadWrite(pos-1,2,KErrCorrupt); + TestReadWrite(pos-0x100,0x101,KErrCorrupt); + TestReadWrite(pos-0xff,0x100,KErrCorrupt); + TestReadWrite(pos,0,0); + TestReadWrite(pos,1,KErrCorrupt); + + TestReadWrite(pos-16384,16384,0); + TestReadWrite(pos-16384,16385,KErrCorrupt); + + TInt errVal=(pos>32768+0x100) ? KErrNone : KErrCorrupt; + TestReadWrite(32768L,0x100,errVal); + errVal=(pos>32768+0x101) ? KErrNone : KErrCorrupt; + TestReadWrite(32768L,0x101,errVal); + errVal=(pos>32768+0x1ff) ? KErrNone : KErrCorrupt; + TestReadWrite(32768L,0xff,errVal); + errVal=(pos>65000+0x100) ? KErrNone : KErrCorrupt; + TestReadWrite(65000L,0x100,errVal); + + errVal=(pos>0x2000000+1) ? KErrNone : KErrCorrupt; + TestReadWrite(0x2000000L,1,errVal); + + TestRwWord(0L,0); + TestRwWord(1L,0); + TestRwWord(pos-2,0); + TestRwWord(pos-1,KErrCorrupt); + TestRwWord(pos,KErrCorrupt); + TestRwWord(pos+1,KErrCorrupt); + + TestRwDWord(0L,0); + TestRwDWord(1L,0); + TestRwDWord(2L,0); + TestRwDWord(3L,0); + TestRwDWord(pos-4,0); + TestRwDWord(pos-3,KErrCorrupt); + TestRwDWord(pos-2,KErrCorrupt); + TestRwDWord(pos-1,KErrCorrupt); + TestRwDWord(pos,KErrCorrupt); + TestRwDWord(pos+1,KErrCorrupt); + + TheDisk.Close(); + } + +static void TestClusters() + { + test.Next(_L("Test corrupt start cluster")); + // Initial Write Corrupt + // Size Len Cluster + TestDirEntry(1024, 513, 0); + TestDirEntry( 512, 512, 0); + TestDirEntry(1024, 513, 1); + TestDirEntry( 512, 512, 1); + TestDirEntry(1024, 513, 0xff0); + + test.Printf(_L("Test corrupt chain\n")); + TestFatEntry(1536,0); + TestFatEntry(1536,1); + +// TInt fatCacheSize=FatCacheSize(); +// TUint16 cluster16=(TUint16)(fatCacheSize/2); +// TUint16 cluster12=(TUint16)((fatCacheSize/3)*2); +// TestFatEntry(1536,cluster12); +// TestFatEntry(1536,cluster16); + TestFatEntry(1536,0xff0); + // don't test when only one cluster for the file + if(1536>gBytesPerCluster) + TestFatEntry(1536,gEndOfChain); + + TestLoopedFile(); + TestLoopedSubDir(); + } + + +static void TestClusterAllocation() +// +// Test number of clusters allocated +// + { + test.Next(_L("Test number of clusters allocated is correct")); + + FormatPack(); + + RFile f; + TInt r; + + r=f.Replace(TheFs,_L("\\GOBLIN.TMP"),EFileRead|EFileWrite); + test(r==KErrNone); + f.SetSize(4*gBytesPerCluster); // 4 Clusters + f.Close(); + + r=f.Replace(TheFs,_L("\\WIZARD.TMP"),EFileRead|EFileWrite); + test(r==KErrNone); + f.SetSize(5*gBytesPerCluster); // 5 Clusters + f.Close(); + + r=f.Replace(TheFs,_L("\\TROLL.TMP"),EFileRead|EFileWrite); + test(r==KErrNone); + f.SetSize(3*gBytesPerCluster); // 3 Clusters + f.Close(); + + r=f.Replace(TheFs,_L("\\GNOME.TMP"),EFileRead|EFileWrite); + test(r==KErrNone); + f.SetSize(10*gBytesPerCluster); // 10 Clusters + f.Close(); + + r=f.Replace(TheFs,_L("\\CYCLOPS.TMP"),EFileRead|EFileWrite); + test(r==KErrNone); + f.SetSize(gBytesPerCluster); // 1 Cluster + f.Close(); + + r=f.Replace(TheFs,_L("\\PIXIE.TMP"),EFileRead|EFileWrite); + test(r==KErrNone); + f.SetSize(gBytesPerCluster); // 1 Cluster + f.Close(); + + r=TheDisk.Open(TheFs,CurrentDrive()); + test(r==KErrNone); + TPtr8 sectorBuf((TUint8*)pBuffer1->Ptr(),TheBootSector.BytesPerSector()); + TInt pos = gRootDirStart; + test(TheDisk.Read(pos,sectorBuf)==KErrNone); + TheDisk.Close(); + + TFatDirEntry* pE=(TFatDirEntry*)pBuffer1->Ptr(); + while (pE->IsVFatEntry()) // UNICODE 8.3 filenames are VFAT by definition + pE++; + + TInt cluster=pE->StartCluster(); + TBuf8<15> name=pE->Name(); + test(name==_L8("GOBLIN TMP")); + + pE++; + while (pE->IsVFatEntry()) + pE++; + + test((pE->StartCluster()-cluster)==4); + cluster=pE->StartCluster(); + name=pE->Name(); + test(name==_L8("WIZARD TMP")); + + pE++; + while (pE->IsVFatEntry()) + pE++; + + test((pE->StartCluster()-cluster)==5); + cluster=pE->StartCluster(); + name=pE->Name(); + test(name==_L8("TROLL TMP")); + + pE++; + while (pE->IsVFatEntry()) + pE++; + + test((pE->StartCluster()-cluster)==3); + cluster=pE->StartCluster(); + name=pE->Name(); + test(name==_L8("GNOME TMP")); + + pE++; + while (pE->IsVFatEntry()) + pE++; + + test ((pE->StartCluster()-cluster)==10); + cluster=pE->StartCluster(); + name=pE->Name(); + test(name==_L8("CYCLOPS TMP")); + + pE++; + while (pE->IsVFatEntry()) + pE++; + + test((pE->StartCluster()-cluster)==1); + name=pE->Name(); + test(name==_L8("PIXIE TMP")); + + r=TheFs.Delete(_L("\\GOBLIN.TMP")); + test(r==KErrNone); + r=TheFs.Delete(_L("\\WIZARD.TMP")); + test(r==KErrNone); + r=TheFs.Delete(_L("\\TROLL.TMP")); + test(r==KErrNone); + r=TheFs.Delete(_L("\\GNOME.TMP")); + test(r==KErrNone); + r=TheFs.Delete(_L("\\CYCLOPS.TMP")); + test(r==KErrNone); + r=TheFs.Delete(_L("\\PIXIE.TMP")); + test(r==KErrNone); + + FormatPack(); + + } + + +static void TestMakeDir(const TDesC& aName,TInt aNewClust,TInt aParentClust) +// +// Test make dir +// + { + test.Printf(_L("Checking cluster %02d, parent %d: \"%S\"\n"), aNewClust, aParentClust, &aName); + + TInt r=TheFs.MkDir(aName); + test(r==KErrNone || r==KErrAlreadyExists); + + TInt pos=ClusterToByte(aNewClust); + TPtr8 sectorBuf((TUint8*)pBuffer1->Ptr(),gBytesPerCluster); + + r=TheDisk.Open(TheFs,CurrentDrive()); + if ((r=TheDisk.Read(pos,sectorBuf))!=KErrNone) + Error(_L("Reading data"),r); + TheDisk.Close(); + + TFatDirEntry* pE=(TFatDirEntry*)pBuffer1->Ptr(); + if (pE->Name()[0]!='.' || pE->Name()[1]!=' ') + { + while (pE->IsVFatEntry()) + pE++; + if (pE->Name()[0]!='.' || pE->Name()[1]!=' ') + Error(_L("Failed to find '.' entry"),KErrNone); + } + if (pE->StartCluster()!=aNewClust) + Error(_L("Bad directory start cluster"),KErrNone); + pE++; + if (pE->Name()[0]!='.' || pE->Name()[1]!='.') + Error(_L("Second entry is not '..'"),KErrNone); + if (pE->StartCluster() != ((aParentClust==gRootCluster)?0:aParentClust)) + Error(_L("Start cluster of .. is not parent directory"),KErrNone); + } + + + +static void TestParentDir(TBool aUseVfat) + { + + test.Next(_L("TestParentDir()")); + + TInt root = gRootCluster; + TInt cl = gFirstDataCluster; + TInt p1 = cl; + + FormatPack(); + + TestMakeDir(_L("\\P1\\"), cl++, root); + + + const TInt nDirEntries= gBytesPerCluster / KSizeOfFatDirEntry; //-- number of dir. entries to fill 1 cluster + const TInt nFiles = aUseVfat ? nDirEntries/2 : nDirEntries; //-- number of 8.3 files to fill 1 cluster + + cl++; + for (TInt i=0;i calcsize) + { + test.Printf(_L("volInfo.iSize = %ld\n"), volInfo.iSize); + test.Printf(_L("volInfo.iFree = %ld\n"), volInfo.iFree); + test.Printf(_L("calculated = %ld\n"), calcsize); + TInt diff = I64LOW(volInfo.iSize-calcsize); + test.Printf(_L("difference = %d (%d clusters)\n"), diff, diff/gBytesPerCluster); + test(0); + } + if (gDiskType == EFat32) + volInfo.iSize -= gBytesPerCluster; // root dir is part of the 'size' + if (volInfo.iSize != volInfo.iFree) + { + test.Printf(_L("volInfo.iSize = %ld\n"), volInfo.iSize); + test.Printf(_L("volInfo.iFree = %ld\n"), volInfo.iFree); + TInt diff = I64LOW(volInfo.iSize-volInfo.iFree); + test.Printf(_L("difference = %d (%d clusters)\n"), diff, diff/gBytesPerCluster); + DumpData(); + DumpFat(); + test(0); + } + + RFile f[KMaxFiles]; + TFileName fileName; + TInt i=0; + for (i=0;iRmDir(gSessionPath); + test(r==KErrNone); + delete fMan; + r=TheFs.Volume(volInfo); + test(r==KErrNone); + if (gDiskType == EFat32) + volInfo.iSize -= gBytesPerCluster; // root dir is part of the 'size' + if (volInfo.iSize-volInfo.iFree!=0) + { + DumpFat(); + DumpData(1, 200); + } + test(volInfo.iSize-volInfo.iFree==0); + } + + +// +// Writes a standard dos entry to the disk and checks that this can be read +// (in Unicode build) +// +static void TestUnicodeEntry() + { + test.Next(_L("Test Unicode entry")); + + const TInt KDirEntrySize=32; + + FormatPack(); + DoReadBootSector(TheBootSector); + TInt pos=gRootDirStart; + + TBuf8 buffer; + buffer.SetLength(KDirEntrySize); + buffer.FillZ(); + buffer.Replace(0,11,_L8("TEST1 ")); + + TInt r=TheDisk.Open(TheFs,CurrentDrive()); + test(r==KErrNone); + r=TheDisk.Write(pos,buffer); + test(r==KErrNone); + TheDisk.Close(); + + r=TheDir.Open(TheFs,_L("\\"),KEntryAttMaskSupported); + test(r==KErrNone); + r=TheDir.Read(TheEntry); + test(r==KErrNone); + test(TheEntry.iName==_L("TEST1")); + r=TheDir.Read(TheEntry); + test(r==KErrEof); + TheDir.Close(); + + r=TheFs.SetSessionPath(_L("\\")); + test(r==KErrNone); + TEntry e; + r=TheFs.Entry(_L("TEST1"),e); + if(e.iName!=_L("TEST1")) + { + test.Printf(_L("e.iName = %S\n"),&e.iName); + test(EFalse); + } + } + +static TUint32 GetValue(const TPtrC8& aData, TInt aOffset, TInt aLength) + { + TUint32 val = 0; + while (aLength-- > 0) + val = val * 256 + aData[aOffset+aLength]; + return val; + } + +static void TestDiskIntegrity(TBool aTestOnly=EFalse) +// +// Does 'sanity checking' on the BPB and other areas +// + { + if (!aTestOnly) + test.Next(_L("Test disk boot area integrity")); + TInt seclen = TheBootSector.BytesPerSector(); + HBufC8 *bootp = HBufC8::NewL(seclen); + TPtr8 boot((TUint8*)bootp, seclen); + HBufC8 *backp = HBufC8::NewL(seclen); + TPtr8 back((TUint8*)backp, seclen); + HBufC8 *infop = HBufC8::NewL(seclen); + TPtr8 info((TUint8*)bootp, seclen); + TInt r=TheDisk.Open(TheFs,CurrentDrive()); + if (r != KErrNone) + test.Printf(_L("Error %d opening on %C"), r, (TUint)gDriveToTest); + test(r==KErrNone); + r=TheDisk.Read(0, boot); + test(r==KErrNone); + TUint32 val = GetValue(boot, 510, 2); + RDebug::Print(_L("BPB magic number = 0x%X\n"), val); + test(aTestOnly || val == 0xAA55); + switch (boot[0]) + { + case 0xEB: + RDebug::Print(_L("Jump %02X 0x%02X\n"), boot[0], boot[1]); + test(aTestOnly || boot[2] == 0x90); + break; + case 0xE9: + RDebug::Print(_L("Jump %02X 0x%02X%02X\n"), boot[0], boot[2], boot[1]); + break; + default: + RDebug::Print(_L("Invalid boot start: %02X %02X %02X\n"), boot[0], boot[1], boot[2]); + test(aTestOnly); + } + switch (gDiskType) + { + case EFat12: + test(aTestOnly || TheBootSector.ReservedSectors() >= 1); + test.Printf(_L("BPB sector OK\n")); + break; + case EFat16: + test(aTestOnly || TheBootSector.ReservedSectors() >= 1); + test.Printf(_L("BPB sector OK\n")); + break; + default: + test(aTestOnly || TheBootSector.ReservedSectors() >= 1); + test(aTestOnly || TheBootSector.ReservedSectors() > TheBootSector.BkBootRecSector()); + test(aTestOnly || TheBootSector.ReservedSectors() > TheBootSector.FSInfoSectorNum()); + test.Printf(_L("BPB sector OK\n")); + if (TheBootSector.BkBootRecSector() > 0) + { + r=TheDisk.Read(TheBootSector.BkBootRecSector()*seclen, back); + test(aTestOnly || r==KErrNone); + if (boot != back) + { + RDebug::Print(_L("Boot sector != backup\n")); + RDebug::Print(_L("Sector 0: Boot sector\n")); + DumpHex(boot.Ptr(), seclen); + RDebug::Print(_L("Sector %d: Backup sector\n"), TheBootSector.BkBootRecSector()); + DumpHex(back.Ptr(), seclen); + test(aTestOnly); + } + test.Printf(_L("Backup BPB sector OK\n")); + } + else + test.Printf(_L("Backup BPB not present\n")); + if (TheBootSector.FSInfoSectorNum() > 0) + { + r=TheDisk.Read(TheBootSector.FSInfoSectorNum()*seclen, info); + test(aTestOnly || r==KErrNone); + // Test the 'magic numbers' (signatures) as specified + val = GetValue(info, 0, 4); + RDebug::Print(_L("FSI signature 1 = 0x%X\n"), val); + test(aTestOnly || val == 0x41615252); + val = GetValue(info, 484, 4); + RDebug::Print(_L("FSI signature 2 = 0x%X\n"), val); + test(aTestOnly || val == 0x61417272); + val = GetValue(info, 508, 4); + RDebug::Print(_L("FSI magic number = 0x%X\n"), val); + test(aTestOnly || val == 0xAA550000); + // Check the last known free count and the next free cluster value. If + // they are not calculated they should be 0xFFFFFFFF, otherwise must be + // less than the number of clusters. + val = GetValue(info, 488, 4); + RDebug::Print(_L("FSI last free # = 0x%X\n"), val); + test(aTestOnly || val == 0xFFFFFFFF || val <= (TUint32)gClusterCount); + val = GetValue(info, 492, 4); + RDebug::Print(_L("FSI next free # = 0x%X\n"), val); + test(aTestOnly || val == 0xFFFFFFFF || val < (TUint32)gClusterCount); + test.Printf(_L("FSInfo sector OK\n")); + } + break; + } + TheDisk.Close(); + delete bootp; + delete backp; + delete infop; + } + +static void TestFATTableEntries() +// +// Test that reading/writing FAT table entries preserves the upper 4 bits of data. +// + { + test.Next(_L("Test reading/writing FAT table entries")); + FormatPack(); + + TUint32 buf[16]; + TInt i=0; + TInt r=KErrNone; + + for (i=0; i <=7; i++) + { + buf[i] = GetFatEntry(i); + } + + test.Printf(_L("First 8 FAT Entries before signature: \n")); + test.Printf(_L("%08x, %08x, %08x, %08x, %08x, %08x, %08x, %08x\n"), + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); + + for (i=0; i <=7; i++) + { + MarkFatEntry(i); + } + + for (i=0; i <=7; i++) + { + buf[i] = GetFatEntry(i); + } + + test.Printf(_L("First 8 FAT Entries after signature: \n")); + test.Printf(_L("%08x, %08x, %08x, %08x, %08x, %08x, %08x, %08x\n"), + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); + + + test(TheFile.Create(TheFs,_L("\\CORRUPT1.TMP"),EFileRead|EFileWrite)==KErrNone); + + TheBuffer.SetLength(2048); + Mem::Fill(&TheBuffer[0],2048,'X'); + + for(i=0; i<=20; i++) + { + r = TheFile.Write(TheBuffer); + test(r==KErrNone); + } + + TheFile.Close(); + + for (i=8; i <=15; i++) + { + buf[i] = GetFatEntry(i-8); + } + + test.Printf(_L("First 8 FAT Entries after file write: \n")); + test.Printf(_L("%08x, %08x, %08x, %08x, %08x, %08x, %08x, %08x\n"), + buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]); + + for (i=0; i<=7; i++) + { + test((buf[i] & 0xF0000000) == (buf[i+8] & 0xF0000000)); + } + + test.Printf(_L("Top 4 bits of first 8 FAT Entries have been preserved.\n")); + } + + +//----------------------------------------------------------------------------- +/** + Test that FAT[0] and FAT[1] just after formatting are compliant to FAT specs. + So that this test step shall be called just after the volume formatted. +*/ +static void TestFirst2FatEntries() +{ + test.Next(_L("Test FAT[0] and FAT[1] after formatting")); + + TInt nRes; + TBuf8<8> fat1Buf; //-- buffer for FAT[0] & FAT[1] read from 1st FAT copy + TBuf8<8> fatBufCurr; + + //-- read first several FAT entries from FAT1 + const TUint32 posFat1Start = TheBootSector.FirstFatSector() * TheBootSector.BytesPerSector(); + const TUint32 fatSize = TheBootSector.TotalFatSectors() * TheBootSector.BytesPerSector(); + const TInt numFATs = TheBootSector.NumberOfFats(); + + + nRes = MediaRawRead(TheFs, CurrentDrive(), posFat1Start, 8, fat1Buf); + test(nRes==KErrNone); + + switch(gDiskType) + { + //----------- FAT12 --------------------- + case EFat12: + { + fat1Buf.SetLength(3); //-- FAT12 entry occupies 1.5 bytes + test.Printf(_L("FAT12, first 2 entries: %x %x %x\n"), fat1Buf[0], fat1Buf[1], fat1Buf[2]); + + test(fat1Buf[0]==0xF8 && fat1Buf[1]==0xFF && fat1Buf[2]==0xFF); //-- see FAT specs, these are first 2 entries + + //-- test that all copies of FAT have the same values in FAT[0] & FAT[1] + for(TInt i=1; i>1)+(iBytesPerSector-1); + return(fatSizeInBytes/iBytesPerSector); + } + + +TUint32 TFatAlignment::MaxFat32Sectors() const + { + TUint32 calc1 = iMaxDiskSectors - iReservedSectors; + TUint32 calc2 = (256 * iSectorsPerCluster) + iNumberOfFats; + calc2 = calc2 >> 1; + return (calc1 + (calc2 - 1))/calc2; + } + + +/** + @return Number of sectors in root directory. 0 for FAT32 +*/ +TUint32 TFatAlignment::RootDirSectors() const + { + const TInt KSizeOfFatDirEntry =32; ///< Size in bytes of a Fat directry entry + + return ( (iRootDirEntries * KSizeOfFatDirEntry + (iBytesPerSector-1)) / iBytesPerSector ); + } + +TInt TFatAlignment::FirstDataSector() const + { + return( iReservedSectors + iNumberOfFats * iSectorsPerFat + RootDirSectors()); + } + +TBool TFatAlignment::Is32BitFat() const + { + return iFat32; + } + +TBool TFatAlignment::Is16BitFat() const + { + return !iFat32; + } + +#define __PRINT1 + + +// AdjustFirstDataSectorAlignment() +// Attempts to align the first data sector on an erase block boundary by modifying the +// number of reserved sectors. +TInt TFatAlignment::AdjustFirstDataSectorAlignment(TInt aEraseBlockSizeInSectors) + { + const TBool bFat16 = Is16BitFat(); + const TBool bFat32 = Is32BitFat(); + + // Save these 2 values in the event of a convergence failure; this should + // hopefully never happen, but we will cater for this in release mode to be safe, + TInt reservedSectorsSaved = iReservedSectors; + TInt sectorsPerFatSaved = iSectorsPerFat; + + TInt reservedSectorsOld = 0; + + // zero for FAT32 + TInt rootDirSectors = (iRootDirEntries * KSizeOfFatDirEntry + (iBytesPerSector-1)) / iBytesPerSector; + TInt fatSectors = 0; + + TInt KMaxIterations = 10; + TInt n; + for (n=0; n= (TInt) (bFat32 ? KDefFat32ResvdSec : KDefFatResvdSec)); + + if ((FirstDataSector() & (aEraseBlockSizeInSectors-1)) == 0) + { + return KErrNone; + } + else + { + iReservedSectors = reservedSectorsSaved; + iSectorsPerFat = sectorsPerFatSaved; + return KErrGeneral; + } + } + + +void TestFirstDataSectorAlignment() + { + test.Start(_L("Exhaustive test of data alignment calculation")); + + typedef struct + { + TInt iNumberOfFats; + TInt iMaxDiskSectors; + TInt iSectorsPerCluster; + TInt iBlockSize; + TInt iRootDirEntries; + } STestVal; + STestVal testVals[] = + { + {2, 15720448, 32, 16*1024, 0}, // 4GB MoviNand, cluster size = 16K + {2, 106496, 2, 2048, 512}, // diskSize = 54MB, = block size = 1MB + {2, 1048576, 8, 2048, 0}, // diskSize = 512 MB + {2, 1048578, 8, 2048, 0}, // Doesn't converge with original algorithm + }; + + TFatAlignment fatAlignment; + TInt numOfTests = sizeof(testVals) / sizeof(STestVal); + for (TInt n=0; n= 1048576; + + fatAlignment.Init( + fat32, + testVal.iNumberOfFats, + testVal.iMaxDiskSectors, + testVal.iSectorsPerCluster, + testVal.iRootDirEntries); + TInt r = fatAlignment.AdjustFirstDataSectorAlignment(testVal.iBlockSize); + test (r == KErrNone); + fatAlignment.Display(); + } + + const TInt64 KOneMByte = 1024*1024; + const TInt64 KOneGByte = 1024*KOneMByte; + const TInt64 KLastSizeToTest = 32*KOneGByte; + TInt iteration=0; + TInt64 diskSize; + + + + TInt successes = 0; + TInt failures = 0; + + for (iteration=0, diskSize = 16*KOneMByte; diskSize < KLastSizeToTest; iteration++, diskSize+=512) + { + TInt diskSizeInSectors = (TInt) (diskSize >> 9); + + const TInt KMaxFAT16Entries=0xFFF0; ///< Maximum number of clusters in a Fat16 Fat table, 65520 + + TBool fat32 = EFalse; + TInt numberOfFats = 2; + TInt rootDirEntries; + TInt sectorsPerCluster; + TInt blockSizeInSectors = 32; // 16K for FAT16 + + if (diskSizeInSectors<4096) // < 2MB + { + rootDirEntries=128; + sectorsPerCluster=1; + } + else if (diskSizeInSectors<8400) // < 4MB + { + rootDirEntries=256; + sectorsPerCluster=2; + } + else if (diskSizeInSectors<16384) // < 8MB + { + rootDirEntries=512; + sectorsPerCluster=4; + } + else if (diskSizeInSectors<32680) // < 16MB + { + rootDirEntries=512; + sectorsPerCluster=8; + } + else if(diskSizeInSectors<1048576) // >= 16Mb - FAT16 < (1048576) 512MB + { + TInt minSectorsPerCluster=(diskSizeInSectors+KMaxFAT16Entries-1)/KMaxFAT16Entries; + rootDirEntries=512; + sectorsPerCluster=1; + while (minSectorsPerCluster>sectorsPerCluster) + sectorsPerCluster<<=1; + } + else //use FAT32 + { + rootDirEntries=0; //this is always the case for fat32 + if(diskSizeInSectors < 16777216) //8GB in 512byte sectors + sectorsPerCluster=8; + else if(diskSizeInSectors < 33554432) //16GB in 512byte sectors + sectorsPerCluster=16; + else if(diskSizeInSectors < 67108864) //32GB in 512byte sectors + sectorsPerCluster=32; + else + sectorsPerCluster=64; //Anything >= 32GB uses a 32K cluster size + blockSizeInSectors = 2048; // 1MB for FAT32 + fat32 = ETrue; + } + + + fatAlignment.Init( + fat32, + numberOfFats, + diskSizeInSectors, + sectorsPerCluster, + rootDirEntries); + TInt r = fatAlignment.AdjustFirstDataSectorAlignment(blockSizeInSectors); + if (r == KErrNone) + successes++; + else + failures++; + + +// if (diskSize % 0x08000000 == 0) +// { +// RDebug::Print(_L("Iter %10lX of %10lX"), diskSize, KLastSizeToTest); +// fatAlignment.Display(); +// } + } + RDebug::Print(_L("Total iterations %u"), iteration); + RDebug::Print(_L("Max loop count %u"), fatAlignment.iMaxIterations); + RDebug::Print(_L("successes %d failures %d, success rate %ld"), + successes, failures, (TInt64(successes) * 100) / TInt64(successes + failures)); + test (failures == 0); + + } + + +static void TestZeroLengthFile() +// +// Test what happens if you write more to a zero length file than +// will fit in the filesystem. +// + { + test.Next(_L("Test behaviour of extending a zero length file")); + + FormatPack(); + + TInt r; + + TVolumeInfo volInfo; + r=TheFs.Volume(volInfo); + test(r==KErrNone); + + TInt64 spaceToUse = volInfo.iFree - gBytesPerCluster; // whole disk except 1 cluster + + test.Printf(_L("spaceToUse %ld gClusterCount %d gBytesPerCluster %d\n"), spaceToUse, gClusterCount, gBytesPerCluster); + test.Printf(_L("Before fill, volInfo.iSize %ld volInfo.iFree %ld\n"), volInfo.iSize, volInfo.iFree); + + RFile f; + + TInt tempfiles = 0; + while (spaceToUse > K1GigaByte) + { + TFileName tempName; + r=f.Temp(TheFs,_L("\\"),tempName,EFileRead|EFileWrite); + test(r==KErrNone); + r=f.SetSize(K1GigaByte); + test(r==KErrNone); + f.Close(); + spaceToUse -= K1GigaByte; + tempfiles++; + } + + r=f.Replace(TheFs,_L("\\USESPACE.TMP"),EFileRead|EFileWrite); + test(r==KErrNone); + r=f.SetSize((TInt)spaceToUse); + test(r==KErrNone); + f.Close(); + + r=TheFs.Volume(volInfo); + test(r==KErrNone); + test.Printf(_L("After fill, volInfo.iSize %ld volInfo.iFree %ld\n"), volInfo.iSize, volInfo.iFree); + + test(volInfo.iFree==gBytesPerCluster); // check we have 1 cluster free + + r=f.Replace(TheFs,_L("\\FILE.TMP"),EFileRead|EFileWrite); + test(r==KErrNone); + r=f.SetSize(2*gBytesPerCluster); // 2 clusters (will fail since there's not space) + test(r==KErrDiskFull); + f.Close(); + + r=TheFs.Volume(volInfo); + test(r==KErrNone); + test(volInfo.iFree==gBytesPerCluster); // check we still have 1 cluster free + + r=f.Replace(TheFs,_L("\\USESPACE.TMP"),EFileRead|EFileWrite); // truncate file to 0 + test(r==KErrNone); + f.Close(); + + r=TheFs.Volume(volInfo); + test(r==KErrNone); + test(volInfo.iFree==(spaceToUse+gBytesPerCluster)); // check we've freed up the space from USESPACE plus one cluster + + + test(TheBootSector.IsValid()); //-- TheBootSector is read after formatting + TInt64 rootDirpos = gRootDirStart; + + + //-- read 1 sector of the root dir. + r = MediaRawRead(TheFs, CurrentDrive(), rootDirpos, TheBootSector.BytesPerSector(), TheBuffer); + test(r == KErrNone); + + const TFatDirEntry* pE=(TFatDirEntry*)TheBuffer.Ptr(); + while (tempfiles-- > 0) + { + while (pE->IsVFatEntry()) + pE++; + test(pE->Size()==(TUint)K1GigaByte); + pE++; + } + + while (pE->IsVFatEntry()) + pE++; + + TBuf8<15> name=pE->Name(); + test(name==_L8("USESPACETMP")); + test(pE->StartCluster()==0); + + pE++; + while (pE->IsVFatEntry()) + pE++; + + name=pE->Name(); + test(name==_L8("FILE TMP")); + test(pE->StartCluster()==0); + + FormatPack(); + + } + + +// +// Call tests that may leave +// +void CallTestsL() + { + + //-- init random generator + rndSeed = Math::Random(); + + //-- set up console output + Fat_Test_Utils::SetConsole(test.Console()); + + + + TInt drvNum; + TInt r=TheFs.CharToDrive(gDriveToTest,drvNum); + test(r==KErrNone); + + if (!Is_Fat(TheFs,drvNum)) + { + test.Printf(_L("CallTestsL: Skipped: test requires FAT filesystem\n")); + return; + } + + + //-- print drive information + PrintDrvInfo(TheFs, drvNum); + + // check this is not the internal ram drive + TVolumeInfo v; + r=TheFs.Volume(v, drvNum); + test(r==KErrNone); + TBool isRamDrive = v.iDrive.iMediaAtt&KMediaAttVariableSize; + + gSessionPath[0] = (TText)gDriveToTest; + // verify that the drive is large enough for proper testing + if (v.iSize<512*1024) + { + test.Printf(_L("CallTestsL: Skipped: test not supported on drives smaller than 512 KB\n")); + return; + } + + FormatPack(); + DumpBootSector(); + + test.Printf(_L("TotalSectors = %u (%u bytes)\n"),gTotalSectors,gTotalSectors*TheBootSector.BytesPerSector()); + test.Printf(_L("Sector size = %u\n"),TheBootSector.BytesPerSector()); + test.Printf(_L("Cluster size = %u sectors\n"),TheBootSector.SectorsPerCluster()); + test.Printf(_L("Alloc unit = %u\n"), gBytesPerCluster); + test.Printf(_L("Fat is %u bit\n"), gFatBits); + User::After(200000); // 1/5 secs + + // set up buffers + TInt bufLen = 16*gBytesPerCluster; + if (bufLen < 16*1024) + bufLen = 16*1024; + pBuffer1=HBufC8::NewL(bufLen); + pBuffer2=HBufC8::NewL(bufLen); + + if (pBuffer1==NULL || pBuffer2==NULL) + Error(_L("OOM"),KErrNoMemory); + + + pBuffer1->Des().Zero(); + pBuffer1->Des().SetLength(bufLen); + + pBuffer2->Des().Zero(); + pBuffer2->Des().SetLength(bufLen); + + if (isRamDrive) + { + User::After(200000); // 1/5 secs + test.Printf(_L("*** Tests not valid on internal ram drive %C:\n"), (TUint)gDriveToTest); + User::After(200000); // 1/5 secs + } + else + { + TestZeroLengthFile(); + +#if defined(__WINS__) + TestFirstDataSectorAlignment(); +#endif + + TestFirst2FatEntries(); + + TestDiskIntegrity(); + + TestBounds(); + TestUnicodeEntry(); + + TestClusters(); + + TestClusterAllocation(); + + + TestParentDir(EFalse); // Test without VFAT entries + TestParentDir(ETrue); // Test with VFAT entries + + TestRoot(); + TestVolumeSize(); + TestFATTableEntries(); + + FormatPack(); + + } + delete pBuffer1; + delete pBuffer2; + } + + +/** + Generate unique temp file name in upper (FAT entry) or lower case (2 VFAT entries) + @param aFN descriptor for the file name + @param aUpperCase if ETrue, the file name will be in upper case, in a lower case otherwise. + +*/ +void GenerateTmpFileName(TDes& aFN, TBool aUpperCase) +{ + const TInt rnd = Math::Rand(rndSeed); + + aFN.Format(_L("%08x.tmp"), rnd); + + if(aUpperCase) + aFN.UpperCase(); + else + aFN.LowerCase(); + +} + +/** + Create FAT or VFAT entry in a speciified directory + + @param aDir specifies the directory where enntry will be created + @param aVFatEntry if true, VFAT entry will be created (2 FAT entries, actually), otherwise - FAT entry + @param apFileName in !=NULL there will be placed the name of the file created +*/ +void CreateFatEntry(const TDesC& aDir, TBool aVFatEntry, TDes *apFileName/*=NULL*/) +{ + TFileName tmpFN; + RFile file; + TInt nRes; + + do + { + GenerateTmpFileName(tmpFN, !aVFatEntry); //-- generates 8.3 file name FAT (1 entry) or VFAT (2 entries) + tmpFN.Insert(0, aDir); + + nRes = file.Create(TheFs, tmpFN, EFileRead|EFileWrite); + + if(nRes == KErrAlreadyExists) + continue; //-- current random name generator isn't perfect... + + if(nRes != KErrNone) + Error(_L("Error creating a file"),nRes); + + file.Close(); + + }while(nRes != KErrNone); + + if(apFileName) + *apFileName = tmpFN; + +} + +