Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h)
Have multiple extension sections in the bld.inf, one for each version
of the compiler. The RVCT version building the tools will build the
runtime libraries for its version, but make sure we extract all the other
versions from zip archives. Also add the archive for RVCT4.
// Copyright (c) 1996-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the License "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
// f32test\server\t_mount.cpp
// Testing some issues connected with mounting/dismounting file fystems, drives finalisation etc.
//
//
/**
@file
*/
#define __E32TEST_EXTENSION__
#include <f32file.h>
#include <e32test.h>
#include <e32math.h>
#include <e32property.h>
#include <f32dbg.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
RTest test(_L("T_Mount"));
static TInt gDriveNum=-1; ///< drive number we are dealing with
static TInt64 gRndSeed;
const TInt KBM_Repetitions = 5; ///< number of repetitions for BM testing
const TUint32 KCheckFileSize = 533; ///< size of the small file to be deleted
_LIT(KFileNameFirst, "\\FIRST%d.DAT");
_LIT(KFileNameMiddle, "\\MID%d.DAT");
_LIT(KFileNameLast, "\\LAST%d.DAT");
_LIT(KFileNameFiller, "\\FILL%d.DAT");
typedef void (*TStressFN)(TInt); //-- pointer to the FAT mount stress function
//-------------------------------------------------------------------
//-- debug bit flags that may be set in the property which controls FAT volume mounting
const TUid KThisTestSID={0x10210EB3}; ///< this EXE SID
const TUint32 KMntProp_EnableALL = 0x00000000; //-- enable all operations
#ifdef _DEBUG
const TUint32 KMntProp_DisableALL = 0xFFFFFFFF; //-- disable all operations
const TUint32 KMntProp_Disable_FsInfo = 0x00000001; //-- mask for disabling/enabling FSInfo information
const TUint32 KMntProp_Disable_FatBkGndScan = 0x00000002; //-- mask for disabling/enabling FAT background scanner
#endif
//-------------------------------------------------------------------
/** set a debug property value, which then wiil be read by FAT fsy */
void SetFsyDebugFlag(TInt aDriveNo, TUint32 aFlags)
{
TInt nRes;
nRes = RProperty::Set(KThisTestSID, aDriveNo, aFlags);
test_KErrNone(nRes);
}
//-------------------------------------------------------------------
/**
format the volume and read the boot sector
*/
static void FormatVolume(TBool aQuickFormat)
{
TInt nRes;
#if 0
//-- FAT32 SPC:1; for the FAT32 testing on the emulator
(void)aQuickFormat;
#ifdef __EPOC32__
test.Printf(_L("This is emulator configuration!!!!\n"));
test(0);
#endif
TFatFormatParam fp;
fp.iFatType = EFat32;
fp.iSecPerCluster = 1;
nRes = FormatFatDrive(TheFs, CurrentDrive(), ETrue, &fp); //-- always quick; doesn't matter for the emulator
#else
nRes = FormatFatDrive(TheFs, CurrentDrive(), aQuickFormat);
#endif
test_KErrNone(nRes);
}
//-------------------------------------------------------------------
/**
Prepare FAT volume for mounting performance testing
1. quick format the drive
2. create KBM_Repetitions files in the beginning (first files)
3. create KMaxFillFiles/2 large files (they take about 40% of the volume);
4. create KBM_Repetitions files (middle files)
5. create KMaxFillFiles/2 large files (they take other 40% of the volume);
6. create KBM_Repetitions files (last files)
@return ETrue if everythis is OK
*/
TBool PrepareVolumeForBM()
{
test.Printf(_L("Prepare the volume for BM testing...\n"));
TInt nRes;
TInt i;
//-- 1. quick format the drive
FormatVolume(ETrue);
if(!Is_Fat32(TheFs, gDriveNum))
{
test.Printf(_L("This test requires FAT32 ! Skipping.\n"));
return EFalse;
}
TVolumeInfo volInfo;
nRes = TheFs.Volume(volInfo, gDriveNum);
test_KErrNone(nRes);
//-- the files will take 80% of the drive space
const TInt KMaxFillFiles = 100;
const TUint32 KFillFileSz = (TUint32)((volInfo.iFree*8) / (10*KMaxFillFiles));
//-- 2. create KBM_Repetitions files in the very begining (occupies first FAT entries)
TBuf<64> buf;
for(i=0; i<KBM_Repetitions; ++i)
{
buf.Format(KFileNameFirst, i);
nRes = CreateCheckableStuffedFile(TheFs, buf, KCheckFileSize);
test_KErrNone(nRes);
}
//-- 3. Fill the FAT with entries and cteate a file in the middle
const TInt nHalf1 = KMaxFillFiles / 2;
for(i=0; i<nHalf1; ++i)
{
buf.Format(KFileNameFiller, i);
nRes = CreateEmptyFile(TheFs, buf, KFillFileSz);
test_KErrNone(nRes);
}
//-- 4. create a files in the middle
for(i=0; i<KBM_Repetitions; ++i)
{
buf.Format(KFileNameMiddle, i);
nRes = CreateCheckableStuffedFile(TheFs, buf, KCheckFileSize);
test_KErrNone(nRes);
}
//-- 5. fill second half FAT
for(i=nHalf1; i<KMaxFillFiles; ++i)
{
buf.Format(KFileNameFiller, i);
nRes = CreateEmptyFile(TheFs, buf, KFillFileSz);
test_KErrNone(nRes);
}
//-- 6. create files in the very end (occupiy last FAT entries)
for(i=0; i<KBM_Repetitions; ++i)
{
buf.Format(KFileNameLast, i);
nRes = CreateCheckableStuffedFile(TheFs, buf, KCheckFileSize);
test_KErrNone(nRes);
}
return ETrue;
}
/**
Mounts and dismounts FAT volume several times calculating average time taken to mount.
Also can stress FS by calling stress function that can do some work on the volume.
@param apStressFN pointer to the stressing function which can be called just after mounting. Can be NULL.
@return time in milliseconds taken to mout the volume
*/
static TUint32 DoMeasureMountTime(TStressFN apStressFN)
{
TInt nRes;
TTime timeStart;
TTime timeEnd;
TInt64 usMountTime=0; //-- total time taken by "Mount"
//-- disable FAT test utils print out, it can affects measured time
EnablePrintOutput(EFalse);
for(TInt i=0; i<KBM_Repetitions; ++i)
{
//-- A. remount FS taking the time when mounting starts
nRes = RemountFS(TheFs, gDriveNum, &timeStart);
test_KErrNone(nRes);
//-- B. call a given stress function
if(apStressFN)
apStressFN(i);
//-- C. take end time
timeEnd.UniversalTime();
test_KErrNone(nRes);
usMountTime += (timeEnd.MicroSecondsFrom(timeStart)).Int64();
}
const TUint32 msMountTime = (TUint32)usMountTime / (K1mSec*KBM_Repetitions);
EnablePrintOutput(ETrue); //-- Enable FAT test utils print out
return msMountTime;
}
//-------------------------------------------------------------------
/**
Stress function.
Use case: read access to the first file on the volume in root dir.
*/
static void FirstReadAccess_FirstFile_Stress(TInt aRepNo)
{
TBuf<64> buf;
buf.Format(KFileNameFirst, aRepNo);
TInt nRes = VerifyCheckableFile(TheFs, buf);
test_KErrNone(nRes);
}
/**
Stress function.
Use case: read access to the middle file on the volume in root dir.
*/
static void FirstReadAccess_MiddleFile_Stress(TInt aRepNo)
{
TBuf<64> buf;
buf.Format(KFileNameMiddle, aRepNo);
TInt nRes = VerifyCheckableFile(TheFs, buf);
test_KErrNone(nRes);
}
/**
Stress function.
Use case: read access to the last file on the volume in root dir.
*/
static void FirstReadAccess_LastFile_Stress(TInt aRepNo)
{
TBuf<64> buf;
buf.Format(KFileNameLast, aRepNo);
TInt nRes = VerifyCheckableFile(TheFs, buf);
test_KErrNone(nRes);
}
/**
Stress function.
Use case: Getting volume information SYNCHRONOUSLY.
*/
static void GetVolInfo_Synch_Stress(TInt)
{
TVolumeInfo volInfo;
TInt nRes = TheFs.Volume(volInfo, gDriveNum);
test_KErrNone(nRes);
}
/**
Stress function.
Use case: Getting volume information _ASYNCHRONOUSLY_, i.e. do not wait until
FAT32 free space scan thread finishes
*/
static void GetVolInfo_Asynch_Stress(TInt)
{
TVolumeInfo volInfo;
//-- let's use special version of the RFS API
TRequestStatus rqStat;
TheFs.Volume(volInfo, gDriveNum, rqStat);
User::WaitForRequest(rqStat);
test(rqStat.Int()==KErrNone);
}
//-------------------------------------------------------------------
/**
Stress function.
Use case: deletes files in the beginning of the volume (or FAT table)
*/
static void DeleteFirstFile_Stress(TInt aRepNo)
{
TBuf<64> buf;
buf.Format(KFileNameFirst, aRepNo);
TInt nRes = TheFs.Delete(buf);
test_KErrNone(nRes);
}
/**
Stress function.
Use case: deletes files in the middle of the volume (or FAT table)
*/
static void DeleteMiddleFile_Stress(TInt aRepNo)
{
TBuf<64> buf;
buf.Format(KFileNameMiddle, aRepNo);
TInt nRes = TheFs.Delete(buf);
test_KErrNone(nRes);
}
/**
Stress function.
Use case: deletes files in the end of the volume (or FAT table)
*/
static void DeleteLastFile_Stress(TInt aRepNo)
{
TBuf<64> buf;
buf.Format(KFileNameLast, aRepNo);
TInt nRes = TheFs.Delete(buf);
test_KErrNone(nRes);
}
//-------------------------------------------------------------------
/**
perform series of tests that measure and print out FAT volume mount time for different secenarios
*/
void MeasureMountTime()
{
TUint32 msTime;
if(!PrepareVolumeForBM()) //-- prepare the volume for BM tests: format it, create file structure etc.
return;
//-- 1. no stress function, measure just pure mount time
msTime = DoMeasureMountTime(NULL);
test.Printf(_L("#--> Pure mount:%d ms\n"), msTime);
//-- 2.1 measure mount time with a read-acess to the first file on the volume
msTime = DoMeasureMountTime(FirstReadAccess_FirstFile_Stress);
test.Printf(_L("#--> mount and read access to 1st file:%d ms\n"), msTime);
//-- 2.2 measure mount time with a read-acess to the middle file on the volume
msTime = DoMeasureMountTime(FirstReadAccess_MiddleFile_Stress);
test.Printf(_L("#--> mount and read access to middle file:%d ms\n"), msTime);
//-- 2.3 measure mount time with a read-acess to the last file on the volume
msTime = DoMeasureMountTime(FirstReadAccess_LastFile_Stress);
test.Printf(_L("#--> mount and read access to last file:%d ms\n"), msTime);
//-- 2.4 measure mount time with getting a volume information
msTime = DoMeasureMountTime(GetVolInfo_Synch_Stress);
test.Printf(_L("#--> mount and getting volInfo (synch):%d ms\n"), msTime);
msTime = DoMeasureMountTime(GetVolInfo_Asynch_Stress);
test.Printf(_L("#--> mount and getting volInfo (Asynch):%d ms\n"), msTime);
//-- 2.4 measure mount time with deleting file in the beginning (write access to the first entries of the FAT32)
msTime = DoMeasureMountTime(DeleteFirstFile_Stress);
test.Printf(_L("#--> mount and delete first file:%d ms\n"), msTime);
//-- 2.5 measure mount time with deleting file in the middle (write access to the middle entries of the FAT32)
msTime = DoMeasureMountTime(DeleteMiddleFile_Stress);
test.Printf(_L("#--> mount and delete middle file:%d ms\n"), msTime);
//-- 2.6 measure mount time with deleting file in the end (write access to the last entries of the FAT32)
msTime = DoMeasureMountTime(DeleteLastFile_Stress);
test.Printf(_L("#--> mount and delete last file:%d ms\n"), msTime);
test.Printf(_L("---\n"), msTime);
}
//----------------------------------------------------------------------------------------------
//! @SYMTestCaseID PBASE-T_MOUNT-0521
//! @SYMTestType PT
//! @SYMPREQ PREQ1721
//! @SYMTestCaseDesc Testing FAT volume mount performance for various scenarios
//!
//! @SYMTestActions
//! 0 Prepare the volume by formatting it and creating 100 files to occupy the space.
//! 1 Turn OFF all mount enhancements
//! 2 Measure and print out volume mount time for the next scenarios:
//! a. simple mount
//! b. mount and read access to the media (reading the last file in the root dir.)
//! c. mount and write access to the media (writing data into the last file in the root dir)
//! d. mount getting volume information
//!
//!
//! 3 Turn ON using FSInfo.
//! 4 Repeat step 2 for this case.
//! @SYMTestExpectedResults Finishes ok.
//! @SYMTestPriority High
//! @SYMTestStatus Implemented
//----------------------------------------------------------------------------------------------
void TestFAT_Mounting_Performance()
{
test.Next(_L("\n#--> Measuring FAT volumes mount performance.\n"));
#ifndef _DEBUG
test.Printf(_L("Skipping the test in the Release build! \n"));
return;
#else
//-- 1. turn OFF all mount enhancements like using FSInfo, backround FAT scan etc.
test.Printf(_L("#--> ==== All mount enhancements are disabled ====\n"));
SetFsyDebugFlag(gDriveNum, KMntProp_DisableALL);
MeasureMountTime();
//-- 2. Turn ON using FSInfo
test.Printf(_L("#--> ==== Enabled Using FSInfo ====\n"));
SetFsyDebugFlag(gDriveNum, KMntProp_DisableALL & ~KMntProp_Disable_FsInfo);
MeasureMountTime();
//-- 2. Turn OFF using FSInfo and ON FAT32 bacground scanning
test.Printf(_L("#--> ==== Enabled FAT32 BkGnd scan, FSInfo disabled ====\n"));
SetFsyDebugFlag(gDriveNum, KMntProp_DisableALL & ~KMntProp_Disable_FatBkGndScan);
MeasureMountTime();
//-- restore mounting mechanism
SetFsyDebugFlag(gDriveNum, KMntProp_EnableALL);
#endif //_DEBUG
}
//-------------------------------------------------------------------
/**
Check if the drive aDriveNo is finalised or not.
The "CleanShutDown" is obtained by QueryVolumeInfoExt API and by reading 2 FATs directly with checking their consistence.
@param aDriveNo drive number to query.
@return ETrue if the drive if finalised
*/
static TBool DoCheckVolumeFinalised(TInt aDriveNo)
{
TInt nRes;
TPckgBuf<TBool> boolPckg;
//-- 1. get "Finalised" state by using the API
nRes = TheFs.QueryVolumeInfoExt(aDriveNo, EIsDriveFinalised, boolPckg);
test_KErrNone(nRes);
//-- N.B. for FAT12 the result can be either OK or "NotSupported"
//-- If FAT12 is in explicit "finalised" state, the result will be OK
//-- if not, we can't query the volume state, because FAT12 doesn't support flags in FAT[1]
const TBool bFinalised_From_API = boolPckg() >0;
TBool bFinalised_From_FAT1 = bFinalised_From_API;
TBuf8<32> fatBuf(32);
TFatBootSector bootSec;
const TUint32 posMainBootSec = KBootSectorNum << KDefaultSectorLog2;
nRes = ReadBootSector(TheFs, gDriveNum, posMainBootSec, bootSec);
test_KErrNone(nRes);
test(bootSec.IsValid());
const TUint32 Fat1StartPos = bootSec.FirstFatSector() * bootSec.BytesPerSector();
if(bootSec.FatType() == EFat16)
{//-- FAT16
TUint16 fatEntry;
const TUint16 KClnShtdnMask = 0x8000; //-- "ClnShutBitMask", see FAT specs
//-- read "CleanShutDown" flag directly from the 1st FAT
nRes = MediaRawRead(TheFs, gDriveNum, Fat1StartPos, fatBuf.Size(), fatBuf);
test_KErrNone(nRes);
Mem::Copy(&fatEntry, (fatBuf.Ptr()+sizeof(fatEntry)), sizeof(fatEntry));
bFinalised_From_FAT1 = (fatEntry & KClnShtdnMask) >0;
for(TInt i=1; i<bootSec.NumberOfFats(); ++i)
{
//-- read a flag from the next FAT
const TUint32 currFatStartPos = (bootSec.FirstFatSector() + i*bootSec.TotalFatSectors())*bootSec.BytesPerSector();
nRes = MediaRawRead(TheFs, gDriveNum, currFatStartPos, fatBuf.Size(), fatBuf);
test_KErrNone(nRes);
Mem::Copy(&fatEntry, (fatBuf.Ptr()+sizeof(fatEntry)), sizeof(fatEntry));
const TBool bFinalised_From_currFAT = (fatEntry & KClnShtdnMask)>0;
test(bFinalised_From_currFAT == bFinalised_From_FAT1);
}
}
else if(bootSec.FatType() == EFat32)
{//-- FAT32
TUint32 fatEntry;
const TUint32 KClnShtdnMask = 0x08000000; //-- "ClnShutBitMask", see FAT specs
//-- read "CleanShutDown" flag directly from the 1st FAT
nRes = MediaRawRead(TheFs, gDriveNum, Fat1StartPos, fatBuf.Size(), fatBuf);
test_KErrNone(nRes);
Mem::Copy(&fatEntry, (fatBuf.Ptr()+sizeof(fatEntry)), sizeof(fatEntry));
bFinalised_From_FAT1 = (fatEntry & KClnShtdnMask) >0;
for(TInt i=1; i<bootSec.NumberOfFats(); ++i)
{
//-- read a flag from the next FAT
const TUint32 currFatStartPos = (bootSec.FirstFatSector() + i*bootSec.TotalFatSectors())*bootSec.BytesPerSector();
nRes = MediaRawRead(TheFs, gDriveNum, currFatStartPos, fatBuf.Size(), fatBuf);
test_KErrNone(nRes);
Mem::Copy(&fatEntry, (fatBuf.Ptr()+sizeof(fatEntry)), sizeof(fatEntry));
const TBool bFinalised_From_currFAT = (fatEntry & KClnShtdnMask) >0;
test(bFinalised_From_currFAT == bFinalised_From_FAT1);
}
}
else //-- FAT12
{//-- FAT12 doesn't have flags in FAT[1]
bFinalised_From_FAT1 = bFinalised_From_API;
}
test(bFinalised_From_FAT1 == bFinalised_From_API);
return bFinalised_From_API;
}
//----------------------------------------------------------------------------------------------
//! @SYMTestCaseID PBASE-T_MOUNT-0522
//! @SYMTestType UT
//! @SYMPREQ PREQ1721
//! @SYMTestCaseDesc RFs::FinaliseDrive() and RFs::FinaliseDrives() API
//!
//! @SYMTestActions
//! 0 Finalise the drive in RW mode and check the result.
//! 1 Finalise the drive in RW mode once again and check the result.
//! 2 Create a file and check that the volume has become unfinalised.
//! 3 Open a file, try to finalise the volume; it shall fail with KErrInUse
//! 4 close the file, finalise, check that the result is OK.
//! 5 "Unfinalise" the volume; check that the volume is not finalised any longer.
//! 6 Finalise the drive in RO mode and check the result.
//! 7 Try to create a file, shall fail with KErrAccessDenied
//! 8 Try to finalise into RW mode, shall fail with KErrAccessDenied
//! 9 Try to unfinalise volume, it shall remain RO
//! 10 Remount the drive, it shall become RW and finalised
//! 11 Test transition "Not finalised" -> EFinal_RW (expected: KErrNone)
//! 12 Test transition EFinal_RW -> EFinal_RO (expected: KErrNone)
//! 13 Test transition EFinal_RO -> EFinal_RW (expected: KErrAccessDenied)
//! 14 Remount the volume to reset RO flag
//! 15 test old RFs::FinaliseDrives() API by finalising all drives in the system
//!
//! @SYMTestExpectedResults finishes if the volume finalisation works correctly. panics otherwise
//! @SYMTestPriority High
//! @SYMTestStatus Implemented
//----------------------------------------------------------------------------------------------
static void TestFinaliseFS()
{
test.Next(_L("Testing RFs::FinaliseDrives() API\n"));
if(!Is_Fat16(TheFs, gDriveNum) && !Is_Fat32(TheFs, gDriveNum))
{
test.Printf(_L("This step requires FAT16 or FAT32 ! Skipping.\n"));
return;
}
TInt nRes;
TBool bDriveFinalised;
//============= 1. finalise the drive (RW mode) and check the result
nRes =TheFs.FinaliseDrive(gDriveNum, RFs::EFinal_RW);
test_KErrNone(nRes);
bDriveFinalised = DoCheckVolumeFinalised(gDriveNum);
test(bDriveFinalised);
//-- 1.1 finalise the drive second time EFinal_RW -> EFinal_RW shall work
nRes =TheFs.FinaliseDrive(gDriveNum, RFs::EFinal_RW);
test_KErrNone(nRes);
bDriveFinalised = DoCheckVolumeFinalised(gDriveNum);
test(bDriveFinalised);
//============= 2. create a file. Shall succeed (EFinal_RW), the volume shall become unfinalised
RFile file;
_LIT(KFileName, "\\my_file1.dat");
nRes = CreateEmptyFile(TheFs, KFileName, 128000);
test_KErrNone(nRes);
bDriveFinalised = DoCheckVolumeFinalised(gDriveNum);
test(!bDriveFinalised); //-- the volume has become "unfinalised"
//-- 2.1 open a file, try to finalise; Shall dail with KErrInUse
nRes = file.Replace(TheFs, KFileName, EFileWrite | EFileRead);
test_KErrNone(nRes);
nRes =TheFs.FinaliseDrive(gDriveNum, RFs::EFinal_RW);
test(nRes==KErrInUse); //-- can't finalise drives with opened objects
file.Close();
nRes =TheFs.FinaliseDrive(gDriveNum, RFs::EFinal_RW);
test_KErrNone(nRes);
bDriveFinalised = DoCheckVolumeFinalised(gDriveNum);
test(bDriveFinalised);
//============= 3. test "unfinalise API"
nRes =TheFs.FinaliseDrive(gDriveNum, RFs::EForceUnfinalise);
test_KErrNone(nRes);
bDriveFinalised = DoCheckVolumeFinalised(gDriveNum);
test(!bDriveFinalised); //-- the volume has become "unfinalised"
//============= 4. test finalisation into RO mode
nRes =TheFs.FinaliseDrive(gDriveNum, RFs::EFinal_RO); //-- the volume becomes RO
test_KErrNone(nRes);
//-- try to write a file on RO volume; it shall fail with KErrAccessDenied
nRes = CreateEmptyFile(TheFs, KFileName, 128000);
test(nRes == KErrAccessDenied);
file.Close();
//-- 4.1 try to finalise into EFinal_RW mode, shall fail with KErrAccessDenied
nRes =TheFs.FinaliseDrive(gDriveNum, RFs::EFinal_RW);
test(nRes == KErrAccessDenied);
//-- 4.2 "unfinalise" the volume, it still shall remain RO
nRes =TheFs.FinaliseDrive(gDriveNum, RFs::EForceUnfinalise);
test_KErrNone(nRes);
//-- try to write a file on RO volume; it shall fail with KErrAccessDenied
nRes = CreateEmptyFile(TheFs, KFileName, 128000);
test(nRes == KErrAccessDenied);
file.Close();
//-- remount FS, the drive shall become RW
nRes = RemountFS(TheFs, gDriveNum);
test_KErrNone(nRes);
bDriveFinalised = DoCheckVolumeFinalised(gDriveNum);
test(bDriveFinalised);
//-- try to write a file on RW volume, shall be OK
nRes = CreateEmptyFile(TheFs, KFileName, 128000);
test(nRes == KErrNone);
file.Close();
bDriveFinalised = DoCheckVolumeFinalised(gDriveNum);
test(!bDriveFinalised);
//============= 5. test various finalisation modes
//-- 5.1 Not finalised -> EFinal_RW (KErrNone)
nRes =TheFs.FinaliseDrive(gDriveNum, RFs::EFinal_RW);
test(nRes == KErrNone);
bDriveFinalised = DoCheckVolumeFinalised(gDriveNum);
test(bDriveFinalised);
//-- 5.2 EFinal_RW -> EFinal_RO (KErrNone)
nRes =TheFs.FinaliseDrive(gDriveNum, RFs::EFinal_RO);
test(nRes == KErrNone);
bDriveFinalised = DoCheckVolumeFinalised(gDriveNum);
test(bDriveFinalised);
//-- 5.2 EFinal_RO -> EFinal_RW (KErrAccessDenied)
nRes =TheFs.FinaliseDrive(gDriveNum, RFs::EFinal_RW);
test(nRes == KErrAccessDenied);
bDriveFinalised = DoCheckVolumeFinalised(gDriveNum);
test(bDriveFinalised);
//-- 5.3 restore
nRes = RemountFS(TheFs, gDriveNum);
test_KErrNone(nRes);
//============= 6. test old RFs::FinaliseDrives API
nRes = CreateEmptyFile(TheFs, KFileName, 128000);
test(nRes == KErrNone);
bDriveFinalised = DoCheckVolumeFinalised(gDriveNum);
test(!bDriveFinalised);
TheFs.FinaliseDrives(); //-- shall work as TheFs.FinaliseDrive(gDriveNum, RFs::EFinal_RW) but for ALL drives
bDriveFinalised = DoCheckVolumeFinalised(gDriveNum);
test(bDriveFinalised);
nRes = CreateEmptyFile(TheFs, KFileName, 128000);
test(nRes == KErrNone);
}
//----------------------------------------------------------------------------------------------
//! @SYMTestCaseID PBASE-T_MOUNT-0523
//! @SYMTestType UT
//! @SYMPREQ PREQ1721
//! @SYMTestCaseDesc testing boot and backup boot sectors on FAT32 volume
//!
//! @SYMTestActions
//! 0 Quick format the drive
//! 1 read main and backup boot & fsinfo sectors and check their validity
//! 2 corrupt main boot sector and check that the drive can be mounted (backup boot sector is used)
//! 3 corrupt backup boot sector and check that the drive can not be mounted.
//! 4 Quick format the drive to restore test environment
//!
//! @SYMTestExpectedResults finishes if the boot sector and backup boot sector functionality compliant with the FAT specs. panics otherwise
//! @SYMTestPriority High
//! @SYMTestStatus Implemented
//----------------------------------------------------------------------------------------------
static void TestBackupBootSector()
{
test.Next(_L("Testing Backup Boot Sector.\n"));
TInt nRes;
//-- quick format the drive
FormatVolume(ETrue);
if(!Is_Fat32(TheFs, gDriveNum))
{
test.Printf(_L("This step requires FAT32 ! Skipping.\n"));
return;
}
TFatBootSector mainBootSec, backupBootSec;
TFSInfo mainFSInfo, backupFSInfo;
const TUint32 posMainBootSec = KBootSectorNum << KDefaultSectorLog2;
const TUint32 posBkBootSec = KBkBootSectorNum << KDefaultSectorLog2;
//-- read main and backup boot & fsinfo sectors and check their validity
nRes = ReadBootSector(TheFs, gDriveNum, posMainBootSec, mainBootSec);
test_KErrNone(nRes);
//-- backup boot sector # must be 6
nRes = ReadBootSector(TheFs, gDriveNum, posBkBootSec, backupBootSec);
test_KErrNone(nRes);
test(mainBootSec.IsValid());
test(backupBootSec.IsValid());
test(mainBootSec == backupBootSec);
//-- read fsinfo sectors
const TUint32 posMainFSInfo = mainBootSec.FSInfoSectorNum() << KDefaultSectorLog2;
const TUint32 posBkFSInfo = (KBkBootSectorNum + mainBootSec.FSInfoSectorNum()) << KDefaultSectorLog2;
test(posMainFSInfo != 0);
test(posBkFSInfo != 0);
nRes = ReadFSInfoSector(TheFs, gDriveNum, posMainFSInfo, mainFSInfo);
test_KErrNone(nRes);
nRes = ReadFSInfoSector(TheFs, gDriveNum, posBkFSInfo, backupFSInfo);
test_KErrNone(nRes);
test(mainFSInfo.IsValid());
test(backupFSInfo.IsValid());
test(mainFSInfo == backupFSInfo);
//-- corrupt main boot sector and check that the drive can be mounted
test.Printf(_L("Corrupting main boot sector...\n"));
//-- A1. corrupt main boot sector starting from the pos:0
nRes = FillMedia(TheFs, gDriveNum, posMainBootSec, posMainBootSec+KDefaultSectorSize, 0xaa);
//-- A2. remount FS, it shall be OK because of the using backup boot sector
nRes = RemountFS(TheFs, gDriveNum);
test_KErrNone(nRes);
//-- B1. corrupt BACKUP boot sector starting from the sec:6
nRes = FillMedia(TheFs, gDriveNum, posBkBootSec, posBkBootSec+KDefaultSectorSize, 0xbb);
//-- B2. remount FS, unable to mount.
nRes = RemountFS(TheFs, gDriveNum);
test(nRes == KErrCorrupt);
//-- quick format the drive
FormatVolume(ETrue);
}
//----------------------------------------------------------------------------------------------
//! @SYMTestCaseID PBASE-T_MOUNT-0524
//! @SYMTestType UT
//! @SYMPREQ PREQ1721
//! @SYMTestCaseDesc testing FSInfo sector functionality.
//!
//! @SYMTestActions
//! 0 Quick format the drive
//! 1 finalise the drive to write correct data to the FSInfo & its backup copy.
//! 2 read FSInfo sector, its backup copy and check that they are both valid, identical and contain correct data
//! 3 check that after the formatting FS info values are the same as real, obtained from the FAT scanning.
//! 4 create a random - sized file, check that after finalisation the number of free clusters is identical to the FSinfo
//! 5 Quick format the drive to restore test environment
//!
//! @SYMTestExpectedResults finishes if the FSInfo sectors functionality compliant with the FAT specs. panics otherwise
//! @SYMTestPriority High
//! @SYMTestStatus Implemented
//----------------------------------------------------------------------------------------------
static void TestFSInfoSector()
{
test.Next(_L("Testing FSInfo Sector.\n"));
#ifndef _DEBUG
test.Printf(_L("Skipping the test in the Release build! \n"));
return;
#else
TInt nRes;
//-- quick format the drive
FormatVolume(ETrue);
if(!Is_Fat32(TheFs, gDriveNum))
{
test.Printf(_L("This step requires FAT32 ! Skipping.\n"));
return;
}
TFatBootSector bootSec;
const TUint32 posMainBootSec = KBootSectorNum << KDefaultSectorLog2;
nRes = ReadBootSector(TheFs, gDriveNum, posMainBootSec, bootSec);
test_KErrNone(nRes);
const TUint32 bytesPerSector = bootSec.BytesPerSector();
const TUint32 secPerClust = bootSec.SectorsPerCluster();
//-- finalise the drive, just in case
nRes =TheFs.FinaliseDrive(gDriveNum, RFs::EFinal_RW);
test_KErrNone(nRes);
//============= 1. read FSInfo sector and its backup copy and compare them
TFSInfo fsInfoSec;
//-- main FSInfo
nRes = ReadFSInfoSector(TheFs, gDriveNum, KFSInfoSectorNum*bytesPerSector, fsInfoSec);
test_KErrNone(nRes);
test(fsInfoSec.IsValid());
TUint32 freeClusters_FSInfo = fsInfoSec.FreeClusterCount();
TUint32 nextFree_FSInfo = fsInfoSec.NextFreeCluster();
//-- backup FSInfo
nRes = ReadFSInfoSector(TheFs, gDriveNum, KBkFSInfoSectorNum*bytesPerSector, fsInfoSec);
test_KErrNone(nRes);
test(fsInfoSec.IsValid());
//-- both copies must be identical
test(freeClusters_FSInfo == fsInfoSec.FreeClusterCount());
test(nextFree_FSInfo == fsInfoSec.NextFreeCluster());
//-- FAT[0] and FAT[1] are not used; FAT[2] is taken by the 1st cluster of the FAT32 Root directory.
test(nextFree_FSInfo == (2+1));
//============= 2. check that after the formatting FS info values are the same as real.
//-- 2.1 disable using FSInfo and other stuff
SetFsyDebugFlag(gDriveNum, KMntProp_Disable_FsInfo);
//-- remount FAT; using FSInfo is disabled. FAT will be explicitly scanned.
nRes = RemountFS(TheFs, gDriveNum);
test_KErrNone(nRes);
//-- restore mounting mechanism
SetFsyDebugFlag(gDriveNum, KMntProp_EnableALL);
//-- get free clusters number from the FSY, which in turn had counted them explicitly
TVolumeInfo volInfo;
nRes = TheFs.Volume(volInfo, gDriveNum);
test_KErrNone(nRes);
TUint32 freeClusters = (TUint32)(volInfo.iFree / (bytesPerSector * secPerClust));
test(freeClusters == freeClusters_FSInfo);
//============= 3. create a random - sized file, check that after finalisation the number of free clusters is identical to the FSinfo
_LIT(KFileName, "\\FILE1.DAT");
const TUint32 rndClusters = 7+((TUint32)Math::Rand(gRndSeed)) % 5000;
const TUint32 fileSz = rndClusters * bytesPerSector * secPerClust;
nRes = CreateEmptyFile(TheFs, KFileName, fileSz);
test_KErrNone(nRes);
//-- 3.1 get data from FS
nRes = TheFs.Volume(volInfo, gDriveNum);
test_KErrNone(nRes);
freeClusters = (TUint32)(volInfo.iFree / (bytesPerSector * secPerClust));
//-- 3.2 finalise the volume and get data from FSInfo
nRes =TheFs.FinaliseDrive(gDriveNum, RFs::EFinal_RW);
test_KErrNone(nRes);
//-- main FSInfo
nRes = ReadFSInfoSector(TheFs, gDriveNum, KFSInfoSectorNum*bytesPerSector, fsInfoSec);
test_KErrNone(nRes);
test(fsInfoSec.IsValid());
freeClusters_FSInfo = fsInfoSec.FreeClusterCount();
nextFree_FSInfo = fsInfoSec.NextFreeCluster();
//-- backup FSInfo
nRes = ReadFSInfoSector(TheFs, gDriveNum, KBkFSInfoSectorNum*bytesPerSector, fsInfoSec);
test_KErrNone(nRes);
test(fsInfoSec.IsValid());
//-- both copies must be identical
test(freeClusters_FSInfo == fsInfoSec.FreeClusterCount());
test(nextFree_FSInfo == fsInfoSec.NextFreeCluster());
//-- the information in FSInfo must be the same as in FAT
test(freeClusters == freeClusters_FSInfo);
TBool bDriveFinalised = DoCheckVolumeFinalised(gDriveNum);
test(bDriveFinalised);
TheFs.Delete(KFileName);
//-- restore mounting mechanism
SetFsyDebugFlag(gDriveNum, KMntProp_EnableALL);
#endif //_DEBUG
}
//-------------------------------------------------------------------
/** initialise test global objects */
static void InitGlobals()
{
TInt nRes;
//-- define a propery which will control mount process in the fsy.
//-- The property key is a drive number being tested
_LIT_SECURITY_POLICY_PASS(KTestPropPolicy);
nRes = RProperty::Define(KThisTestSID, gDriveNum, RProperty::EInt, KTestPropPolicy, KTestPropPolicy);
test(nRes == KErrNone || nRes == KErrAlreadyExists);
nRes = RProperty::Set(KThisTestSID, gDriveNum, KMntProp_EnableALL);
test_KErrNone(nRes);
gRndSeed = Math::Random();
(void)&gRndSeed; //-- get rid of warning
}
/** destroy test global objects */
static void DestroyGlobals()
{
//-- delete test property
RProperty::Delete(KThisTestSID, gDriveNum);
TVolumeInfo v;
TheFs.Volume(v);
}
//-------------------------------------------------------------------
/**
Manual test. Requires manual removing and putting back the media.
On the emulator one can use pressing (and holding) F5 key to simulate media removal.
*/
void Manual_TestRemount_On_MediaRemoval()
{
TInt nRes;
_LIT(KFileName, "\\my_file1.dat");
const TUint32 KFileSz = K1MegaByte;
//-- 1. create a file
nRes = CreateEmptyFile(TheFs, KFileName, KFileSz);
test_KErrNone(nRes);
RFile file;
nRes = file.Open(TheFs, KFileName, EFileRead | EFileWrite);
test_KErrNone(nRes);
TBuf8<512> buf(512);
TVolumeInfo vi;
buf.FillZ();
TKeyCode key;
for(;;)
{
TheFs.SetDebugRegister(0x00);
nRes = file.Read(0, buf);
test.Printf(_L("Remove the media and press a key.\n"));
key = test.Getch();
if(key == EKeyEscape)
break;
TheFs.SetDebugRegister(KFSYS | KFSERV);
nRes = file.Read(0, buf);
if(nRes != KErrNone)
{
test.Printf(_L("ReadFile: %d!\n"), nRes);
key = test.Getch();
if(key == EKeyEscape)
break;
nRes = TheFs.Volume(vi,gDriveNum);
test.Printf(_L("Volume: %d!\n"), nRes);
key = test.Getch();
if(key == EKeyEscape)
break;
nRes = file.Write(0, buf);
test.Printf(_L("WriteFile: %d!\n"), nRes);
key = test.Getch();
if(key == EKeyEscape)
break;
}
}
file.Close();
}
//-------------------------------------------------------------------
/**
Wait for the request aRqStat to be completed with timeout.
@param aRqStat request status object we need to wait to complete
@param aTimeout_uS timeout in microseconds
@return ETrue if the aRqStat is completed before time is out
EFalse if aTimeout_uS has passed. And the state of the aRqStat not changed.
*/
TBool WaitForRequestWithTimeout(TRequestStatus& aRqStat, TUint32 aTimeout_uS)
{
TRequestStatus rqStatTimeout(KRequestPending);
RTimer tmrTimeOut;
TInt nRes;
TBool bReqCompleted;
if(aRqStat.Int() != KRequestPending)
return ETrue; //-- nothing to wait for.
//-- set up a timeout timer
nRes = tmrTimeOut.CreateLocal();
test(nRes == KErrNone);
tmrTimeOut.After(rqStatTimeout, aTimeout_uS);
User::WaitForRequest(aRqStat, rqStatTimeout);
if(aRqStat == KRequestPending)
{//-- timeout.
bReqCompleted = EFalse;
}
else
{//-- the main request has been completed, cancel timer
bReqCompleted = ETrue;
if(rqStatTimeout.Int() == KRequestPending)
{
tmrTimeOut.Cancel();
User::WaitForRequest(rqStatTimeout);
}
}
tmrTimeOut.Close();
return bReqCompleted;
}
//-------------------------------------------------------------------
void TestNotifyDiskSpace()
{
test.Next(_L("Testing NotifyDiskSpace() on asynchronous mounting\n"));
#ifndef _DEBUG
test.Printf(_L("Skipping the test in the Release build! \n"));
return;
#else
TInt nRes;
TRequestStatus rqStat;
TVolumeInfo volInfo;
//-- quick format the drive
FormatVolume(ETrue);
if(!Is_Fat32(TheFs, gDriveNum))
{//-- only FAT32 supports asynch mounting;
test.Printf(_L("This test step requires FAT32!\n"));
return;
}
//-- FAT32 free space threshold that is supposed to be triggered by notifiers
const TInt64 KFreeSpaceThreshold = 300*K1MegaByte;
//-- Turn OFF using FSInfo and ON FAT32 background scanning; it will cause compulsory FAT32 background free clusters scan.
//-- if FAT32 background free clusters scan is disabled in config, this test can hang on waiting for free space notifications.
test.Printf(_L("==== Enabled FAT32 BkGnd scan, FSInfo disabled ====\n"));
SetFsyDebugFlag(gDriveNum, KMntProp_DisableALL & ~KMntProp_Disable_FatBkGndScan);
//===== create a big file in the beginning in order to avoid freeSpaceThreshold being reached too fast
nRes = CreateEmptyFile(TheFs, _L("\\big_empty_file.bin"), 10*K1MegaByte);
test_KErrNone(nRes);
//TheFs.SetDebugRegister(0x03);
//===== Test that FAT32 free space scanning thread updates File Server free space notifiers
test.Printf(_L("Testing FAT32 free space scanning thread triggers notifiers..."));
nRes = RemountFS(TheFs, gDriveNum); //-- remount FS; it must cause FAT32 free space scan in background
test_KErrNone(nRes);
test.Printf(_L("Waiting for %LU bytes available on the volume...\n"), KFreeSpaceThreshold);
//-- check if we can use the notifiers at all... If free space is >= KFreeSpaceThreshold, the notifier won't be triggered
//-- get _current_ amount of free space asynchronously
TheFs.Volume(volInfo, gDriveNum, rqStat);
User::WaitForRequest(rqStat);
test(rqStat.Int()==KErrNone);
if(volInfo.iSize <= KFreeSpaceThreshold)
{
test.Printf(_L("The volume is too small for %LU bytes notify ...\n"), KFreeSpaceThreshold);
test.Printf(_L("The test is inconclusive ...\n"));
return;
}
if(volInfo.iFree >= KFreeSpaceThreshold)
{
test.Printf(_L("The volume already has %LU free bytes...\n"), volInfo.iFree);
test.Printf(_L("The test is inconclusive ...\n"));
return;
}
TheFs.NotifyDiskSpace(KFreeSpaceThreshold, gDriveNum, rqStat);
test(rqStat.Int()==KRequestPending);
//-- wait for notification for 30 seconds; If for some reason FAT32 background scanning for free clusters doesn't
//-- work (e.g. configued out in FAT), this test is inconclusive.
TBool bCompleted = WaitForRequestWithTimeout(rqStat, 30*K1Sec);
if(!bCompleted)
{
test.Printf(_L("Wait timeout! something is wrong...\n"));
test(0);
}
test_KErrNone(rqStat.Int());
//-- get _current_ amount of free space asynchronously
TheFs.Volume(volInfo, gDriveNum, rqStat);
User::WaitForRequest(rqStat);
test(rqStat.Int()==KErrNone);
test.Printf(_L("Current amount of free space on the volume: %LU \n"), volInfo.iFree);
//===== Test that aborting FAT32 free space scanning thread will trigger notifiers
test.Printf(_L("Testing aborting FAT32 free space scanning thread..."));
nRes = RemountFS(TheFs, gDriveNum); //-- remount FS; it must cause FAT32 free space scan in background
test_KErrNone(nRes);
TheFs.NotifyDiskSpace(KFreeSpaceThreshold, gDriveNum, rqStat);
test(rqStat.Int()==KRequestPending);
nRes = RemountFS(TheFs, gDriveNum); //-- remount FS; it will abort the scanning thread
test_KErrNone(nRes);
test(rqStat.Int() != KRequestPending);
//-- get _current_ amount of free space asynchronously
TheFs.Volume(volInfo, gDriveNum, rqStat);
User::WaitForRequest(rqStat);
test(rqStat.Int()==KErrNone);
test.Printf(_L("Current amount of free space on the volume: %LU \n"), volInfo.iFree);
//-- find out free space on the volume; it will also blocks until FAT32 free space scanning finishes.
nRes = TheFs.Volume(volInfo);
test(nRes==KErrNone);
test.Printf(_L("free space on the volume: %LU \n"), volInfo.iFree);
//TheFs.SetDebugRegister(0x00);
//-- restore mounting mechanism
SetFsyDebugFlag(gDriveNum, KMntProp_EnableALL);
#endif //_DEBUG
}
//-------------------------------------------------------------------
void CallTestsL()
{
//-- set up console output
Fat_Test_Utils::SetConsole(test.Console());
TInt nRes=TheFs.CharToDrive(gDriveToTest, gDriveNum);
test(nRes==KErrNone);
//-- check if this is FAT
if(!Is_Fat(TheFs, gDriveNum) || Is_Automounter(TheFs, gDriveNum))
{//-- it doesn't make much sense to run this test under automounter+FAT. The automounter can't easily handle formatting corrupted media
//-- and the mounting permaormance measurements don't make much sense in this case as well.
test.Printf(_L("Skipping. This test requires explicitly mounted FAT file system.\n"));
return;
}
//-- check this is not the internal ram drive
TVolumeInfo v;
nRes = TheFs.Volume(v);
test(nRes==KErrNone);
if(v.iDrive.iMediaAtt & KMediaAttVariableSize)
{
test.Printf(_L("Skipping. Internal ram drive not tested.\n"));
return;
}
//-------------------------------------
PrintDrvInfo(TheFs, gDriveNum);
InitGlobals();
TestNotifyDiskSpace();
//-------------------------------------
TestBackupBootSector();
TestFSInfoSector();
TestFinaliseFS();
//-------------------------------------
TestFAT_Mounting_Performance();
//-------------------------------------
//-- manual test
//Manual_TestRemount_On_MediaRemoval();
//-------------------------------------
DestroyGlobals();
}