--- /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 <f32file.h>
+#include <e32test.h>
+
+#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)
+ | |
+ | <a very long file name (19 entries)> (5)
+ |
+ - dir2 (6)
+ |
+ - full (7)
+ | |
+ | |
+ | - <seven 2*32 bytes entries> (11-17)
+ |
+ - somedirwith3entries (8)
+ |
+ - somedir2with3entries (9)
+ |
+ - almostfull(10)
+ |
+ - <two lots of 6*32 bytes entries> (18+19)
+
+*/
+
+/*
+ The initial FAT32 directory structure (with cluster number in brackets is as follows):
+
+ |
+ - scndrv (3)
+ |
+ - dir1 (4)
+ | |
+ | <a very long file name (19 entries)> (5)
+ |
+ - dir2 (6)
+ |
+ - full (7)
+ | |
+ | |
+ | - <seven 2*32 bytes entries> (11-17)
+ |
+ - somedirwith3entries (8)
+ |
+ - somedir2with3entries (9)
+ |
+ - almostfull(10)
+ |
+ - <two lots of 6*32 bytes entries> (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<<KDefaultSectorLog2, BootSector);
+ test(nRes == KErrNone);
+
+ if(!BootSector.IsValid())
+ {
+ test.Printf(_L("Wrong bootsector! Dump:\n"));
+ BootSector.PrintDebugInfo();
+ test(0);
+ }
+
+ // Calculate derived variables (fixed for a particular disk format)
+ if (BootSector.RootDirEntries() == 0)
+ {
+ test.Printf(_L("Disk is FAT32\n"));
+ gDiskType = EFat32;
+ gEndOfChain = 0x0FFFFFFF;
+ }
+ else if (BootSector.FatType() == EFat16)
+ {
+ test.Printf(_L("Disk is FAT16\n"));
+ gDiskType = EFat16;
+ gEndOfChain = 0xFFFF;
+ }
+ else
+ {
+ test.Printf(_L("Disk is FAT12\n"));
+ gDiskType = EFat12;
+ gEndOfChain = 0x0FFF;
+ }
+
+ gBytesPerCluster = BootSector.BytesPerSector() * BootSector.SectorsPerCluster();
+ gFatStartBytes = BootSector.ReservedSectors() * BootSector.BytesPerSector();
+ gEntriesPerCluster = gBytesPerCluster / KSizeOfFatDirEntry;
+
+ TBool big = (BootSector.SectorsPerCluster() > 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; entriesSoFar<maxRootEntries - aNum; entriesSoFar+=2)
+ {
+ name[1]=(TUint16)(count/26/26+'a');
+ name[2]=(TUint16)(count/26%26+'a');
+ name[3]=(TUint16)(count%26+'a');
+ r=TheFs.Delete(name);
+ test(r==KErrNone || r==KErrNotFound);
+ ++count;
+ }
+ }
+
+LOCAL_C void CreateRootDir()
+//
+// fill up root directory to 1 clusters by creating entries and then deleting
+// them except the last.
+//
+ {
+ test.Next(_L("Create Root Directory Entries"));
+ TInt maxRootEntries = gRootDirEntries;
+ TFileName name=_L("\\???xxx");
+ TInt count=0;
+ TInt entriesSoFar=2;
+ TInt r;
+ RFile f;
+ for (entriesSoFar=0; entriesSoFar<maxRootEntries; entriesSoFar+=2)
+ {
+ name[1]=(TUint16)(count/26/26+'a');
+ name[2]=(TUint16)(count/26%26+'a');
+ name[3]=(TUint16)(count%26+'a');
+ r=f.Create(TheFs, name, EFileWrite);
+ test(r==KErrNone);
+ f.Close();
+ ++count;
+ }
+ DeleteRootDir(1);
+ }
+
+LOCAL_C void FillUpRootDir(TInt aFree=0)
+//
+// fill up root directory
+//
+ {
+ TInt maxRootEntries = gRootDirEntries - aFree;
+ TFileName dir=_L("\\??\\");
+ TInt count=0;
+ TInt entriesSoFar=2;
+ TInt r;
+ while(entriesSoFar<maxRootEntries)
+ {
+ dir[1]=(TUint16)(count/26+'a');
+ dir[2]=(TUint16)(count%26+'a');
+ r=TheFs.MkDir(dir);
+ test(r==KErrNone);
+ entriesSoFar+=2;
+ ++count;
+ }
+ }
+
+LOCAL_C void UnFillUpRootDir(TInt aFree=0)
+//
+// reverse changes from FillUpRootDir()
+//
+ {
+ TFileName dir=_L("\\??\\");
+ TInt entriesSoFar=gRootDirEntries-aFree;
+ TInt count=0;
+ TInt r;
+ while(entriesSoFar>2)
+ {
+ 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;j<gFatTestSize/BootSector.BytesPerSector();++j)
+ {
+ TInt off = j*BootSector.BytesPerSector()+j*7%512;
+ fat[off]=1;
+ }
+ WriteFatDisk(fat);
+ r=TheFs.ScanDrive(gSessionPath);
+ test(r==KErrNone);
+ res=IsSameAsDrive(origFat,origDir);
+ test(res);
+
+ // write semi-random changes to second fat
+ test.Next(_L("Test semi-random changes to second fat"));
+ WriteFatDisk(fat, 1);
+ r=TheFs.ScanDrive(gSessionPath);
+ test(r==KErrNone);
+ res=IsSameAsDrive(origFat,origDir);
+ test(res);
+
+ delete newFat;
+ test.End();
+ }
+
+LOCAL_C void DoPartEntries()
+//
+// Tests that scandrive detects/corrects partial entries
+//
+ {
+ RFile temp;
+ TInt last;
+ TBool res;
+ test.Start(_L("Check partial entries"));
+ TPtr8 fatBuf=FatBufPtr->Des();
+ 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;
+ }
+
+
+
+
+
+