diff -r 000000000000 -r a41df078684a kerneltest/f32test/filesystem/fat/t_mount.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kerneltest/f32test/filesystem/fat/t_mount.cpp Mon Oct 19 15:55:17 2009 +0100 @@ -0,0 +1,1312 @@ +// 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 +#include +#include +#include +#include + +#include "t_server.h" +#include "fat_utils.h" + +using namespace Fat_Test_Utils; + +#ifdef __VC32__ + // Solve compilation problem caused by non-English locale + #pragma setlocale("english") +#endif + +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 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 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; i0; + + 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; i0; + + 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(); + + } + + + + + + + + + + + + + + + + + +