Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h)
Have multiple extension sections in the bld.inf, one for each version
of the compiler. The RVCT version building the tools will build the
runtime libraries for its version, but make sure we extract all the other
versions from zip archives. Also add the archive for RVCT4.
// 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 <f32file.h>
#include <e32test.h>
#include <e32math.h>
#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<<KDefaultSectorLog2, aBootSector);
test(nRes == KErrNone);
if(!aBootSector.IsValid())
{
test.Printf(_L("Wrong bootsector! Dump:\n"));
aBootSector.PrintDebugInfo();
test(0);
}
// Calculate derived variables (fixed for a particular disk format)
if (TheBootSector.FatType() == EFat32)
{
gDiskType = EFat32;
gFatBits = 32;
gEndOfChain = 0x0FFFFFFF;
}
else if (TheBootSector.FatType() == EFat16)
{
gDiskType = EFat16;
gFatBits = 16;
gEndOfChain = 0xFFFF;
}
else
{
gDiskType = EFat12;
gFatBits = 12;
gEndOfChain = 0x0FFF;
}
gBytesPerCluster = TheBootSector.BytesPerSector() * TheBootSector.SectorsPerCluster();
gRootDirSectors = ((TheBootSector.RootDirEntries() * KSizeOfFatDirEntry + TheBootSector.BytesPerSector() - 1) /
TheBootSector.BytesPerSector());
gEntriesPerCluster = gBytesPerCluster / KSizeOfFatDirEntry;
gTotalSectors = (TheBootSector.TotalSectors() ? TheBootSector.TotalSectors() : TheBootSector.HugeSectors());
switch (gDiskType)
{
case EFat12:
case EFat16:
gFatSizeSectors = TheBootSector.FatSectors();
gRootSector = TheBootSector.ReservedSectors() + TheBootSector.NumberOfFats() * gFatSizeSectors;
gFirstDataSector = gRootSector + gRootDirSectors;
gRootCluster = 0;
gFirstDataCluster = 2;
gDataStartBytes = gFirstDataSector * TheBootSector.BytesPerSector();
gRootDirStart = gRootSector * TheBootSector.BytesPerSector();
break;
case EFat32:
gFatSizeSectors = TheBootSector.FatSectors32();
gRootSector = TheBootSector.ReservedSectors() + TheBootSector.NumberOfFats() * gFatSizeSectors;
gFirstDataSector = gRootSector + gRootDirSectors;
gRootCluster = 2;
gFirstDataCluster = 3;
gDataStartBytes = gFirstDataSector * TheBootSector.BytesPerSector();
gRootDirStart = (TheBootSector.RootClusterNum() - 2) * gBytesPerCluster + gDataStartBytes;
break;
default:
break;
}
gClusterCount = (gTotalSectors - gFirstDataSector) / TheBootSector.SectorsPerCluster();
gFatTestEntries = MaxClusters();
if (gFatTestEntries > 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;i<dirEntriesNeeded;i++)
{
CreateFatEntry(TheFileName, ETrue);
}
test.Printf(_L("Test dir with no match\n"));
FatWrite(gFirstDataCluster,gFirstDataCluster);
if ((r=TheDir.Open(TheFs,_L("\\D\\nomatch"),KEntryAttMaskSupported))!=KErrNone)
Error(_L("Failed Directory open"),r);
if ((r=TheDir.Read(TheEntry))!=KErrCorrupt)
Error(_L("Failed Directory read"),r);
TheDir.Close();
test.Printf(_L("Test dir with match\n"));
if ((r=TheDir.Open(TheFs,_L("\\D\\*.*"),KEntryAttMaskSupported))!=KErrNone)
Error(_L("Failed Directory open"),r);
if ((r=TheDir.Read(TheEntry))!=KErrNone)
Error(_L("Failed Directory read"),r);
TheDir.Close();
test.Printf(_L("Test dir without loop\n"));
FatWrite(gFirstDataCluster,gEndOfChain);
if ((r=TheDir.Open(TheFs,_L("\\D\\nomatch"),KEntryAttMaskSupported))!=KErrNone)
Error(_L("Directory open"),r);
if ((r=TheDir.Read(TheEntry))!=KErrEof)
Error(_L("Reading empty dir returned"),r);
TheDir.Close();
test.Printf(_L("Test dir with long filenames\n"));
FormatPack();
r=TheFs.MkDir(_L("\\D\\"));
if (r!=KErrNone && r!=KErrAlreadyExists)
Error(_L("Failed to make directory"),r);
TheFileName=_L("\\D\\");
dirEntriesNeeded = ((TheBootSector.BytesPerSector()*TheBootSector.SectorsPerCluster()/KSizeOfFatDirEntry)-3);
dirEntriesNeeded = ThrottleDirEntries(dirEntriesNeeded, 3);
//-- generate some number of VFAT dir. entries by creating 8.3 temp. files in a lower case
for (i=0;i<dirEntriesNeeded;i++)
{
CreateFatEntry(TheFileName, ETrue);
}
MakeFile(_L("\\D\\longfileName.Long"));
test.Printf(_L("Test dir with no match\n"));
FatWrite(gFirstDataCluster,gFirstDataCluster);
if ((r=TheDir.Open(TheFs,_L("\\D\\nomatch"),KEntryAttMaskSupported))!=KErrNone)
Error(_L("Failed Directory open"),r);
if ((r=TheDir.Read(TheEntry))!=KErrCorrupt)
Error(_L("Failed Directory read"),r);
TheDir.Close();
test.Printf(_L("Test dir with match\n"));
if ((r=TheDir.Open(TheFs,_L("\\D\\*.*"),KEntryAttMaskSupported))!=KErrNone)
Error(_L("Failed Directory open"),r);
if ((r=TheDir.Read(TheEntry))!=KErrNone)
Error(_L("Failed Directory read"),r);
TheDir.Close();
test.Printf(_L("Test dir without loop\n"));
FatWrite(gFirstDataCluster,gEndOfChain);
if ((r=TheDir.Open(TheFs,_L("\\D\\nomatch"),KEntryAttMaskSupported))!=KErrNone)
Error(_L("Directory open"),r);
#if !defined _UNICODE
if ((r=TheDir.Read(TheEntry))!=KErrCorrupt)
Error(_L("Reading empty dir returned"),r);
#endif
TheDir.Close();
}
static void TestLoopedFile()
//
// Test Looped file
//
{
test.Printf(_L("Test looped file\n"));
FormatPack();
TInt r;
test.Next(_L("CreateFile"));
test(TheFile.Replace(TheFs,_L("\\LOOPED1.TMP"),EFileRead|EFileWrite)==KErrNone);
TPtr8 buf=pBuffer1->Des();
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()<<SectorShifts())
{
if (r!=KErrCorrupt)
{
test.Printf(_L("Predicted error %d Actual error %d\n"),KErrCorrupt,r);
Error(_L("Failed write"),r);
}
}
else
{
if (r!=KErrNone)
{
test.Printf(_L("Predicted error %d Actual error %d\n"),KErrNone,r);
Error(_L("Failed write"),r);
}
}
TheFile.Close();
}
static void TestDirEntry(TInt anInitialSize,TInt aWriteLen,TInt aCorruptStartCluster)
//
// Test directory entry
//
{
test.Printf(_L("Initial size=%d, len=%d, start cluster=0x%x\n"),anInitialSize,aWriteLen,aCorruptStartCluster);
FormatPack();
TInt r;
test(TheFile.Create(TheFs,_L("\\CORRUPT1.TMP"),EFileRead|EFileWrite)==KErrNone);
TheBuffer.SetLength(anInitialSize);
Mem::Fill(&TheBuffer[0],anInitialSize,'A');
r=TheFile.Write(TheBuffer);
test(r==KErrNone);
TheFile.Close();
r=TheDisk.Open(TheFs,CurrentDrive());
test(r==KErrNone);
TPtr8 sectorBuf((TUint8*)pBuffer1->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<nFiles;i++)
{
CreateFatEntry(_L("\\P1\\"), aUseVfat);
}
TInt p1p2 = cl;
if(aUseVfat)
{
TestMakeDir(_L("\\p1\\p2\\"), cl++, p1);
TestMakeDir(_L("\\p1\\p21\\"), cl++, p1);
TestMakeDir(_L("\\p1\\p2\\p3\\"), cl++, p1p2);
TestMakeDir(_L("\\p1\\p2\\p33\\"), cl++, p1p2);
TestMakeDir(_L("\\p1\\p2\\p34\\"), cl++, p1p2);
TestMakeDir(_L("\\p1\\p2\\p35\\"), cl++, p1p2);
TestMakeDir(_L("\\p1\\p2\\p36\\"), cl++, p1p2);
TestMakeDir(_L("\\p1\\p2\\p37\\"), cl++, p1p2);
TestMakeDir(_L("\\p1\\p2\\p38\\"), cl++, p1p2);
}
else
{
TestMakeDir(_L("\\P1\\P2\\"), cl++, p1);
TestMakeDir(_L("\\P1\\P21\\"), cl++, p1);
TestMakeDir(_L("\\P1\\P2\\P3\\"), cl++, p1p2);
TestMakeDir(_L("\\P1\\P2\\P33\\"), cl++, p1p2);
TestMakeDir(_L("\\P1\\P2\\P34\\"), cl++, p1p2);
TestMakeDir(_L("\\P1\\P2\\P35\\"), cl++, p1p2);
TestMakeDir(_L("\\P1\\P2\\P36\\"), cl++, p1p2);
TestMakeDir(_L("\\P1\\P2\\P37\\"), cl++, p1p2);
TestMakeDir(_L("\\P1\\P2\\P38\\"), cl++, p1p2);
TestMakeDir(_L("\\P1\\P2\\P39\\"), cl++, p1p2);
TestMakeDir(_L("\\P1\\P2\\P40\\"), cl++, p1p2);
TestMakeDir(_L("\\P1\\P2\\P41\\"), cl++, p1p2);
TestMakeDir(_L("\\P1\\P2\\P42\\"), cl++, p1p2);
TestMakeDir(_L("\\P1\\P2\\P43\\"), cl++, p1p2);
TestMakeDir(_L("\\P1\\P2\\P44\\"), cl++, p1p2);
TestMakeDir(_L("\\P1\\P2\\P45\\"), cl++, p1p2);
}
// if sectors/cluster == 1 then the directory \p1\p2\ will now have to
// allocate another cluster
if(TheBootSector.SectorsPerCluster()==1)
++cl;
if(aUseVfat)
{
TestMakeDir(_L("\\p1\\p2\\p310\\"), cl++, p1p2);
TestMakeDir(_L("\\p1\\p2\\p311\\"), cl++, p1p2);
TestMakeDir(_L("\\p1\\p2\\p312\\"), cl++, p1p2);
TestMakeDir(_L("\\p1\\p2\\p313\\"), cl++, p1p2);
TestMakeDir(_L("\\p1\\p2\\p314\\"), cl++, p1p2);
TestMakeDir(_L("\\p1\\p2\\p315\\"), cl++, p1p2);
TestMakeDir(_L("\\p1\\p2\\p316\\"), cl++, p1p2);
TestMakeDir(_L("\\p1\\p2\\p317\\"), cl++, p1p2);
}
else
{
TestMakeDir(_L("\\P1\\P2\\P310\\"), cl++, p1p2);
TestMakeDir(_L("\\P1\\P2\\P311\\"), cl++, p1p2);
TestMakeDir(_L("\\P1\\P2\\P312\\"), cl++, p1p2);
TestMakeDir(_L("\\P1\\P2\\P313\\"), cl++, p1p2);
TestMakeDir(_L("\\P1\\P2\\P314\\"), cl++, p1p2);
TestMakeDir(_L("\\P1\\P2\\P315\\"), cl++, p1p2);
TestMakeDir(_L("\\P1\\P2\\P316\\"), cl++, p1p2);
TestMakeDir(_L("\\P1\\P2\\P317\\"), cl++, p1p2);
TestMakeDir(_L("\\P1\\P2\\P318\\"), cl++, p1p2);
TestMakeDir(_L("\\P1\\P2\\P319\\"), cl++, p1p2);
TestMakeDir(_L("\\P1\\P2\\P320\\"), cl++, p1p2);
TestMakeDir(_L("\\P1\\P2\\P321\\"), cl++, p1p2);
TestMakeDir(_L("\\P1\\P2\\P322\\"), cl++, p1p2);
TestMakeDir(_L("\\P1\\P2\\P323\\"), cl++, p1p2);
TestMakeDir(_L("\\P1\\P2\\P324\\"), cl++, p1p2);
TestMakeDir(_L("\\P1\\P2\\P325\\"), cl++, p1p2);
}
// if sectors/cluster <= 2 then the directory \p1\p2\ will have to
// allocate another cluster
if(TheBootSector.SectorsPerCluster()<=2)
++cl;
TestMakeDir(_L("\\P1\\P2\\P330\\"), cl++, p1p2);
TestMakeDir(_L("\\P11\\"), cl++, root);
}
static const TInt KMaxFiles=5;
//
// Test root dir size
//
static void TestRoot()
{
test.Next(_L("Test root dir size"));
if (gDiskType == EFat32)
{
test.Printf(_L("Not possible on FAT32 filesystem\n"));
return;
}
FormatPack();
TInt rootEntries=TheBootSector.RootDirEntries();
test.Printf(_L("Total root entries allowed = %d\n"),rootEntries);
TFileName fileName[KMaxFiles]; // KMaxFiles=5 in this test
TFileName tempName;
TInt numberOfEntries=rootEntries;
TInt r;
RFile f;
//-- generate 8.3 FAT entries, temp files created in upper-case, otherwise it will be 2 vFAT entries
while(numberOfEntries--)
{
if (numberOfEntries<KMaxFiles)
CreateFatEntry(_L("\\"), EFalse, &fileName[numberOfEntries]);
else
CreateFatEntry(_L("\\"), EFalse);
}
r = f.Create(TheFs, _L("\\123456.78"), EFileRead|EFileWrite);
test(r==KErrDirFull);
f.Close();
TInt i=0;
for (i=0;i<KMaxFiles;i++)
{
r=TheFs.Delete(fileName[i]);
test(r==KErrNone);
}
r=TheFs.SetSessionPath(_L("\\"));
test(r==KErrNone);
TInt nameLength=(KMaxFiles-1)*13; // -1 for zero terminator
CreateLongName(tempName,gSeed,nameLength*2);
r=f.Create(TheFs,tempName,0); // Needs 9 free entries - there are only 5 available
test(r==KErrDirFull);
tempName.SetLength(nameLength+1);
r=f.Create(TheFs,tempName,0); // Needs 6 free entries - there are only 5 available
test(r==KErrDirFull);
tempName.SetLength(nameLength);
r=f.Create(TheFs,tempName,0); // Needs 5 free entries - there are 5 available
test(r==KErrNone);
f.Close();
#if 0 // This is the old test that assumed UNICODE builds
// which created VFAT entries even for uppercase 8.3 file names
TInt i=0;
for (i=0;i<KMaxFiles-2;i++)
{
r=TheFs.Delete(fileName[i]); // UNICODE build - free 6 entries (delete 3 files)
test(r==KErrNone);
}
r=TheFs.SetSessionPath(_L("\\"));
test(r==KErrNone);
TInt vFatUnitNameSize=13;
TInt nameLength=(KMaxFiles-1)*vFatUnitNameSize-1; //
CreateLongName(tempName,gSeed,nameLength*2);
r=f.Create(TheFs,tempName,0); // Needs 9 free entries
test(r==KErrDirFull);
nameLength=(KMaxFiles)*vFatUnitNameSize;
tempName.SetLength(nameLength+1);
r=f.Create(TheFs,tempName,0); // Needs 7 free entries
test(r==KErrDirFull);
tempName.SetLength(nameLength);
r=f.Create(TheFs,tempName,0); // Needs 6 free entries
test(r==KErrNone);
f.Close();
#endif
TheFs.Delete(tempName);
tempName.SetLength(nameLength-7);
r=f.Create(TheFs,tempName,0);
test(r==KErrNone);
f.Close();
r=f.Create(TheFs,_L("ASDF"),0);
test(r==KErrDirFull);
TheFs.Delete(tempName);
tempName.SetLength(nameLength-15);
r=f.Create(TheFs,tempName,0);
test(r==KErrNone);
f.Close();
tempName=_L("testname");
r=f.Create(TheFs,tempName,0);
test(r==KErrDirFull);
tempName.UpperCase();
r=f.Create(TheFs,tempName,0);
test(r==KErrNone);
f.Close();
r=TheFs.SetSessionPath(gSessionPath);
test(r==KErrNone);
}
static void TestVolumeSize()
//
// Test the volume size is zero when empty
//
{
test.Next(_L("Test the volume size"));
FormatPack();
TVolumeInfo volInfo;
TInt r=TheFs.Volume(volInfo);
test(r==KErrNone);
TInt64 calcsize = MAKE_TINT64(0, gClusterCount)*gBytesPerCluster;
if (volInfo.iSize > 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;i<KMaxFiles;i++)
{
fileName=_L("\\File");
fileName.AppendNum(i);
r=f[i].Create(TheFs,fileName,0);
test(r==KErrNone);
}
TInt maxTotalSize=1048576;
TInt maxFileSize=maxTotalSize/KMaxFiles;
TInt maxIterations=20;
while(maxIterations--)
{
for (i=0;i<KMaxFiles;i++)
{
TInt randSize=Math::Rand(gSeed)%maxFileSize;
r=f[i].SetSize(randSize);
test(r==KErrNone);
}
test.Printf(_L("Countdown .. %d \r"),maxIterations);
}
test.Printf(_L("\n"));
TInt totalSize=0;
for (i=0;i<KMaxFiles;i++)
{
TInt size=0;
r=f[i].Size(size);
test(r==KErrNone);
totalSize+=((size+gBytesPerCluster-1)/gBytesPerCluster)*gBytesPerCluster;
}
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!=totalSize)
{
test.Printf(_L("volInfo.iSize = %ld\n"), volInfo.iSize);
test.Printf(_L("volInfo.iFree = %ld\n"), volInfo.iFree);
test.Printf(_L("totalSize = %ld\n"), totalSize);
TInt diff = I64LOW(volInfo.iSize-volInfo.iFree) - totalSize;
test.Printf(_L("difference = %d (%d clusters)\n"), diff, diff/gBytesPerCluster);
}
test(volInfo.iSize-volInfo.iFree==totalSize);
for (i=0;i<KMaxFiles;i++)
f[i].Close();
for (i=0;i<KMaxFiles;i++)
{
fileName=_L("\\File");
fileName.AppendNum(i);
r=TheFs.Delete(fileName);
test(r==KErrNone);
}
r=TheFs.Volume(volInfo);
if (gDiskType == EFat32)
volInfo.iSize -= gBytesPerCluster; // root dir is part of the 'size'
test(r==KErrNone);
test(volInfo.iSize-volInfo.iFree==0);
MakeDir(gSessionPath);
TInt entries=(gBytesPerCluster/KSizeOfFatDirEntry)*5-2;
entries = ThrottleDirEntries(entries, 2);
TInt clusters = ((entries * KSizeOfFatDirEntry) + gBytesPerCluster-1) / gBytesPerCluster;
//-- create "entries" FAT dir. entries by creating 8.3 files in upper case
while(entries--)
{
CreateFatEntry(gSessionPath, EFalse);
}
r=TheFs.Volume(volInfo);
test(r==KErrNone);
if (gDiskType == EFat32)
volInfo.iSize -= gBytesPerCluster; // root dir is part of the 'size'
test.Printf(_L("volInfo.iSize = %ld\n"), volInfo.iSize);
test.Printf(_L("volInfo.iFree = %ld\n"), volInfo.iFree);
if (volInfo.iSize-volInfo.iFree!=clusters*gBytesPerCluster)
{
DumpFat();
DumpData(1, 200);
}
test(volInfo.iSize-volInfo.iFree==clusters*gBytesPerCluster);
//-- create 1 FAT dir. entry
CreateFatEntry(gSessionPath, EFalse);
r=TheFs.Volume(volInfo);
test(r==KErrNone);
if (gDiskType == EFat32)
volInfo.iSize -= gBytesPerCluster; // root dir is part of the 'size'
test.Printf(_L("volInfo.iSize = %ld\n"), volInfo.iSize);
test.Printf(_L("volInfo.iFree = %ld\n"), volInfo.iFree);
if (volInfo.iSize-volInfo.iFree!=(clusters+1)*gBytesPerCluster)
{
DumpFat();
DumpData(1, 200);
}
test(volInfo.iSize-volInfo.iFree==(clusters+1)*gBytesPerCluster);
CFileMan* fMan=CFileMan::NewL(TheFs);
r=fMan->RmDir(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<KDirEntrySize> 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<numFATs; ++i)
{
nRes = MediaRawRead(TheFs, CurrentDrive(), posFat1Start + i*fatSize, 8, fatBufCurr);
test(nRes==KErrNone);
fatBufCurr.SetLength(3);
if(fatBufCurr != fat1Buf)
{
test.Printf(_L("1st 2 FAT entries in FAT#%d are different from FAT1!\n"), i);
test(0);
}
}
}
break;
//----------- FAT16 ---------------------
case EFat16:
{
typedef TUint16 TFat16Entry;
fat1Buf.SetLength(2*sizeof(TFat16Entry));
const TFat16Entry* pFat = (const TFat16Entry*)fat1Buf.Ptr();
const TFat16Entry fatEntry_0 = pFat[0]; //-- do not mask entries
const TFat16Entry fatEntry_1 = pFat[1]; //-- do not mask entries
test.Printf(_L("FAT16[0]=0x%x, FAT16[1]=0x%x\n"), fatEntry_0, fatEntry_1);
test(fatEntry_0 == 0xFFF8); //-- see FAT specs
test(fatEntry_1 == 0xFFFF); //-- the volume shall be clean just after the formatting. It can be 0x7FFF if a write to the volume occured.
//-- test that all copies of FAT have the same values in FAT[0] & FAT[1]
for(TInt i=1; i<numFATs; ++i)
{
nRes = MediaRawRead(TheFs, CurrentDrive(), posFat1Start + i*fatSize, 8, fatBufCurr);
test(nRes==KErrNone);
fatBufCurr.SetLength(2*sizeof(TFat16Entry));
if(fatBufCurr != fat1Buf)
{
test.Printf(_L("1st 2 FAT entries in FAT#%d are different from FAT1!\n"), i);
test(0);
}
}
}
break;
//----------- FAT32 ---------------------
case EFat32:
{
typedef TUint32 TFat32Entry;
fat1Buf.SetLength(2*sizeof(TFat32Entry));
const TFat32Entry* pFat = (const TFat32Entry*)fat1Buf.Ptr();
const TFat32Entry fatEntry_0 = pFat[0]; //-- do not mask entries
const TFat32Entry fatEntry_1 = pFat[1]; //-- do not mask entries
test.Printf(_L("FAT32[0]=0x%x, FAT32[1]=0x%x\n"), fatEntry_0, fatEntry_1);
test(fatEntry_0 == 0x0FFFFFF8); //-- see FAT specs
test(fatEntry_1 == 0x0FFFFFFF); //-- the volume shall be clean just after the formatting. It can be 0x07FFFFFF if a write to the volume occured.
//-- test that all copies of FAT have the same values in FAT[0] & FAT[1]
for(TInt i=1; i<numFATs; ++i)
{
nRes = MediaRawRead(TheFs, CurrentDrive(), posFat1Start + i*fatSize, 8, fatBufCurr);
test(nRes==KErrNone);
fatBufCurr.SetLength(2*sizeof(TFat32Entry));
if(fatBufCurr != fat1Buf)
{
test.Printf(_L("1st 2 FAT entries in FAT#%d are different from FAT1!\n"), i);
test(0);
}
}
}
break;
default:
test(0);
break;
};//switch(gDiskType)
}
/**
Exhaustive test of Data alignmemnt calculation
in this code the function
TInt TFatAlignment::AdjustFirstDataSectorAlignment(TInt aBlockSize)
should be exactly the same as
TInt CFatFormatCB::AdjustFirstDataSectorAlignment(TInt aBlockSize)
*/
class TFatAlignment
{
public:
enum {KDefFatResvdSec = 1, KDefFat32ResvdSec = 32}; ///< default number of FAT32 reserved sectors
public:
TFatAlignment();
void Init(TBool aFat32, TInt aNumberOfFats, TInt aMaxDiskSectors, TInt aSectorsPerCluster, TInt aRootDirEntries);
TUint32 MaxFat32Sectors() const;
TInt MaxFat16Sectors() const;
TInt MaxFat12Sectors() const;
TUint32 RootDirSectors() const;
TInt FirstDataSector() const;
TBool Is32BitFat() const;
TBool Is16BitFat() const;
TInt AdjustFirstDataSectorAlignment(TInt aBlockSize);
void Display();
public:
TInt iBytesPerSector;
TInt iNumberOfFats;
TInt iMaxDiskSectors;
TInt iSectorsPerCluster;
TInt iReservedSectors;
TInt iSectorsPerFat;
TInt iRootDirEntries;
TBool iFat32; // 0 = FAT16, 1 = FAT32
TInt iMaxIterations;
};
TFatAlignment::TFatAlignment()
{
iMaxIterations = 0;
}
void TFatAlignment::Init(TBool aFat32, TInt aNumberOfFats, TInt aMaxDiskSectors, TInt aSectorsPerCluster, TInt aRootDirEntries)
{
iBytesPerSector = 512;
iFat32 = aFat32;
iNumberOfFats = aNumberOfFats;
iMaxDiskSectors = aMaxDiskSectors;
iSectorsPerCluster = aSectorsPerCluster;
iRootDirEntries = aRootDirEntries;
iReservedSectors = iFat32 ? KDefFat32ResvdSec : KDefFatResvdSec;
iSectorsPerFat = iFat32 ? MaxFat32Sectors() : MaxFat16Sectors();
}
void TFatAlignment::Display()
{
RDebug::Print(_L("iFat32 %u iNumberOfFats %u,iMaxDiskSectors %u,iSectorsPerCluster %u,iReservedSectors %u,iSectorsPerFat %u, iRootDirEntries %u, FirstDataSector %08X"),
iFat32,
iNumberOfFats,
iMaxDiskSectors,
iSectorsPerCluster,
iReservedSectors,
iSectorsPerFat,
iRootDirEntries,
FirstDataSector());
}
TInt TFatAlignment::MaxFat16Sectors() const
{
TInt fatSizeInBytes=(2*iMaxDiskSectors)/iSectorsPerCluster+(iBytesPerSector-1);
return(fatSizeInBytes/iBytesPerSector);
}
TInt TFatAlignment::MaxFat12Sectors() const
{
TInt maxDiskClusters=iMaxDiskSectors/iSectorsPerCluster;
TInt fatSizeInBytes=maxDiskClusters+(maxDiskClusters>>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<KMaxIterations && reservedSectorsOld != iReservedSectors; n++)
{
reservedSectorsOld = iReservedSectors;
iSectorsPerFat = bFat32 ? MaxFat32Sectors() : bFat16 ? MaxFat16Sectors() : MaxFat12Sectors();
fatSectors = iSectorsPerFat * iNumberOfFats;
// calculate number of blocks
TInt nBlocks = (iReservedSectors + fatSectors + rootDirSectors + aEraseBlockSizeInSectors-1) / aEraseBlockSizeInSectors;
iReservedSectors = (nBlocks * aEraseBlockSizeInSectors) - rootDirSectors - fatSectors;
}
ASSERT(iReservedSectors >= (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<numOfTests; n++)
{
STestVal& testVal = testVals[n];
TBool fat32 = testVal.iMaxDiskSectors >= 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;
}