diff -r 000000000000 -r a41df078684a kerneltest/f32test/filesystem/fat/t_scn32dr1.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kerneltest/f32test/filesystem/fat/t_scn32dr1.cpp Mon Oct 19 15:55:17 2009 +0100 @@ -0,0 +1,2390 @@ +// Copyright (c) 1998-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\scndrv\t_scn32dr1.cpp +// +// + +#include +#include + +#include "t_server.h" + +#include "fat_utils.h" +using namespace Fat_Test_Utils; + +#ifdef __VC32__ + // Solve compilation problem caused by non-English locale + #pragma setlocale("english") +#endif + +/* +T_testscndrv tests the scandrive utility. Errors in this test will be +introduced using the RRawdDisk class. The correct fixup is tested by rereading +the disk. Drives tested are the default path(epoc) and X: (wins). This test +returns immediately if used on the internal ram drive +*/ + +/* + The initial FAT12 directory structure (with cluster number in brackets) is as follows: + + | + - scndrv (2) + | + - dir1 (3-4) + | | + | (5) + | + - dir2 (6) + | + - full (7) + | | + | | + | - (11-17) + | + - somedirwith3entries (8) + | + - somedir2with3entries (9) + | + - almostfull(10) + | + - (18+19) + +*/ + +/* + The initial FAT32 directory structure (with cluster number in brackets is as follows): + + | + - scndrv (3) + | + - dir1 (4) + | | + | (5) + | + - dir2 (6) + | + - full (7) + | | + | | + | - (11-17) + | + - somedirwith3entries (8) + | + - somedir2with3entries (9) + | + - almostfull(10) + | + - (18+19) + +*/ + +GLDEF_D RTest test(_L("T_SCN32DR1")); + +LOCAL_D const TInt KMaxFatEntries = 2048; +LOCAL_D const TInt KMaxFatSize = KMaxFatEntries * 4; + +LOCAL_D const TInt KDirAttrReadOnly = 0x01; +LOCAL_D const TInt KDirAttrHidden = 0x02; +LOCAL_D const TInt KDirAttrSystem = 0x04; +LOCAL_D const TInt KDirAttrVolumeId = 0x08; +LOCAL_D const TInt KDirAttrDirectory = 0x10; +LOCAL_D const TInt KDirAttrArchive = 0x20; +LOCAL_D const TInt KDirAttrLongName = KDirAttrReadOnly | KDirAttrHidden | KDirAttrSystem | KDirAttrVolumeId; +LOCAL_D const TInt KDirAttrLongMask = KDirAttrLongName | KDirAttrDirectory | KDirAttrArchive; +LOCAL_D const TInt KDirLastLongEntry = 0x40; + +LOCAL_D RRawDisk TheRawDisk; +LOCAL_D TFatBootSector BootSector; +LOCAL_D TFileName TheDrive=_L("?:\\"); +LOCAL_D HBufC8* FatBufPtr = NULL; +LOCAL_D HBufC8* DirBufPtr = NULL; +LOCAL_D HBufC8* ExtBufPtr = NULL; +LOCAL_D TInt32 ExtBufAdd = 0; +LOCAL_D TInt32 ExtBufLen = 0; +LOCAL_D HBufC8* FatDiskPtr = NULL; +LOCAL_D HBufC8* DirDiskPtr = NULL; + +static TFatType gDiskType = EInvalid; + +LOCAL_D TInt gDriveNumber; + +LOCAL_D TInt gBytesPerCluster; +LOCAL_D TInt gEntriesPerCluster; +LOCAL_D TInt gRootDirSectors; +LOCAL_D TInt gRootDirEntries; +LOCAL_D TInt gRootDirStart; // in bytes +LOCAL_D TInt gRootSector; +LOCAL_D TInt gFatStartBytes; +LOCAL_D TInt gFatTestSize; // in bytes +LOCAL_D TInt gFatTestEntries; +LOCAL_D TInt gFatSizeSectors; +LOCAL_D TInt gFirstDataSector; +LOCAL_D TInt gMaxDataCluster; +LOCAL_D TInt gDataStartBytes; +LOCAL_D TInt gEndOfChain; // for FAT12/16/32 + +// cluster numbers in 1 and >1 sector per cluster modes +LOCAL_D TInt gClusterRootDir; // 2 2 +LOCAL_D TInt gClusterScnDrv; // 3 3 +LOCAL_D TInt gClusterDir1; // 4 4 +LOCAL_D TInt gClusterDir1ext; // 5 4 +LOCAL_D TInt gClusterDir2; // 7 6 +LOCAL_D TInt gClusterDir2_Full; // 8 7 +LOCAL_D TInt gClusterDir2_SD3E; // 9 8 +LOCAL_D TInt gClusterDir2_SD23E; // 10 9 +LOCAL_D TInt gClusterDir2_AFull; // 11 10 +LOCAL_D TInt gClusterEndMaxDepth; // 147 146 + +LOCAL_D TFileName LastInFull; + +class TEntryInfo + { +public: + TEntryInfo(TInt aBytePos,TInt aLength):iBytePos(aBytePos),iLength(aLength){} + TEntryInfo(){} +public: + TInt iBytePos; + TInt iLength; + }; + + +LOCAL_C TInt DirBufferSize() +// +// returns size in bytes nec for buffer to store relevant disk data +// + { + return(gMaxDataCluster*gBytesPerCluster); + } + +LOCAL_C TInt PosInBytes(TInt aFatIndex) +// +// Return number of bytes into the FAT +// + { + 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); + } + +LOCAL_C TUint32 MaxClusters() +// +// Return the number of data clusters on the disk +// + { + TUint32 totSec = (BootSector.TotalSectors() ? BootSector.TotalSectors() : BootSector.HugeSectors()); + TUint32 numSec = totSec - gFirstDataSector; + return numSec / BootSector.SectorsPerCluster(); + } + +LOCAL_C TInt ClusterToByte(TInt aCluster) +// +// converts cluster number to byte offset on disk +// + { + TInt pos; + if (aCluster < 2) + pos = gRootDirStart; + else + pos = (aCluster - 2) * gBytesPerCluster + gFirstDataSector * BootSector.BytesPerSector(); + return pos; + } + +LOCAL_C TInt ByteToCluster(TInt aBytePos) +// +// Converts byte offset from root dir buffer to cluster number +// + { + if (aBytePos < gRootDirStart) + return -1; + if (aBytePos < gDataStartBytes) + return 0; + return (aBytePos - gDataStartBytes) / gBytesPerCluster + 2; + } + +LOCAL_C TInt ClusterEntryToBytes(TInt aCluster,TInt aEntry) +// +// converts position in cluster and entry number to byte pos from root directory +// + { + TInt pos; + pos = ClusterToByte(aCluster) - gRootDirStart + aEntry*KSizeOfFatDirEntry; + return pos; + } + +LOCAL_C TInt FindUnMatch(const TUint8* aBuf, const TUint8* aCmp, TInt aLen, TInt aStart=0) +// +// Return position in buffers which doesn't match, or -1 if it matches +// + { + for (TInt i = aStart; i < aStart + aLen; i++) + if (aBuf[i] != aCmp[i]) + return i; + return -1; + } + +LOCAL_C 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 += BootSector.ReservedSectors() * BootSector.BytesPerSector(); + TInt r=TheRawDisk.Open(TheFs,gSessionPath[0]-'A'); + test(r==KErrNone); + TPtr8 buf(&data[0], 4); + r=TheRawDisk.Read(pos, buf); + test(r==KErrNone); + TheRawDisk.Close(); + } + + TUint32 val = 0; + switch (gDiskType) + { + case EFat32: + val = ptr[0] + (ptr[1] << 8) + (ptr[2] << 16) + (ptr[3] << 24); + break; + case EFat16: + val = ptr[0] + (ptr[1] << 8); + break; + case EFat12: + val = ptr[0] + (ptr[1] << 8); + if (aIndex & 1) + val >>= 4; + val &= 0xFFF; + break; + default: + test(0); + } + return val; + } + +LOCAL_C void WriteFat(TInt aFatIndex,TInt aValue,const TUint8* aFat) +// +// Write a value to both fats starting at aFat +// + { + TUint8* p=(TUint8*)(aFat+PosInBytes(aFatIndex)); + switch (gDiskType) + { + case EFat32: + p[0] = (TUint8) (aValue); + p[1] = (TUint8) (aValue >> 8); + p[2] = (TUint8) (aValue >> 16); + p[3] = (TUint8) (aValue >> 24); + break; + case EFat16: + p[0] = (TUint8) (aValue); + p[1] = (TUint8) (aValue >> 8); + break; + case EFat12: + { + TUint8 mask=0x0F; + TBool odd=(aFatIndex)&1; + TUint8 fatVal; + TInt value=aValue; + if(odd) + { + mask<<=4; + value<<=4; + fatVal=p[0]; + fatVal&=~mask; + fatVal|=(TUint8)(value&0xFF); + p[0]=fatVal; + p[1]=(TUint8)(value>>8); + } + else + { + p[0]=(TUint8)(value&0xFF); + fatVal=p[1]; + fatVal&=~mask; + fatVal|=(TUint8)(value>>8); + p[1]=fatVal; + } + } + break; + default: + test(0); + } + return; + } + +static void DoReadBootSector() + { + + TInt nRes = ReadBootSector(TheFs, CurrentDrive(), KBootSectorNum< 1); + switch (gDiskType) + { + case EFat12: + case EFat16: + gRootDirEntries = BootSector.RootDirEntries(); + gRootDirSectors = ((gRootDirEntries * KSizeOfFatDirEntry + BootSector.BytesPerSector() - 1) / BootSector.BytesPerSector()); + gFatSizeSectors = BootSector.FatSectors(); + gRootSector = BootSector.ReservedSectors() + BootSector.NumberOfFats() * gFatSizeSectors; + gFirstDataSector = gRootSector + gRootDirSectors; + gDataStartBytes = gFirstDataSector * BootSector.BytesPerSector(); + gRootDirStart = gRootSector * BootSector.BytesPerSector(); + gClusterRootDir = (big ? 0 : 0); + gClusterScnDrv = (big ? 2 : 2); + gClusterDir1 = (big ? 3 : 3); + gClusterDir1ext = (big ? 3 : 4); + gClusterDir2 = (big ? 5 : 6); + gClusterDir2_Full = (big ? 6 : 7); + gClusterDir2_SD3E = (big ? 7 : 8); + gClusterDir2_SD23E = (big ? 8 : 9); + gClusterDir2_AFull = (big ? 9 : 10); + gClusterEndMaxDepth = (big ? 145 : 146); + break; + case EFat32: + // + // FAT32 will alway pre-allocate a single cluster for the root directory + // + // - The following calculations may look wierd (as the spec says that the FAT32 root dir + // is not fixed) but for the purposes of this test we assume that root dir is only + // one cluster in size, so we don't fill up the disk trying to fill up the root dir. + // + gRootDirEntries = gBytesPerCluster * 1 / KSizeOfFatDirEntry; // Maximum entries within default FAT32 root directory (before extension) + gRootDirSectors = 0; // FAT32 has no fixed root directory sectors over which to skip + gFatSizeSectors = BootSector.FatSectors32(); + + gRootSector = BootSector.ReservedSectors() + BootSector.NumberOfFats() * gFatSizeSectors; + gFirstDataSector = gRootSector; + + gDataStartBytes = gFirstDataSector * BootSector.BytesPerSector(); + gRootDirStart = (BootSector.RootClusterNum() - 2) * gBytesPerCluster + gDataStartBytes; + + gClusterRootDir = (big ? 2 : 2); + gClusterScnDrv = (big ? 3 : 3); + gClusterDir1 = (big ? 4 : 4); + gClusterDir1ext = (big ? 4 : 5); + gClusterDir2 = (big ? 6 : 7); + gClusterDir2_Full = (big ? 7 : 8); + gClusterDir2_SD3E = (big ? 8 : 9); + gClusterDir2_SD23E = (big ? 9 : 10); + gClusterDir2_AFull = (big ? 10 : 11); + gClusterEndMaxDepth = (big ? 146 : 147); + break; + default: + break; + } + + gMaxDataCluster = gClusterDir2_AFull + 2 + (gFirstDataSector / BootSector.SectorsPerCluster() + 1); + + gFatTestEntries = MaxClusters(); + if (gFatTestEntries > KMaxFatSize) + gFatTestEntries = KMaxFatSize; + } + + +GLDEF_C void DumpBootSector() +// +// Display (in log) TFatBootSector structure +// + { + RDebug::Print(_L("iBytesPerSector = %8d"), BootSector.BytesPerSector()); + RDebug::Print(_L("iSectorsPerCluster = %8d"), BootSector.SectorsPerCluster()); + RDebug::Print(_L("iReservedSectors = %8d"), BootSector.ReservedSectors()); + RDebug::Print(_L("iNumberOfFats = %8d"), BootSector.NumberOfFats()); + RDebug::Print(_L("iRootDirEntries = %8d"), BootSector.RootDirEntries()); + RDebug::Print(_L("iTotalSectors = %8d"), BootSector.TotalSectors()); + RDebug::Print(_L("iMediaDescriptor = %8d"), BootSector.MediaDescriptor()); + RDebug::Print(_L("iFatSectors = %8d"), BootSector.FatSectors()); + RDebug::Print(_L("iSectorsPerTrack = %8d"), BootSector.SectorsPerTrack()); + RDebug::Print(_L("iNumberOfHeads = %8d"), BootSector.NumberOfHeads()); + RDebug::Print(_L("iHiddenSectors = %8d"), BootSector.HiddenSectors()); + RDebug::Print(_L("iHugeSectors = %8d"), BootSector.HugeSectors()); + + if (gDiskType == EFat32) + { + RDebug::Print(_L("iFatSectors32 = %8d"), BootSector.FatSectors32()); + RDebug::Print(_L("iFATFlags = %8d"), BootSector.FATFlags()); + RDebug::Print(_L("iVersionNumber = %8d"), BootSector.VersionNumber()); + RDebug::Print(_L("iRootClusterNum = %8d (0x%08X)"), BootSector.RootClusterNum(), gRootDirStart); + RDebug::Print(_L("iFSInfoSectorNum = %8d (0x%08X)"), BootSector.FSInfoSectorNum(), BootSector.FSInfoSectorNum() * BootSector.BytesPerSector()); + RDebug::Print(_L("iBkBootRecSector = %8d (0x%08X)"), BootSector.BkBootRecSector(), BootSector.BkBootRecSector() * BootSector.BytesPerSector()); + } + } + +GLDEF_C 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("--------------------------------------------")); + } + +GLDEF_C TDes* DirAttributes(TInt aAttrib) +// +// Return a pointer to a local buffer containing the attribute letters. +// + { + LOCAL_D TBuf<6> str; + LOCAL_D char* atr = "RHSVDA"; + str.Fill(TText('-'), 6); + for (TInt i = 0; i < 6; i++) + if ((aAttrib >> i) & 1) + str[i] = atr[i]; + return &str; + } + +GLDEF_C TBool IsValidDirEntry(TFatDirEntry* aDir) +// +// Test whether buffer is a valid normal directory entry +// + { + // first character must be 0x05 or greater than space + if (aDir->iData[0] < 0x21 && aDir->iData[0] != 0x05) + return EFalse; + // other characters must be not less than space + for (TInt i = 1; i < 11; i++) + if (aDir->iData[i] < 0x20) + return EFalse; + return ETrue; + } + +GLDEF_C 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); + } + } + +GLDEF_C 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); + } + +GLDEF_C 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()) + { + if (aNum > 0) + 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)) + { + if (aNum > 0) + RDebug::Print(_L("%5d: ============= INVALID ENTRY"), aNum); + return EFalse; + } + else + { + TBuf<11> name; + name.Copy(d->Name()); + RDebug::Print(_L("%5d: '%S' %S start %-5d size %d"), + aNum, &name, DirAttributes(d->Attributes()), d->StartCluster(), d->Size()); + } + return ETrue; + } + +GLDEF_C 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; + } + } + +GLDEF_C void DumpRootDir(const TUint8* aData) +// +// Dump the data area buffer, trying to interpret directory clusters (only look +// at clusters which are marked as 'used' in the FAT). +// + { + RDebug::Print(_L("Root dir @ 0x%08X:"), gRootDirStart); + for (TInt i = 0; i < BootSector.RootDirEntries(); i++) + { + if (DumpDirEntry(i, aData)) + aData += KSizeOfFatDirEntry; + else + break; + } + } + +GLDEF_C void DumpData(const TUint8* aFat, const TUint8* aDir) +// +// Dump the data area buffer, trying to interpret directory clusters (only look +// at clusters which are marked as 'used' in the FAT). +// + { + RDebug::Print(_L("--------------- DATA AREA ------------------")); + if (gDiskType != EFat32) + { + DumpRootDir(aDir); + } + TInt max = (gFatTestEntries < gMaxDataCluster ? gFatTestEntries : gMaxDataCluster); + for (TInt cluster = 2; cluster < max; cluster++) + { + if (GetFatEntry(cluster, aFat) != 0) + { + RDebug::Print(_L("Cluster %d @ 0x%08X:"), cluster, ClusterToByte(cluster)); + DumpDirCluster(aDir+gDataStartBytes-gRootDirStart, cluster); + } + } + RDebug::Print(_L("--------------------------------------------")); + } + +GLDEF_C 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; + RDebug::Print(_L("--------------- DATA AREA ------------------")); + if (aEnd > gFatTestEntries) + aEnd = gFatTestEntries; + if (aEnd < 0) + aEnd = aStart + 1; + if (aStart < 2 && gDiskType != EFat32) + { + HBufC8* buf=HBufC8::New(BootSector.RootDirEntries() * KSizeOfFatDirEntry); + test(buf != NULL); + TPtr8 ptr=buf->Des(); + TInt r=TheRawDisk.Open(TheFs,gSessionPath[0]-'A'); + test(r==KErrNone); + r=TheRawDisk.Read(gRootDirStart, ptr); + test(r==KErrNone); + TheRawDisk.Close(); + DumpRootDir(buf->Ptr()); + delete(buf); + aStart = 2; + } + 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=TheRawDisk.Open(TheFs,gSessionPath[0]-'A'); + test(r==KErrNone); + r=TheRawDisk.Read(ClusterToByte(cluster), ptr); + test(r==KErrNone); + TheRawDisk.Close(); + RDebug::Print(_L("Cluster %d @ 0x%08X:"), cluster, ClusterToByte(cluster)); + DumpDirCluster(ptr.Ptr()); + delete buf; + } + } + RDebug::Print(_L("--------------------------------------------")); + } + +GLDEF_C void DumpHex(const TUint8* aData, TInt aLen, TInt aBase=0) +// +// Dump a block of memory to the log in hex. +// + { + for (TInt base = 0; base < aLen; base += 16) + { + TBuf<16*3+3+16+1> buf; + TInt off; + for (off = base; off < aLen && off < base + 16; off++) + { + buf.Append(TText(' ')); + buf.AppendNumFixedWidth(aData[off], EHex, 2); + } + buf.Append(_L(" |")); + for (off = base; off < aLen && off < base + 16; off++) + { + TUint8 ch = aData[off]; + buf.Append(ch < 0x20 || ch > 0x7E ? TText('_') : TText(ch)); + } + buf.Append(_L("|")); + RDebug::Print(_L("%04X: %S"), base+aBase, &buf); + } + } + +GLDEF_C void DumpHex(const TPtrC8& aData, TInt aLen, TInt aBase=0) +// +// Dump a block of memory to the log in hex. +// + { + DumpHex(aData.Ptr(), aLen, aBase); + } + +GLDEF_C void DumpHex(TInt aPos, TInt aLen, TInt aBase=0) +// +// Dump a block of memory to the log in hex. +// + { + TPtr8 dirBuf=DirBufPtr->Des(); + DumpHex(dirBuf.Ptr()+aPos, aLen, aBase); + } + +GLDEF_C void Dump(TEntryInfo& aEntry) +// +// Dump an entry description to the log in hex +// + { + RDebug::Print(_L("--- TEntryInfo 0x%08X, %d"), aEntry.iBytePos, aEntry.iLength); + TInt len = aEntry.iLength*KSizeOfFatDirEntry; + DumpHex(aEntry.iBytePos, len, aEntry.iBytePos); + } + +LOCAL_C TInt GetStartCluster(TInt aCluster, TInt aEntry) +// +// Get the start cluster pertaining to a directory entry in a specific +// directory cluster, return -1 if not available (invalid entry). +// + { + HBufC8* buf=HBufC8::New(gBytesPerCluster*2); + test(buf!=NULL); + TPtr8 ptr=buf->Des(); + TInt r=TheRawDisk.Open(TheFs,gSessionPath[0]-'A'); + test(r==KErrNone); + r=TheRawDisk.Read(ClusterToByte(aCluster), ptr); + test(r==KErrNone); + TheRawDisk.Close(); + RDebug::Print(_L("Cluster %d @ 0x%08X:"), aCluster, ClusterToByte(aCluster)); + TFatDirEntry* d = (TFatDirEntry*)ptr.Ptr() + aEntry; + while ((d->Attributes() & KDirAttrLongMask) == KDirAttrLongName && aEntry < gEntriesPerCluster) + { + if (d->IsErased() || d->IsEndOfDirectory()) + break; + ++aEntry; + d = (TFatDirEntry*)ptr.Ptr() + aEntry; + } + TInt start = d->StartCluster(); + if (d->IsErased()) + start = -1; + else if (d->IsEndOfDirectory()) + start = -1; + else if ((d->Attributes() & KDirAttrLongMask) == KDirAttrLongName) + start = -1; + else if (!IsValidDirEntry(d)) + start = -1; + delete buf; + return start; + } + +LOCAL_C void Format() + { + TInt nRes; + +#if 0 + TFatFormatParam fmt; + fmt.iFatType = EFat32; + fmt.iSecPerCluster = 1; + + nRes = FormatFatDrive(TheFs, CurrentDrive(), ETrue, &fmt); +#else + nRes = FormatFatDrive(TheFs, CurrentDrive(), ETrue); +#endif + + test(nRes == KErrNone); + + } + +LOCAL_C void CreateDeepDir(TFileName& aDir,TInt aDepth) +// +// Increase directory strucutre by aDepth starting with aDir. +// + { + TFileName num; + num.Num(1); + num+=_L("\\"); + TInt r; + while(aDepth--) + { + num[0] = TText(aDepth % 26 + 'A'); + aDir+=num; + r=TheFs.MkDir(aDir); + test(r==KErrNone); + } + } + +LOCAL_C void DeleteDeepDir(TFileName& aDir,TInt aDepth) +// +// Delete dir structure. +// + { + TInt r; + while(aDepth--) + { + r=TheFs.RmDir(aDir); + test(r==KErrNone); + aDir.SetLength(aDir.Length()-2); + } + } + +LOCAL_C void CreateMaxDepthDir(TFileName& aDir1,TFileName& aDir2) +// +// Create directory structure with max possible depth-1. +// Achieved by using dir names of one character. +// + { + //create dir structure with depth of 25 + CreateDeepDir(aDir1,25); + // split dir structure + aDir2=aDir1; + aDir2+=_L("a\\"); + TInt r=TheFs.MkDir(aDir2); + test(r==KErrNone); + // create dir with depth of 126 directories - one short of max depth + CreateDeepDir(aDir1,101); + // create dir with depth of 90 + CreateDeepDir(aDir2,64); + } + +LOCAL_C void DeleteMaxDepthDir(TFileName&aDir1,TFileName&aDir2) +// +// Deletes max depth dir structure. +// + { + DeleteDeepDir(aDir2,64); + TInt r=TheFs.RmDir(aDir2); + test(r==KErrNone); + aDir2.SetLength(aDir2.Length()-2); + DeleteDeepDir(aDir1,102); + DeleteDeepDir(aDir1,24); + } + +LOCAL_C void MakeVeryLongName(TFileName& aLong) +// +// appends a very long file name to aLong +// + { + // create a name to take up 18 vfat entries - (1 sector + 2 entries) + for(TInt i=0;i<234;++i) + { + TInt c='a'+i%26; + aLong.Append(c); + } + } + +GLDEF_C void CreateLongNames(TFileName& aLong, TInt aClusters) +// +// Creates entries to fill up the number of directory clusters +// + { + TInt len = aLong.Length(); // length of directory prefix + MakeVeryLongName(aLong); + TInt count = 0; + RFile temp; + for (TInt i = 0; i < aClusters * gEntriesPerCluster; i += 19) + { + aLong[len+0] = TText(count/26 + 'A'); + aLong[len+1] = TText(count%26 + 'A'); + count++; + TInt r=temp.Create(TheFs,aLong,EFileShareAny); + test(r==KErrNone); + temp.Close(); + } + } + +GLDEF_C void DeleteLongNames(TFileName& aLong, TInt aClusters) +// +// Deletes entries created by CreateLongNames() +// + { + TInt len = aLong.Length(); // length of directory prefix + MakeVeryLongName(aLong); + TInt count = 0; + for (TInt i = 0; i < aClusters * gEntriesPerCluster; i += 19) + { + aLong[len+0] = TText(count/26 + 'A'); + aLong[len+1] = TText(count%26 + 'A'); + count++; + TInt r=TheFs.Delete(aLong); + test(r==KErrNone || r==KErrNotFound); + } + } + +LOCAL_C void DeleteRootDir(TInt aNum=0) +// +// Delete all entries in the root directory up to the last - aNum +// + { + test.Next(_L("Delete Root Directory Entries")); + TInt maxRootEntries = gRootDirEntries; + TFileName name=_L("\\???xxx"); + TInt count=0; + TInt entriesSoFar=2; + TInt r; + count = 0; + for (entriesSoFar=0; entriesSoFar2) + { + dir[1]=TUint16(count/26+'a'); + dir[2]=TUint16(count%26+'a'); + r=TheFs.RmDir(dir); + test(r==KErrNone); + entriesSoFar-=2; + ++count; + } + } + +LOCAL_C void DeleteDirectoryStructure() +// +// deletes the directory structure +// + { + test.Next(_L("Delete Directory Structure")); + TInt r=TheFs.RmDir(_L("\\scndrv\\dir2\\almostfull\\")); + test(r==KErrNone); + TInt entriesNeeded=(gEntriesPerCluster-2) / 2; //7*2entries + . + .. = full sector + for (TInt i = 0; i < entriesNeeded; i++) + { + TFileName file=_L("\\scndrv\\dir2\\full\\__a"); + file.AppendNum(i); + r=TheFs.Delete(file); + test(r==KErrNone||r==KErrNotFound); + } + r=TheFs.RmDir(_L("\\scndrv\\dir2\\full\\")); + test(r==KErrNone); + r=TheFs.RmDir(_L("\\scndrv\\dir2\\")); + test(r==KErrNone); + TFileName veryLongName=(_L("\\scndrv\\dir1\\")); + MakeVeryLongName(veryLongName); + r=TheFs.Delete(veryLongName); + test(r==KErrNone); + r=TheFs.RmDir(_L("\\scndrv\\dir1\\")); + test(r==KErrNone); + r=TheFs.RmDir(_L("\\scndrv\\")); + test(r==KErrNone); + } + +LOCAL_C void CreateDirectoryStructure() +// +// creates the directory structure +// + { + test.Next(_L("Create Directory Structure")); + // cluster 3 (root dir is cluster 2) + TInt r=TheFs.MkDir(_L("\\scndrv\\")); + test(r==KErrNone); + // cluster 4 + r=TheFs.MkDir(_L("\\scndrv\\dir1\\")); + test(r==KErrNone); + TFileName veryLongName=(_L("\\scndrv\\dir1\\")); + MakeVeryLongName(veryLongName); + RFile f; + // cluster 5 + r=f.Create(TheFs,veryLongName,EFileShareAny); + test(r==KErrNone); + r=f.SetSize(512); + test(r==KErrNone); + f.Close(); + // cluster 6 + r=TheFs.MkDir(_L("\\scndrv\\dir2\\")); + test(r==KErrNone); + // cluster 7 + r=TheFs.MkDir(_L("\\scndrv\\dir2\\full\\")); + test(r==KErrNone); + // cluster 8 + r=TheFs.MkDir(_L("\\scndrv\\dir2\\somedirwith3entries\\")); + test(r==KErrNone); + // cluster 9 + r=TheFs.MkDir(_L("\\scndrv\\dir2\\somedir2with3entries\\")); + test(r==KErrNone); + // cluster 10 + r=TheFs.MkDir(_L("\\scndrv\\dir2\\almostfull\\")); + test(r==KErrNone); + // cluster 11-17 + TInt entriesNeeded=(gEntriesPerCluster-2) / 2; //7*2entries + . + .. = full sector + for (TInt i = 0; i < entriesNeeded; i++) + { + TFileName file=_L("\\scndrv\\dir2\\full\\__a"); + file.AppendNum(i); + LastInFull = file; + r=f.Create(TheFs,file,EFileShareAny); + test(r==KErrNone); + if (i < 7) + { + r=f.SetSize(512); + test(r==KErrNone); + } + f.Close(); + } + // cluster 18-19 + TInt charLength=13*4+1; // name to take up 6 entries + TFileName file1=_L("\\scndrv\\dir2\\almostfull\\"); + while(charLength--) + { + TInt c='A'+charLength%26; + file1.Append(c); + } + TFileName file2=file1; + file1.AppendNum(1); + file2.AppendNum(2); + r=f.Create(TheFs,file1,EFileShareAny); + test(r==KErrNone); + r=f.SetSize(512); + test(r==KErrNone); + f.Close(); + r=f.Create(TheFs,file2,EFileShareAny); + test(r==KErrNone); + r=f.SetSize(512); + test(r==KErrNone); + f.Close(); + } + +LOCAL_C TUint8* DirPtr(TInt aOffset) +// +// Return a pointer to the offset in the dir buffer, or in the special +// extension buffer if the dir buffer isn't large enough. If the extension +// buffer is needed, it is read from disk when it is created (2 clusters, to +// allow for entries spanning cluster boundaries). Errors will occur if +// another cluster is needed before the extension has been released. +// +// Note that if an extension buffer is allocated then writing a dir buffer to +// disk will also write the extension buffer and any changes made in it. +// + { + // if the offset is in store, return its byte address + if (aOffset < DirBufPtr->Des().MaxLength()) + return (TUint8*)DirBufPtr->Ptr() + aOffset; + // not in main buffer, see if extension is allocated already + if (!ExtBufPtr) + { + // allocate buffer for 2 clusters, starting at the cluster which + // contains aOffset + ExtBufLen = 2 * gBytesPerCluster; + ExtBufPtr = HBufC8::New(ExtBufLen); + test(ExtBufPtr != NULL); + // read the clusters in + ExtBufAdd = aOffset - aOffset % gBytesPerCluster; + TInt clust = (ExtBufAdd - (gDataStartBytes - gRootDirStart)) /gBytesPerCluster + 2; + RDebug::Print(_L("Extension buffer for cluster %d allocated"), clust); + TInt r=TheRawDisk.Open(TheFs,gSessionPath[0]-'A'); + test(r==KErrNone); + TPtr8 des(ExtBufPtr->Des()); + r=TheRawDisk.Read(gRootDirStart + ExtBufAdd, des); + test(r==KErrNone); + TheRawDisk.Close(); + } + // convert to offset in the extension buffer + aOffset -= ExtBufAdd; + if (aOffset >= ExtBufLen) + { + test.Printf(_L("*** ERROR: tried to access cluster %d not in store\n"), + (aOffset + ExtBufAdd) / gBytesPerCluster + 2); + test(0); + } + return (TUint8*)ExtBufPtr->Ptr() + aOffset; + } + +LOCAL_C void DirPtrFree() +// +// Free the extension buffer and zero the references to it. +// + { + if (ExtBufPtr) + { + TInt clust = (ExtBufAdd - (gDataStartBytes - gRootDirStart)) /gBytesPerCluster + 2; + RDebug::Print(_L("Extension buffer for cluster %d deleted"), clust); + delete ExtBufPtr; + ExtBufPtr = NULL; + ExtBufAdd = 0; + ExtBufLen = 0; + } + } + +LOCAL_C void ReadDirDisk(TDes8& aDirBuf, TInt aCluster = -1) +// +// reads directory section of disk into buffer +// + { + test(aCluster != 1); + + TInt r=TheRawDisk.Open(TheFs,gSessionPath[0]-'A'); + test(r==KErrNone); + + if (aCluster == -1) // all clusters ? + { + r=TheRawDisk.Read(gRootDirStart, aDirBuf); + } + else if (aCluster == 0) // root directory cluster + { + TPtr8 dirPtr = aDirBuf.MidTPtr(0, gBytesPerCluster); + r=TheRawDisk.Read(gRootDirStart, dirPtr); + } + else + { + // directory buffer starts at root directory, so + // calculate offset from there. + TInt pos = ((aCluster - 2) * gBytesPerCluster) + + (gRootDirSectors * BootSector.BytesPerSector()); + TPtr8 dirPtr = aDirBuf.MidTPtr(pos, gBytesPerCluster); + r=TheRawDisk.Read(gRootDirStart + pos, dirPtr); + } + + test(r==KErrNone); + TheRawDisk.Close(); + } + +LOCAL_C void ReadFatDisk(TDes8& aFatBuf) +// +// reads fat section of disk info buffer +// + { + TInt r=TheRawDisk.Open(TheFs,gSessionPath[0]-'A'); + test(r==KErrNone); + r=TheRawDisk.Read(gFatStartBytes, aFatBuf); + test(r==KErrNone); + TheRawDisk.Close(); + } + +LOCAL_C void WriteDirDisk(TDes8& aDirBuf, TInt aCluster = -1) +// +// writes dir buffer to disk +// + { + test(aCluster != 1); + + TInt r=TheRawDisk.Open(TheFs,gSessionPath[0]-'A'); + test(r==KErrNone); + + if (aCluster == -1) + { + r=TheRawDisk.Write(gRootDirStart, aDirBuf); + } + else if (aCluster == 0) // root directory cluster + { + TPtr8 dirPtr = aDirBuf.MidTPtr(0, gBytesPerCluster); + r=TheRawDisk.Write(gRootDirStart, dirPtr); + } + else + { + // directory buffer starts at root directory, so + // calculate offset from there. + TInt pos = ((aCluster - 2) * gBytesPerCluster) + + (gRootDirSectors * BootSector.BytesPerSector()); + TPtr8 dirPtr = aDirBuf.MidTPtr(pos, gBytesPerCluster); + r=TheRawDisk.Write(gRootDirStart + pos, dirPtr); + } + + test(r==KErrNone); + if (ExtBufPtr) + { + TPtr8 des(ExtBufPtr->Des()); + r=TheRawDisk.Write(gRootDirStart + ExtBufAdd, des); + test(r==KErrNone); + } + TheRawDisk.Close(); + } + +LOCAL_C void WriteFatDisk(TDes8& aFatBuf, TInt aStart=0) +// +// writes fat buffer to disk +// + { + TInt r=TheRawDisk.Open(TheFs,gSessionPath[0]-'A'); + test(r==KErrNone); + TInt fatCount=BootSector.NumberOfFats() - aStart; + TInt pos = gFatStartBytes + aStart * gFatSizeSectors*BootSector.BytesPerSector(); + while(fatCount--) + { + r=TheRawDisk.Write(pos, aFatBuf); + test(r==KErrNone); + pos += gFatSizeSectors*BootSector.BytesPerSector(); + } + TheRawDisk.Close(); + } + +LOCAL_C void WriteReservedId(TInt aBytePos) +// +// write reserved id to byte 19 of entry starting at aBytePos +// + { + TUint8* pEntry=DirPtr(aBytePos); + pEntry[19]=1; + } + +LOCAL_C void WriteEndOfDir(TInt aBytePos) +// +// write end of directory marker to entry starting at aBytePos +// + { + TUint8* pEntry=DirPtr(aBytePos); + Mem::FillZ(pEntry,KFatDirNameSize); + } + +LOCAL_C void WriteDelete(TInt aBytePos,TInt aNum) +// +// writes 0xe5 to entries starting at aBytePos +// + { + TUint8* pEntry=DirPtr(aBytePos); + while(aNum--) + { + pEntry[0]=0xE5; + pEntry+=KSizeOfFatDirEntry; + } + } + +LOCAL_C void WriteCopyDir(TInt aSrc, TInt aDst) +// +// Copy one directory entry over another +// + { + TUint8* pSrc=DirPtr(aSrc); + TUint8* pDst=DirPtr(aDst); + Mem::Copy(pDst, pSrc, KSizeOfFatDirEntry); + } + +LOCAL_C void InitialiseBuffers() +// +// reads disk into buffers +// + { + gFatTestEntries = MaxClusters(); + if (gFatTestEntries > KMaxFatSize) + gFatTestEntries = KMaxFatSize; + gFatTestSize = PosInBytes(gFatTestEntries); + FatBufPtr=HBufC8::New(gFatTestSize); + test(FatBufPtr!=NULL); + DirBufPtr=HBufC8::New(DirBufferSize()); + test(DirBufPtr!=NULL); + + // Buffers for reading from disk + FatDiskPtr=HBufC8::New(gFatTestSize); + test(FatDiskPtr!=NULL); + DirDiskPtr=HBufC8::New(DirBufferSize()); + test(DirDiskPtr!=NULL); + } + +LOCAL_C TBool IsSameAsDrive(const TDes8& aFatBuf,const TDes8& aDirBuf) +// +// compares the two bufs passed in with those on disk +// + { + TPtr8 fatDisk=FatDiskPtr->Des(); + TPtr8 dirDisk=DirDiskPtr->Des(); + ReadDirDisk(dirDisk); + ReadFatDisk(fatDisk); + TBool fatOk = (aFatBuf.Compare(fatDisk)==0); + TBool dirOk = (aDirBuf.Compare(dirDisk)==0); + if (!fatOk) + { + TInt i = FindUnMatch(aFatBuf.Ptr(), fatDisk.Ptr(), fatDisk.Length()); + switch (gDiskType) + { + case EFat32: + i /= 4; + + if(i == 1) + {//-- mismatch in FAT[1] for FAT16/FAT32 it is OK because FAT[1] is used by volume finalisation + //-- to store Volume Clean Shutdown flag + fatOk = ETrue; + } + + break; + + case EFat16: + i /= 2; + + if(i == 1) + {//-- mismatch in FAT[1] for FAT16/FAT32 it is OK because FAT[1] is used by volume finalisation + //-- to store Volume Clean Shutdown flag + fatOk = ETrue; + } + + break; + + case EFat12: + i = i*2 / 3; + if (GetFatEntry(i, aFatBuf.Ptr()) == GetFatEntry(i, fatDisk.Ptr())) + ++i; + break; + default: + test(0); + } + + if(fatOk && i==1) + { + test.Printf(_L("Skipping FAT[1] entry for FAT16/32 \n"), i); + } + else + { + test.Printf(_L("FAT entry %d different from expected\n"), i); + + RDebug::Print(_L("Expected:\n")); + DumpFat(aFatBuf.Ptr()); + RDebug::Print(_L("Actual:\n")); + DumpFat(fatDisk.Ptr()); + } + } + + if (!dirOk) + { + TInt i = FindUnMatch(aDirBuf.Ptr(), dirDisk.Ptr(), dirDisk.Length()); + TInt clust = ByteToCluster(i); + TInt entry = i % gBytesPerCluster / KSizeOfFatDirEntry; + test.Printf(_L("DIR different from expected\n")); + test.Printf(_L(" at pos %d sector %d cluster %d entry %d:\n"), i, i / BootSector.BytesPerSector(), clust, entry); + + RDebug::Print(_L("Expected:\n")); + DumpHex(aDirBuf.Ptr() + i, 32); + RDebug::Print(_L("-------------")); + RDebug::Print(_L("Actual:\n")); + DumpHex(dirDisk.Ptr() + i, 32); + + RDebug::Print(_L("Expected:\n")); + DumpData(aFatBuf.Ptr(), aDirBuf.Ptr()); + RDebug::Print(_L("Actual:\n")); + DumpData(fatDisk.Ptr(), dirDisk.Ptr()); + } + else if (ExtBufPtr) + { + HBufC8* extPtr = HBufC8::New(ExtBufLen); + test(extPtr != NULL); + TInt r=TheRawDisk.Open(TheFs,gSessionPath[0]-'A'); + test(r==KErrNone); + TPtr8 des(extPtr->Des()); + r=TheRawDisk.Read(ExtBufAdd+gRootDirStart, des); + test(r==KErrNone); + TheRawDisk.Close(); + TInt i = FindUnMatch(ExtBufPtr->Ptr(), extPtr->Ptr(), ExtBufLen); + if (i >= 0) + { + TInt extcl = (ExtBufAdd - (gDataStartBytes-gRootDirStart)) / gBytesPerCluster + 2; + TInt clust = i / gBytesPerCluster; + TInt entry = i % gBytesPerCluster / KSizeOfFatDirEntry; + test.Printf(_L("DIR different from expected\n")); + test.Printf(_L(" at cluster %d entry %d:\n"), extcl+clust, entry); + DumpHex(ExtBufPtr->Ptr() + clust * gBytesPerCluster + entry * KSizeOfFatDirEntry, 32); + RDebug::Print(_L("-------------")); + DumpHex(extPtr->Ptr() + clust * gBytesPerCluster + entry * KSizeOfFatDirEntry, 32); + // RDebug::Print(_L("Expected:\n")); + // DumpData(aFatBuf.Ptr(), aDirBuf.Ptr()); + // RDebug::Print(_L("Actual:\n")); + // DumpData(fatDisk.Ptr(), dirDisk.Ptr()); + dirOk = EFalse; + } + delete extPtr; + } + + return(fatOk && dirOk); + } + +LOCAL_C void WriteErased(TEntryInfo aTrg,TInt aToDelete) +// +// writes erased marker, starting at dos entry and working backwards +// used to simulate a part entry40*BootSector.BytesPerSector() +// + { + TInt toStart=aTrg.iBytePos+(aTrg.iLength-1)*KSizeOfFatDirEntry; + TPtr8 dirBuf=DirBufPtr->Des(); + while(aToDelete--) + { + dirBuf[toStart]=0xE5; + toStart-=KSizeOfFatDirEntry; + } + } + +LOCAL_C void CreatePartialEntry(TEntryInfo aTrg,TInt aToDelete,TBool aAddEOfDir) +// +// creates a partial entry +// + { + WriteErased(aTrg,aToDelete); + if(aAddEOfDir) + WriteEndOfDir(aTrg.iBytePos+aTrg.iLength*KSizeOfFatDirEntry); + TPtr8 dirBuf=DirBufPtr->Des(); + WriteDirDisk(dirBuf); + } + +LOCAL_C TBool TestPartialEntry(TEntryInfo aEntry) +// +// tests that scandrive deals with a partial entry and returns the result +// + { + test.Next(_L("TestPartialEntry")); + TInt r=TheFs.ScanDrive(gSessionPath); + test(r==KErrNone); + WriteDelete(aEntry.iBytePos,aEntry.iLength); + + TPtr8 fatBuf=FatBufPtr->Des(); + TPtr8 dirBuf=DirBufPtr->Des(); + + TBool res=IsSameAsDrive(fatBuf,dirBuf); + return(res); + } + +LOCAL_C void CreateMatchingEntry(TEntryInfo aTrg,TEntryInfo aSrc,TBool aAddEOfDir) +// +// creates matching entry +// + { + test.Next(_L("Create entry with start cluster already used")); + TUint8* src = DirPtr(aSrc.iBytePos); + TUint8* dst = DirPtr(aTrg.iBytePos); + Mem::Copy(dst, src, aSrc.iLength*KSizeOfFatDirEntry); + WriteReservedId(aTrg.iBytePos+(aTrg.iLength-1)*KSizeOfFatDirEntry); + if(aAddEOfDir) + WriteEndOfDir(aTrg.iBytePos+aTrg.iLength*KSizeOfFatDirEntry); + TPtr8 dirBuf=DirBufPtr->Des(); + WriteDirDisk(dirBuf); + } + +LOCAL_C TBool TestMatchingEntry(TEntryInfo aToDelete) +// +// tests that scandrive deals with matching entries correctly +// + { + test.Next(_L("TestMatchingEntries")); + WriteDelete(aToDelete.iBytePos,aToDelete.iLength); + TInt r=TheFs.ScanDrive(gSessionPath); + test(r==KErrNone); + + TPtr8 fatBuf=FatBufPtr->Des(); + TPtr8 dirBuf=DirBufPtr->Des(); + + TBool res=IsSameAsDrive(fatBuf,dirBuf); + DirPtrFree(); + return(res); + } + + +LOCAL_C void TestExtendedChars() +// +// tests that extended characters corresponding to ISO Latin 1 +// characters 128-255 are recognised as valid by scandrive +// + { + test.Next(_L("TestExtendedChars()")); + Format(); + + _LIT(KRoot,"\\"); + CDir* dirs; + // check no entries in the root directory + TInt r=TheFs.GetDir(KRoot,KEntryAttMaskSupported,ESortNone,dirs); + test(r==KErrNone); + test(dirs->Count()==0); + delete(dirs); + dirs=NULL; + + // create file + _LIT(KOrigShortName,"P_SSI.TXT"); + + //_LIT(KTestFile,"\\p\xE4ssi.txt"); //-- this causes problems for VC6 and default locale different from English + TBuf<64> TestFileName(_L("\\p$ssi.txt")); + TestFileName[2] = 0xe4; //-- replace '$' with this code + + //_LIT(KExtShortName,"P\xC4SSI.TXT"); //-- this causes problems for VC6 and default locale different from English + TBuf<64> ExtShortName(_L("P$SSI.TXT")); + ExtShortName[1] = 0xC4; //-- replace '$' with this code + + + RFile file; + r=file.Replace(TheFs,TestFileName,EFileShareExclusive); + test(r==KErrNone); + file.Close(); + + // get short name + TFileName shortName; + r=TheFs.GetShortName(TestFileName,shortName); + test(r==KErrNone); + test(shortName==KOrigShortName); + + // must be first entry in root, modify to read like + // a windows-generated short name (ie. contains extended character) + DumpData(NULL, 0, 20); + TInt bytePos=ClusterEntryToBytes(0,1); + RRawDisk raw; + r=raw.Open(TheFs,gSessionPath[0]-'A'); + test(r==KErrNone); + TBuf8<1> buf(1); + + //-- change 2nd character in the short name (Fat DOS entry) + buf[0]=(TUint8)'\xC4'; + r=raw.Write(gRootDirStart+bytePos+1,buf); + test(r==KErrNone); + + //-- fix the fiddled short name checksum in the corresponding VFat entry + bytePos=ClusterEntryToBytes(0,0); + buf[0]=(TUint8)0x2f; + r=raw.Write(gRootDirStart+bytePos+13,buf); + test(r==KErrNone); + + // retrieve short name from media. + // Note: do not use RFs::GetShortName() as its behaviours are code page dependent. + bytePos=ClusterEntryToBytes(0,1); + TBuf8<11> shortNameBuf8; + r=raw.Read(gRootDirStart+bytePos,shortNameBuf8); + test(r==KErrNone); + shortNameBuf8 = DosNameFromStdFormat(shortNameBuf8); + shortName.Copy(shortNameBuf8); + raw.Close(); + + + test(shortName==ExtShortName); + DumpData(NULL, 0, 20); + //TheFs.SetDebugRegister(KFSYS); + r=TheFs.ScanDrive(gSessionPath); + TheFs.SetDebugRegister(0); + test(r==KErrNone); + DumpData(NULL, 0, 20); + + // retrieve short name from media. + r=raw.Open(TheFs,gSessionPath[0]-'A'); + test(r==KErrNone); + bytePos=ClusterEntryToBytes(0,1); + r=raw.Read(gRootDirStart+bytePos,shortNameBuf8); + test(r==KErrNone); + shortNameBuf8 = DosNameFromStdFormat(shortNameBuf8); + shortName.Copy(shortNameBuf8); + raw.Close(); + + test(shortName==ExtShortName); + + // delete file + r=TheFs.Delete(TestFileName); + test(r==KErrNone); + } + +LOCAL_C void TestMountAndScan() +// +// test MountFileSystemAndScan() +// + { + TFullName extName; + TBool primaryExtensionExists = EFalse; + + test.Next(_L("TestMountAndScan")); + HBufC8* newFat=HBufC8::New(gFatTestSize); + test(newFat!=NULL); + TPtr8 fat=newFat->Des(); + TPtr8 origFat=FatBufPtr->Des(); + TPtr8 origDir=DirBufPtr->Des(); + + // set cluster of \scndrv\dir1\ to a hanging cluster + ReadFatDisk(fat); + WriteFat(gClusterDir1ext,35,fat.Ptr()); + WriteFat(35,36,fat.Ptr()); + WriteFatDisk(fat); + // set the default path to something other than the current drive + TFileName fsName; + TInt r=TheFs.FileSystemName(fsName,gSessionPath[0]-'A'); + test(r==KErrNone); + TFileName origDefPath, newDefPath; + r=TheFs.SessionPath(origDefPath); + test(r==KErrNone); + newDefPath=origDefPath; + newDefPath[0]=(TText)'z'; + r=TheFs.SetSessionPath(newDefPath); + test(r==KErrNone); + r = TheFs.ExtensionName(extName,gSessionPath[0]-'A',0); + if (r == KErrNone) + { + primaryExtensionExists = ETrue; + } + r=TheFs.DismountFileSystem(fsName,gSessionPath[0]-'A'); + test(r==KErrNone); + // mount file system and check scandrive corrects error + TBool isMount; + if (primaryExtensionExists) + r=TheFs.MountFileSystemAndScan(fsName,extName,gSessionPath[0]-'A',isMount); + else + r=TheFs.MountFileSystemAndScan(fsName,gSessionPath[0]-'A',isMount); + test(isMount && r==KErrNone); + TBool res=IsSameAsDrive(origFat,origDir); + test(res); + + r=TheFs.SetSessionPath(origDefPath); + test(r==KErrNone); + delete newFat; + } + + +LOCAL_C void TestConsecutiveMountAndScans() +// +// test fix for DEF093072: [codebase]MountFileSystemAndScan returns err -21 but ok flag +// + { + TFullName extName; + TBool primaryExtensionExists = EFalse; + TFileName fsName; + TInt r=TheFs.FileSystemName(fsName,gSessionPath[0]-'A'); + test(r==KErrNone); + r = TheFs.ExtensionName(extName,gSessionPath[0]-'A',0); + if (r == KErrNone) + { + primaryExtensionExists = ETrue; + } + r=TheFs.DismountFileSystem(fsName,gSessionPath[0]-'A'); + test(r==KErrNone); + + // RFs::MountFileSystemAndScan twice consecutively + // first time + TBool isMount; + if (primaryExtensionExists) + r=TheFs.MountFileSystemAndScan(fsName,extName,gSessionPath[0]-'A',isMount); + else + r=TheFs.MountFileSystemAndScan(fsName,gSessionPath[0]-'A',isMount); + test(isMount && r==KErrNone); + // and a second time + if (primaryExtensionExists) + r=TheFs.MountFileSystemAndScan(fsName,extName,gSessionPath[0]-'A',isMount); + else + r=TheFs.MountFileSystemAndScan(fsName,gSessionPath[0]-'A',isMount); + test(!isMount && r==KErrAccessDenied); + } + +LOCAL_C void DoHangingClusters() +// +// Tests that scandrive removes hanging clusters +// + { + test.Next(_L("Check Hanging clusters")); + HBufC8* newFat=HBufC8::New(gFatTestSize); + test(newFat!=NULL); + TPtr8 fat=newFat->Des(); + TPtr8 origFat=FatBufPtr->Des(); + TPtr8 origDir=DirBufPtr->Des(); + + // set cluster of \scndrv\dir1\ to a hanging cluster + test.Start(_L("Test hanging cluster in \\scndrv\\dir1\\")); + ReadFatDisk(fat); + WriteFat(gClusterDir1ext,35,fat.Ptr()); + WriteFat(35,36,fat.Ptr()); + WriteFatDisk(fat); + TInt r=TheFs.ScanDrive(gSessionPath); + test(r==KErrNone); + TBool res=IsSameAsDrive(origFat,origDir); + test(res); + + // set cluster chain of first entry of \scndrv\dir1\ to + // larger size than file size + test.Next(_L("Test hanging cluster in first entry")); + ReadFatDisk(fat); + WriteFat(gClusterDir1ext,39,fat.Ptr()); + WriteFat(39,500,fat.Ptr()); + WriteFat(500,gEndOfChain,fat.Ptr()); + WriteFatDisk(fat); + r=TheFs.ScanDrive(gSessionPath); + test(r==KErrNone); + res=IsSameAsDrive(origFat,origDir); + test(res); + + // set cluster of \scndrv\ to a hanging cluster + test.Next(_L("Test hanging cluster of \\scndrv\\")); + ReadFatDisk(fat); + WriteFat(gClusterScnDrv,511,fat.Ptr()); + WriteFatDisk(fat); + r=TheFs.ScanDrive(gSessionPath); + test(r==KErrNone); + res=IsSameAsDrive(origFat,origDir); + test(res); + + delete newFat; + test.End(); + } + +LOCAL_C void DoLostClusters() +// +// Tests that scandrive removes lost clusters +// + { + test.Next(_L("Check lost clusters")); + HBufC8* newFat=HBufC8::New(gFatTestSize); + test(newFat!=NULL); + TPtr8 fat=newFat->Des(); + TPtr8 origFat=FatBufPtr->Des(); + TPtr8 origDir=DirBufPtr->Des(); + ReadFatDisk(origFat); + ReadDirDisk(origDir); + + // write cluster chain + test.Start(_L("Test removal of lost cluster chain")); + ReadFatDisk(fat); + for(TInt i=25;i<35;++i) + WriteFat(i,i+1,fat.Ptr()); + WriteFat(35,gEndOfChain,fat.Ptr()); + WriteFatDisk(fat); + TInt r=TheFs.ScanDrive(gSessionPath); + test(r==KErrNone); + TBool res=IsSameAsDrive(origFat,origDir); + test(res); + + // write semi-random changes to first fat + test.Next(_L("Test semi-random changes to first fat")); + for(TInt j=1;jDes(); + TPtr8 dirBuf=DirBufPtr->Des(); + + TInt r=TheFs.RmDir(_L("\\scndrv\\dir2\\somedirwith3entries\\")); + test(r==KErrNone || r==KErrNotFound || KErrPathNotFound); + r=TheFs.RmDir(_L("\\scndrv\\dir2\\somedir2with3entries\\")); + test(r==KErrNone || r==KErrNotFound || KErrPathNotFound); + + if (BootSector.RootDirEntries() != 0) + { + // Can only do this on FAT12/16, FAT32 root directory is extensible + // partial entry that fills up the root dir + test.Next(_L("Partial entry at end of rootdir")); + FillUpRootDir(2); + r=temp.Create(TheFs,_L("\\temp"),EFileShareAny); + test(r==KErrNone); + temp.Close(); + ReadDirDisk(dirBuf); + ReadFatDisk(fatBuf); + TEntryInfo partial1(ClusterEntryToBytes(gClusterRootDir,BootSector.RootDirEntries()-2),2); + CreatePartialEntry(partial1,1,EFalse); + res=TestPartialEntry(partial1); + test(res); + UnFillUpRootDir(2); + ReadDirDisk(dirBuf); + ReadFatDisk(fatBuf); + } + + // use first entry \scndrv\dir2\almostfull\ + test.Next(_L("Partial entry in middle of subdir")); + last = GetStartCluster(gClusterDir2_AFull,7); + TEntryInfo partial2(ClusterEntryToBytes(gClusterDir2_AFull,2),6); + CreatePartialEntry(partial2,3,EFalse); + // entry has been allocated a cluster which scandrive should delete along with partial entry + if (last > 0) + WriteFat(last,0,fatBuf.Ptr()); + res=TestPartialEntry(partial2); + test(res); + + // reduce size of \scndrv\dir2\full\ + test.Next(_L("Test directory reclaim")); + last = GetStartCluster(gClusterDir2_Full,gEntriesPerCluster-2); + WriteEndOfDir(ClusterEntryToBytes(gClusterDir2_Full,gEntriesPerCluster-2)); + WriteDirDisk(dirBuf); + TInt entry = GetFatEntry(gClusterDir2_Full, fatBuf.Ptr()); + WriteFat(gClusterDir2_Full,gEndOfChain,fatBuf.Ptr()); + while (entry && (entry & gEndOfChain) != gEndOfChain) + { + TInt next = GetFatEntry(entry, fatBuf.Ptr()); + WriteFat(entry,0,fatBuf.Ptr()); + entry = next; + } + if (last > 0) + WriteFat(last,0,fatBuf.Ptr()); + r=TheFs.ScanDrive(gSessionPath); + test(r==KErrNone); + res=IsSameAsDrive(fatBuf,dirBuf); + test(res); + + // use last entry of first cluster in \scndrv\dir2\full\ + test.Next(_L("Partial entry at end of subdir")); + r=temp.Create(TheFs,_L("\\scndrv\\dir2\\full\\temp"),EFileShareAny); + test(r==KErrNone); + temp.Close(); + ReadDirDisk(dirBuf); + ReadFatDisk(fatBuf); + TEntryInfo partial3(ClusterEntryToBytes(gClusterDir2_Full,gEntriesPerCluster-2),2); + CreatePartialEntry(partial3,1,EFalse); + res=TestPartialEntry(partial3); + test(res); + + // use entry in \scndrv\dir2\almostfull\ + test.Next(_L("Partial entry preceeding end-of-dir marker")); + last = GetStartCluster(gClusterDir2_AFull,14); + if (last > 0) + WriteFat(last,0,fatBuf.Ptr()); + last = GetStartCluster(gClusterDir2_AFull,8); + if (last > 0) + WriteFat(last,0,fatBuf.Ptr()); + WriteEndOfDir(ClusterEntryToBytes(gClusterDir2_AFull,14)); + WriteDirDisk(dirBuf); + TEntryInfo partial4(ClusterEntryToBytes(gClusterDir2_AFull,8),6); + CreatePartialEntry(partial4,4,EFalse); + res=TestPartialEntry(partial4); + test(res); + + // NOTE: + // Following test case is not valid anymore after fixing of + // PDEF128576: Unicode name file deleted after Scandrive + // In the fixes, we decided to discard file name checking in ScanDrive, + // as it is impossible for ScanDrive to judge if the illegal byte is part of a legal + // DBCS charater. + + // create entry in \scndrv\dir2\almostfull\ +// test.Next(_L("Partial entry with invalid dos name")); +// r=temp.Create(TheFs,_L("\\scndrv\\dir2\\almostfull\\Dodgy file name"),EFileShareAny); +// test(r==KErrNone); +// temp.Close(); +// ReadDirDisk(dirBuf); +// TInt dosStart=ClusterEntryToBytes(gClusterDir2_AFull,4); +// dirBuf[dosStart+4]=0x1; +// WriteDirDisk(dirBuf); +// r=TheFs.ScanDrive(gSessionPath); +// test(r==KErrNone); +// WriteDelete(dosStart-2*32,3); +// res=IsSameAsDrive(fatBuf,dirBuf); +// test(res); + + if (BootSector.SectorsPerCluster() == 1) + { + // use entry created in \scndrv\dir2\ + test.Next(_L("Partial entry spanning more than two clusters")); + last = GetStartCluster(gClusterDir2_Full,gEntriesPerCluster-1); + WriteEndOfDir(ClusterEntryToBytes(gClusterDir2_Full,gEntriesPerCluster-2)); + WriteEndOfDir(ClusterEntryToBytes(gClusterDir2_Full,gEntriesPerCluster-1)); + WriteDirDisk(dirBuf); + TFileName longFile=_L("\\scndrv\\dir2\\full\\"); + MakeVeryLongName(longFile); + r=temp.Create(TheFs,longFile,EFileShareAny); + test(r==KErrNone); + temp.Close(); + ReadDirDisk(dirBuf); + WriteFat(gClusterDir2_Full,gClusterDir2_SD3E,fatBuf.Ptr()); + WriteFat(gClusterDir2_SD3E,gClusterDir2_SD23E,fatBuf.Ptr()); + WriteFat(gClusterDir2_SD23E,gEndOfChain,fatBuf.Ptr()); + if (last > 0) + WriteFat(last,0,fatBuf.Ptr()); + TEntryInfo partial5(ClusterEntryToBytes(gClusterDir2_Full,gEntriesPerCluster-2),19); + CreatePartialEntry(partial5,7,EFalse); + res=TestPartialEntry(partial5); + test(res); + r=TheFs.Delete(longFile); + test(r==KErrNone || r==KErrNotFound); + r=TheFs.Delete(_L("\\temp")); + test(r==KErrNone || r==KErrNotFound); + } + ReadDirDisk(dirBuf); + + test.End(); + } + +LOCAL_C void DoMatchingEntries() +// +// Tests that scandrive detects/corrects entries with the same start cluster +// Copies entry to new location - replicates start cluster +// + { + test.Next(_L("Check matching entries")); + TPtr8 fatBuf=FatBufPtr->Des(); + TPtr8 dirBuf=DirBufPtr->Des(); + ReadDirDisk(dirBuf); + ReadFatDisk(fatBuf); + + // first entry in \scndrv\almostfull\ + root dir + test.Start(_L("matching entries in subdir + root dir")); + TEntryInfo from1(ClusterEntryToBytes(gClusterDir2_AFull,2),6); + TEntryInfo to1(ClusterEntryToBytes(gClusterRootDir,2),6); + CreateMatchingEntry(to1,from1,EFalse); + TBool res=TestMatchingEntry(to1); + test(res); + + // matching entries between 2 subdirs, one which has a full cluster + // first entry in \scndrv\dir2\full\ + end of \scndrv\dir2\almostfull\ + test.Next(_L("matching entries between 2 subdirs")); + TEntryInfo from2(ClusterEntryToBytes(gClusterDir2_Full,2),2); + TEntryInfo to2(ClusterEntryToBytes(gClusterDir2_AFull,14),2); + CreateMatchingEntry(to2,from2,EFalse); + res=TestMatchingEntry(to2); + test(res); + + // matching entries between two subdirs - one with end of dir marker next + // \scndrv\dir2\somedirwith3entries to \scndrv\ + test.Next(_L("matching entries between two subdirs")); + TEntryInfo from3(ClusterEntryToBytes(gClusterDir2,4),3); + TEntryInfo to3(ClusterEntryToBytes(gClusterScnDrv,6),3); + CreateMatchingEntry(to3,from3,ETrue); + res=TestMatchingEntry(to3); + test(res); + + // matching entries in same subdir, one in new cluster - irrelevant if matching names + // 1st and last entries in \scndrv\dir2\full\ + test.Next(_L("matching entries in same subdir")); + // delete entries to allow contiguous clusters in \scndrv\dir2\full directory + TInt r=TheFs.RmDir(_L("\\scndrv\\dir2\\somedirwith3entries\\")); + test(r==KErrNone); + r=TheFs.RmDir(_L("\\scndrv\\dir2\\somedir2with3entries\\")); + test(r==KErrNone); + // ensure directory is expanded + RFile temp; + r=temp.Create(TheFs,_L("\\scndrv\\dir2\\full\\temp"),EFileShareAny); + test(r==KErrNone); + temp.Close(); + r=TheFs.Delete(_L("\\scndrv\\dir2\\full\\temp")); + test(r==KErrNone); + ReadDirDisk(dirBuf); + ReadFatDisk(fatBuf); + TEntryInfo from4(ClusterEntryToBytes(gClusterDir2_Full,4),2); + TEntryInfo to4(ClusterEntryToBytes(gClusterDir2_Full+1,0),2); + CreateMatchingEntry(to4,from4,ETrue); + res=TestMatchingEntry(to4); + test(res); + + // \scndrv\dir1\very long name to \\scndrv\dir2\full\ + test.Next(_L("matching entries in diff dirs + new cluster")); + // delete last entry in directory + r=TheFs.Delete(LastInFull); + test(r==KErrNone); + TFileName veryLongName=_L("\\scndrv\\dir2\\full\\"); + MakeVeryLongName(veryLongName); + r=temp.Create(TheFs,veryLongName,EFileShareAny); + test(r==KErrNone); + temp.Close(); + r=TheFs.Delete(veryLongName); + test(r==KErrNone); + ReadDirDisk(dirBuf); + ReadFatDisk(fatBuf); + TEntryInfo from5(ClusterEntryToBytes(gClusterDir1,2),19); + TEntryInfo to5(ClusterEntryToBytes(gClusterDir2_Full,gEntriesPerCluster-2),19); + CreateMatchingEntry(to5,from5,EFalse); + res=TestMatchingEntry(to5); + test(res); + + test.End(); + } + + +LOCAL_C void DoMaxDepth() +// +// Test directory structure with max possible depth +// + { + test.Next(_L("Check max directory depth")); + test.Start(_L("Using single character names")); + TPtr8 fatBuf=FatBufPtr->Des(); + TPtr8 dirBuf=DirBufPtr->Des(); + // Create dir structure + TFileName dir1=_L("\\"); + TFileName dir2; + CreateMaxDepthDir(dir1,dir2); + ReadDirDisk(dirBuf); + ReadFatDisk(fatBuf); + // run scandisk and compare + TInt r=TheFs.ScanDrive(gSessionPath); + test(r==KErrNone); + TBool res=IsSameAsDrive(fatBuf,dirBuf); + test(res); + // Create a entry with matching start cluster and check fixed up + TEntryInfo from(ClusterEntryToBytes(gClusterDir2_AFull,2),6); + TEntryInfo to(ClusterEntryToBytes(gClusterEndMaxDepth,2),6); + CreateMatchingEntry(to,from,ETrue); + res=TestMatchingEntry(to); + test(res); + // DeleteMaxDepthStructure + DeleteMaxDepthDir(dir1,dir2); + test.End(); + } + +LOCAL_C void DoRootDir() +// +// Check that a full root directory is searched OK +// + { + test.Next(_L("Check a full root directory")); + FillUpRootDir(); + TPtr8 fatBuf=FatBufPtr->Des(); + TPtr8 dirBuf=DirBufPtr->Des(); + ReadDirDisk(dirBuf); + ReadFatDisk(fatBuf); + + TInt r=TheFs.ScanDrive(gSessionPath); + test(r==KErrNone); + + TBool res=IsSameAsDrive(fatBuf,dirBuf); + test(res); + UnFillUpRootDir(); + } + +LOCAL_C void TestNonVfatNames(const TPtrC& aDirName, TInt aDirCluster, TInt aEntry=2) +// +// Check that files without 'long' entries are kept intact. Creates files with +// a DOS type name, and for each one created except the last deletes the VFAT +// entry by copying the DOS entry over it and writing end of directory. This +// leaves a VFAT entry at the end of the directory, except when there is only +// room for one file. +// +// The layout, for 1 sector per cluster, is thus like: +// 0 . +// 1 .. +// 2 TEMPFILE.000 +// 3 TEMPFILE.001 +// ... +// 14 tempfile.012 VFAT +// 15 TEMPFILE.012 +// +// or for an almost full directory +// +// 0 . +// 1 .. +// whatever... +// 14 TEMPFILE.000 +// 15 end of directory +// + { + test.Printf(_L("Test cluster %2d, aEntry %d: %S\n"), aDirCluster, aEntry, &aDirName); + TPtr8 fatBuf=FatBufPtr->Des(); + TPtr8 dirBuf=DirBufPtr->Des(); + TInt cluster = aDirCluster; + + TInt maxEntry = gEntriesPerCluster; + if (aDirName.Compare(_L("\\")) == KErrNone) + maxEntry = Min(gRootDirEntries, gEntriesPerCluster); + + TInt entry = aEntry; + TInt r = KErrNone; + TInt i; + + while (entry > gEntriesPerCluster) + { + entry -= gEntriesPerCluster; + cluster++; + } + + TInt nFiles = maxEntry - entry - 1; + TInt startEntry = entry; + + test.Printf(_L("cluster %d, entry %d maxEntry %d, nFiles %d\n"), cluster, entry, maxEntry, nFiles); + + TBuf8<256> buf; + buf.Fill('*', 256); + + // Set up files, ignoring used slots + TInt filesThisTime = nFiles; + TInt totalFilesCreated = 0; + FOREVER + { + // + // Create a number of VFat entries + // + // - We create as many as we can fit in the cluster in one go. + // This is faster than creating a single entry then copying, as writing the + // entries one at a time using RRawDisk causes a remount of the file system, + // which can take a very long time on a large disk. + // + filesThisTime = (nFiles - totalFilesCreated) >> 1; + if(filesThisTime == 0) + { + if(nFiles == totalFilesCreated) + { + test.Printf(_L("Created all Non-VFAT entries\n")); + break; + } + + //...creating the final entry + filesThisTime = 1; + } + + for (i = 0; i < filesThisTime; i++) + { + TFileName name(aDirName); + name.Append(_L("tempfile.")); + name.AppendNumFixedWidth(i+totalFilesCreated, EHex, 3); + RFile f; + r = f.Create(TheFs, name, EFileShareAny); + test(r == KErrNone); + r = f.Write(buf); + test(r == KErrNone); + f.Close(); + } + + // + // Move DOS FAT entries up using RRawDisk, deleting the original VFAT entries + // + ReadDirDisk(dirBuf, cluster); + TInt dosEntry = entry + 1; + for (i = 0; i < filesThisTime; i++) + { + // Copy VFAT to Non-VFAT entries + if (entry+2 < maxEntry || nFiles < 2) + { + TInt posVFAT = ClusterEntryToBytes(cluster, entry); + TInt posEOD = ClusterEntryToBytes(cluster, entry+1); + TInt posDOS = ClusterEntryToBytes(cluster, dosEntry); + + WriteCopyDir(posDOS, posVFAT); // Copy the DOS entry + WriteDelete(posDOS,2); // Delete the original entry + WriteEndOfDir(posEOD); // Write End Of Directory + + entry += 1; + dosEntry += 2; + } + else + { + // last entry has VFAT intact, to fill cluster + entry += 2; + } + + } + + WriteDirDisk(dirBuf, cluster); + totalFilesCreated += filesThisTime; + test.Printf(_L(" created %d entries\n"), totalFilesCreated); + } + + ReadDirDisk(dirBuf); + ReadFatDisk(fatBuf); + + DumpData(NULL, aDirCluster, cluster+1); + + test.Printf(_L("Running ScanDrive\n"), filesThisTime); + r=TheFs.ScanDrive(gSessionPath); + test(r==KErrNone); + + TBool res=IsSameAsDrive(fatBuf,dirBuf); + test(res); + + test.Printf(_L("Deleting %d files\n"), nFiles); + for (i = 0; i < nFiles; i++) + { + TFileName name(aDirName); + name.Append(_L("tempfile.")); + name.AppendNumFixedWidth(i, EHex, 3); + r = TheFs.Delete(name); + test(r == KErrNone); + } + + ReadDirDisk(dirBuf); + ReadFatDisk(fatBuf); + WriteEndOfDir(ClusterEntryToBytes(cluster, startEntry)); + WriteDirDisk(dirBuf); + + test.Printf(_L("Running ScanDrive\n"), filesThisTime); + r=TheFs.ScanDrive(gSessionPath); + test(r==KErrNone); + res=IsSameAsDrive(fatBuf,dirBuf); + test(res); + } + +LOCAL_C void DoNonVfatNames() +// +// Check that files without 'long' entries are kept intact +// + { + test.Next(_L("Check non-VFAT file names")); + TestNonVfatNames(_L("\\"), gClusterRootDir, 2); + TestNonVfatNames(_L("\\scndrv\\dir1\\"), gClusterDir1, 2+19); + TestNonVfatNames(_L("\\scndrv\\dir2\\somedirwith3entries\\"), gClusterDir2_SD3E, 2); + TestNonVfatNames(_L("\\scndrv\\dir2\\almostfull\\"), gClusterDir2_AFull, 14); + } + + +LOCAL_C void DoTests() + { + + Format(); + DoReadBootSector(); + DumpBootSector(); + InitialiseBuffers(); + CreateRootDir(); + CreateDirectoryStructure(); + TPtr8 fatBuf=FatBufPtr->Des(); + TPtr8 dirBuf=DirBufPtr->Des(); + ReadDirDisk(dirBuf); + ReadFatDisk(fatBuf); + DumpFat(); + DumpData(NULL, DirBufPtr->Ptr()); + + DoNonVfatNames(); + DoRootDir(); + DoMaxDepth(); + DoMatchingEntries(); + DoPartEntries(); + DoLostClusters(); + DoHangingClusters(); + TestMountAndScan(); + TestConsecutiveMountAndScans(); + DeleteDirectoryStructure(); + DeleteRootDir(); + TestExtendedChars(); + + DumpBootSector(); + DumpFat(); + DumpData(NULL, 0, 200); + + delete FatDiskPtr; + delete DirDiskPtr; + delete FatBufPtr; + delete DirBufPtr; + } + + +void CallTestsL() + { + TInt r; + r = TheFs.CharToDrive(gSessionPath[0], gDriveNumber); + test( KErrNone == r ); + + + //-- set up console output + Fat_Test_Utils::SetConsole(test.Console()); + + //-- print drive information + PrintDrvInfo(TheFs, gDriveNumber); + + if (!Is_Fat(TheFs, gDriveNumber)) + { + test.Printf(_L("CallTestsL: Skipped: test requires FAT filesystem\n")); + return; + } + + // check this is not the internal ram drive + TVolumeInfo v; + r=TheFs.Volume(v); + test(r==KErrNone); + if(v.iDrive.iMediaAtt&KMediaAttVariableSize) + { + test.Printf(_L("Error: Internal ram drive not tested\n")); + return; + } + + r=TheFs.SetSessionPath(gSessionPath); + test(r==KErrNone); + + DoTests(); + + return; + } + + + + + +