// 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:
// Common CFatMountCB code for both EFAT.FSY and EFAT32.fsy
//
//

/**
 @file
 @internalTechnology
*/

//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//!!
//!! WARNING!! DO NOT edit this file !! '\sfat' component is obsolete and is not being used. '\sfat32'replaces it
//!!
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!


#include "sl_std.h"
#include "sl_cache.h"
#include "sl_leafdir_cache.h"
#include "sl_dir_cache.h"
#include "sl_scandrv.h"
#include <hal.h>

TShortName DoGenerateShortNameL(const TDesC& aLongName,TInt& aNum,TBool aUseTildeSelectively);

static void  MarkClusterVisited(RBitVector& aFatBitVec, TUint32 aCluster);
static TBool IsClusterVisited(const RBitVector& aFatBitVec, TUint32 aCluster);
static TInt  NextUnvisitedCluster(const RBitVector& aFatBitVec, TUint32 aCluster);

//-----------------------------------------------------------------------------------------

TFatVolParam::TFatVolParam()
    {
    Mem::FillZ(this, sizeof(TFatVolParam));
    }

/**
    populate the object with the values from the boot sector.
    @param aBootSector a reference to the valid boots sector
*/
void TFatVolParam::Populate(const TFatBootSector& aBootSector)
    {
    ASSERT(aBootSector.IsValid());

    iSectorsPerCluster = aBootSector.SectorsPerCluster();
    iSectorSizeLog2    = Log2(aBootSector.BytesPerSector());
    iClusterSizeLog2   = iSectorSizeLog2+Log2(iSectorsPerCluster);
    iFirstFatSector    = aBootSector.FirstFatSector();
    iNumberOfFats      = aBootSector.NumberOfFats();
    iFatSizeInBytes    = aBootSector.TotalFatSectors()*aBootSector.BytesPerSector();
    iTotalSectors      = aBootSector.VolumeTotalSectorNumber();
    iRootClusterNum    = aBootSector.RootClusterNum(); //-- will be 0 for FAT12/16

    iRootDirectorySector = aBootSector.RootDirStartSector();
    iRootDirEnd = (iRootDirectorySector + aBootSector.RootDirSectors()) << SectorSizeLog2(); //-- doesn't matter for FAT32

    //-- get main and backup FSInfo sectors position, these fields will be 0 for FAT12/16
    iFSInfoSectorNum   = aBootSector.FSInfoSectorNum();
    iBkFSInfoSectorNum = (TUint16)(aBootSector.BkBootRecSector()+iFSInfoSectorNum); //-- Bk FSInfo sector must follow the Bk boot sector
    }

TBool TFatVolParam::operator==(const TFatVolParam& aRhs) const
    {
    ASSERT(&aRhs != this);
    if(&aRhs == this)
        return ETrue; //-- comparing with itself

    return (Mem::Compare((TUint8*)this, sizeof(TFatVolParam), (TUint8*)&aRhs, sizeof(TFatVolParam)) == 0);
    }


//-----------------------------------------------------------------------------------------


CFatMountCB::CFatMountCB()
    {
    __PRINT2(_L("CFatMountCB::CFatMountCB() 0x%x, %S"), this, &KThisFsyName);
    iFatType = EInvalid;
    iState   = ENotMounted;
    DBG_STATEMENT(iCBRecFlag = 0); //-- debug flag only
    }

CFatMountCB::~CFatMountCB()
    {
    __PRINT1(_L("#-CFatMountCB::~CFatMountCB() 0x%x"), this);

    DoDismount();

    delete iNotifier;
    delete iFatTable;
    delete iRawDisk;
    delete iLeafDirCache;

    }

//-----------------------------------------------------------------------------------------

CFatMountCB* CFatMountCB::NewL()
    {
    CFatMountCB* pSelf = new(ELeave) CFatMountCB;

    CleanupStack::PushL(pSelf);
    pSelf->ConstructL();
    CleanupStack::Pop(pSelf);

    return pSelf;
    }

// second-stage constructor
void CFatMountCB::ConstructL()
{
    //-- create Notifier
    iNotifier = CAsyncNotifier::New();
    if( !iNotifier )
        {
        Close();
        User::Leave(KErrNoMemory);
        }

    iNotifier->SetMount(this);
    }

//-------------------------------------------------------------------------------------------------------------------

/**
Implementation of CMountCB::FileSystemClusterSize(). Returns cluster size of this mount.
@return Cluster size value if successful; otherwise KErrNotReady if the mount is not ready.
@see CMountCB::FileSystemClusterSize()
*/
TInt CFatMountCB::ClusterSize() const
    {
    if (ClusterSizeLog2())
        return (1 << ClusterSizeLog2());

    return KErrNotReady;
    }

//-------------------------------------------------------------------------------------------------------------------

/**
    @leave KErrAccessDenied if the mount is read-only
*/
void CFatMountCB::CheckWritableL() const
    {
    if(ReadOnly())
        {
        __PRINT(_L("CFatMountCB is RO!"));
        User::Leave(KErrAccessDenied);
        }
    }

/**
    @leave KErrCorrupt if the mount is in inconsistent state i.e high-level file and directory  operations can not be performed
*/
void CFatMountCB::CheckStateConsistentL() const
    {
    if(!ConsistentState())
        {
        __PRINT(_L("CFatMountCB state is inconsistent !"));
        User::Leave(KErrCorrupt);
        }
    }


//-------------------------------------------------------------------------------------------------------------------
/**
    Helper Method. Check if the parameters of the volume being remounted are the same as current ones.
    @return ETrue if volume parameters remained same.
*/
TBool CFatMountCB::CheckVolumeTheSame()
{
    //-- initialise local drive
    TInt nRes =InitLocalDrive();
    if(nRes != KErrNone)
        return EFalse;

    //-- read the boot sector or its backup copy if the main is damaged. It will aslo validate it.
    TFatBootSector bootSector;
    nRes = ReadBootSector(bootSector, iRamDrive);
    if(nRes != KErrNone)
        return EFalse;

    //-- 1. check volume Uid
    if(iUniqueID != bootSector.UniqueID())
        return EFalse;

    //-- check volume parameters, they must remain the same
    TFatVolParam volParam;
    volParam.Populate(bootSector);

    if(!(volParam == iVolParam))
        return EFalse;


    return ETrue;

}
//-------------------------------------------------------------------------------------------------------------------

/**
    Helper Method. Check if the parameters of the volume being remounted are the same as current ones.
    If they are, re-initialises the mount.
*/
void CFatMountCB::DoReMountL()
    {

    if(!CheckVolumeTheSame())
        User::Leave(KErrGeneral);

    //-- get drive capabilities
    TLocalDriveCapsV2Buf capsBuf;
    User::LeaveIfError(LocalDrive()->Caps(capsBuf));

    //-- the volume is the same as it was on original MountL()
    //-- we need to re-initialize for the case when the media was removed, FAT or directory structure changed on other device and the media returned back.
    DoDismount();

    SetState(EMounting);

    InitializeL(capsBuf(), ETrue); //-- forcedly disable FSInfo usage. This will lead to FAT free clusters re-counting.

    }

//-------------------------------------------------------------------------------------------------------------------

/**
    Try remount this Fat volume. Checks if the volume parameters remained the same as on original MountL() call, and
    if they are, re-initialises the mount. This includes resetting all caches.
    ! Do not call this method from TFatDriveInterface methods, like citical and non-critical notifiers ! This can lead to the
    recursive loops and undefined behaviour.

    @return KErrNone if the remount was OK
            system-wide error code otherwise
*/
TInt CFatMountCB::ReMount()
    {
    __PRINT2(_L("CFatMountCB::ReMount(), drv:%d, curr state:%d"), DriveNumber(), State());

    const TFatMntState currState = State();

    //-- analyse the mount state and find out if we can remount at all.
    switch(currState)
        {
        case ENotMounted:
        __PRINT(_L("CFatMountCB::ReMount() Invalid mount state!"));

        ASSERT(0);
        return KErrGeneral;

        //-- correct state, proceed to remount
        default:
        break;
    }

    //-- there are 2 options here:
    //-- 1. normally initialised mount had been forcedly dismounted (it can optionally have objects opened on it)
    //--    in this case the DoReMountL() will succeed and everything will be fine, the objects will be accessible afterwards
    //-- 2. the mount hasn't been initialised at all (it does not have for example, FAT table created etc.)
    //--    in this case we may need to fake the success. This can only happen on forced mount by CFormatCB
    TInt nRes;
    TRAP(nRes, DoReMountL());

    if(nRes != KErrNone)
        {
        //-- note that the mount may be here left in inconsistent state (EMounting)
        //-- if DoReMountL() fails. This is OK, because we can not make any valid read/write operations in such a state and
        //-- the drive must be dismounted and mounted again. File Server's TDrive shall do this.
        __PRINT1(_L("CFatMountCB::ReMount() failed! code:%d"), nRes);

        //-- If we are in the EInit_Forced state, it means that we are trying to remount the volume that has been formatted.
        //-- scenario: On formatting, if we can't read a bootsector, new _empty_ object of the CFatMountCB is created and
        //-- it is used for performing a format. If the format has finished, but RFormat isn't closed yet and we try to access the volume,
        //-- we will get here, because all members of the constructed mount will be zeroes.
        if(currState == EInit_Forced)
            {
            __PRINT(_L("CFatMountCB::ReMount() simulating normal remount!"));
            SetState(currState);
            return KErrNone;
            }

        return nRes;
        }

    __PRINT1(_L("CFatMountCB::ReMount() Completed drv:%d"), DriveNumber());
    SetState(EInit_R);
    return nRes;
    }

//-------------------------------------------------------------------------------------------------------------------

/**
    Reset the last leaf dir or invalidate leaf dir cache if leaf dir cache is
    instantiated.
*/

void CFatMountCB::InvalidateLeafDirCache()
    {
    if (iLeafDirCache)
        {
        iLeafDirCache->Reset();
        }
    else
        {
        User::Free(iLastLeafDir);
        iLastLeafDir=NULL;
        }
    }

//-------------------------------------------------------------------------------------------------------------------

/**
    Delete mount's caches
    Moves CFatMountCB into ENotMounted state immediately.
*/
void CFatMountCB::DoDismount()
    {
    __PRINT1(_L("CFatMountCB::DoDismount() drv:%d"), DriveNumber());

    //-- try to flush and destroy FAT cache
    if (iFatTable)
        {
        if(!ConsistentState() || ReadOnly())
            {//-- the mount state is inconsistent, so the data can't be flushed. Ignore dirty cache either.
            iFatTable->Dismount(ETrue);
            }
        else
            {//-- Try to flush the FAT - if this fails there's not much we can do
            TRAPD(r, iFatTable->FlushL());
            iFatTable->Dismount(r != KErrNone); //-- ignore dirty data if we failed to flush the cache
            }
        }

    //-- destroy leafdir name cache, this cache will be re-created while mounting or re-mounting
    //-- see CFatMountCB::InitializeL()
    delete iLeafDirCache;
    iLeafDirCache = NULL;

    //-- destroy directory cache, this cache will be re-created while mounting or re-mounting
    //-- see CFatMountCB::InitializeL()
    delete iRawDisk;
    iRawDisk = NULL;

    //-- Set mount state to "Dismounted". Which means that there might be no caches, but the mount is alive,
    //-- i.e. iFatTable & iRawDisk are valid
    SetState(EDismounted);
    }

//-----------------------------------------------------------------------------------------

/** old implementation */
void CFatMountCB::FinaliseMountL()
    {
    FinaliseMountL(RFs::EFinal_RW);
    }

//-----------------------------------------------------------------------------------------
/**
    Dismount the CFatMountCB and the drive.
    called from TDrive::Dismount().
*/
void CFatMountCB::Dismounted()
    {
    __PRINT1(_L("CFatMountCB::Dismounted() drv:%d"), DriveNumber());

    //-- n.b. it is no safe to do a kind of filnalisatin work here that implies accessing the media.
    //-- this method may be called after the media change occured from the TDrive::Dismount(). It means
    //-- that if we try to write some data here, they could be written into a different medium, if it had been
    //-- physically changed.

    const TFatMntState prevState = State();

    DoDismount(); //-- it will change mount state to EDismounted
    DismountedLocalDrive();

    //-- check if the previous state was EInit_Forced, which means that this method was called
    //-- on the mount that might not be alive (no valid iFatTable & iRawDisk).
    //-- This can happen only during format operation on non-mounted previously volume.
    //-- this EInit_Forced state must be processed separately, see ::Remount()
    if(prevState == EInit_Forced)
        SetState(EInit_Forced);

    }


//-------------------------------------------------------------------------------------------------------------------

/**
    Find out if the mount is finalised.
    @param  aFinalised on exit will be ETrue if the maunt is finalised, EFalse otherwise.
    @return standard error codes.
*/
TInt CFatMountCB::IsFinalised(TBool& aFinalised)
    {
    switch(State())
        {
        case EFinalised: //-- already explicitly finalised
            aFinalised = ETrue;
        return KErrNone;

        case EInit_W: //-- the volume had been written
            aFinalised = EFalse;
        return KErrNone;

        default: //-- it depends on the state
        break;
        }

    //-- find out if the volume is _physically_ finalised.
    //-- It can be in the state EInit_R, but finalised before mounting
    if(!VolCleanFlagSupported())
        return KErrNotSupported;

    TInt nRes = KErrNone;
    TRAP(nRes, aFinalised = VolumeCleanL());

    return nRes;
    }

//-------------------------------------------------------------------------------------------------------------------

/**
    @return ETrue if the mount is in consistent state i.e. normally mounted.
    See TFatMntState enum for more detail.
*/
TBool CFatMountCB::ConsistentState() const
    {
    return (iState==EInit_R) || (iState==EInit_W) || (iState == EFinalised);
    }

//-------------------------------------------------------------------------------------------------------------------

/**
    Open CFatMountCB for write. I.e. perform some actions on the first write attempt.
    This is a callback from TFatDriveInterface.
    @return System wide error code.
*/
TInt CFatMountCB::OpenMountForWrite()
    {
    if(State() == EInit_W)
        return KErrNone; //-- nothing to do, the mount is already opened for write

    __PRINT1(_L("#- CFatMountCB::OpenMountForWrite() drv:%d\n"),DriveNumber());

    ASSERT(State() == EInit_R || State() == EFinalised);

    //-- Check possible recursion. This method must not be called recursively. SetVolumeCleanL() works through direct disc access and
    //-- can not call TFatDriveInterface methods that call this method etc.
    ASSERT(iCBRecFlag == 0);
    DBG_STATEMENT(iCBRecFlag = 1); //-- set recursion check flag

    //-- do here some "opening" work, like marking volme as dirty
    //-- be careful here, as soon as this is a callback from TFatDriveInterface, writing via TFatDriveInterface may cause some unwanted recursion.

    //-- mark the volume as dirty
    TInt nRes=KErrNone;
    TRAP(nRes, SetVolumeCleanL(EFalse));
    if(nRes == KErrNone)
        {
        SetState(EInit_W);
        }

    DBG_STATEMENT(iCBRecFlag = 0); //-- reset recursion check flag

    return nRes;

    }

//-------------------------------------------------------------------------------------------------------------------

/**
    Unfinalise the mount, reset "VolumeCleanShutDown" flag and change the state if necessarily.
*/
void CFatMountCB::UnFinaliseMountL()
    {
    switch(State())
        {
        case EFinalised:
        case EInit_R:
            SetVolumeCleanL(EFalse); //-- the mount, mark volume "dirty"
            SetState(EInit_R);
        return;

        case EInit_W:
        return; //-- nothing to do

        default:
        //-- other mount states are inconsistent; can't perform this operation
        User::Leave(KErrAbort);
        break;

        }

    }

//-------------------------------------------------------------------------------------------------------------------

/**
    Finalise the mount.

    @param  aOperation  describes finalisation operation ,see RFs::TFinaliseDrvMode
    @param  aParam1     not used, for future expansion
    @param  aParam2     not used, for future expansion

    @leave  System wide error code. particular cases:
            KErrArgument invalid arguments
            KErrInUse    if the volume has opened objects (files, directories etc)
            KErrCorrupt  if the volume is corrupt

*/
void CFatMountCB::FinaliseMountL(TInt aOperation, TAny* /*aParam1*/, TAny* /*aParam2*/)
    {
    __PRINT2(_L("#- CFatMountCB::FinaliseMountL() op:%d, drv:%d"), aOperation, DriveNumber());

    switch(aOperation)
        {
        case RFs::EFinal_RW:
        case RFs::EFinal_RO:
        break;

        case RFs::EForceUnfinalise:
            UnFinaliseMountL();
        return;

        default:
            __PRINT1(_L("#- CFatMountCB::FinaliseMountL() unexpected operation!:%d"), aOperation);
            ASSERT(0);
            User::Leave(KErrArgument);
        return;
        }

    //-- mount finalisation work

    ASSERT(aOperation == RFs::EFinal_RW || aOperation == RFs::EFinal_RO);

    if(State() == EFinalised)
        {//-- the mount is already finalised. All we can do is to set it to RO mode
        if(ReadOnly() && aOperation == RFs::EFinal_RW)
            {
            User::Leave(KErrAccessDenied); //-- can't override RO flag
            }

        (void)LocalDrive()->Finalise(ETrue);

        if(aOperation == RFs::EFinal_RO)
            {
            SetReadOnly(ETrue);
            return;
            }

        return;
        }

    if(LockStatus() != 0)
        {//-- can't finalise the volume if it has opened objects and not in the consistent state.
         //-- Theoretically, we can finalise the mount if we have files opened only for read, but at present,
         //-- it's impossible to detect such situation.
        User::Leave(KErrInUse);
        }

    if(State() != EInit_R && State() != EInit_W)
        {//-- can't finalise the mount because it can be in an inconsistent state; e.g. corrupt.
        __PRINT1(_L("#- CFatMountCB::FinaliseMountL() Invalid mount State: %d"),State());
        User::Leave(KErrCorrupt);
        }

    //-- flush FAT cache
    FAT().FlushL();

    //-- for FAT32 we may need to update data in FSInfo sectors
    if(Is32BitFat())
        {
        if(FAT().ConsistentState())
            {//-- FAT table state is consistent and the number of free clusters is known.
             //-- Do it disregarding the mount state, it may help in the situation when 2 copies of the FSInfo are different for some reason.
            DoUpdateFSInfoSectorsL(EFalse);
            }
            else
            {//-- FAT table state is inconsistent, the most probable case here: background scan for free clusters is still working.
             //-- in this case we can't put corect values into the FSInfo.
            if(State() == EInit_W)
                {//-- bad situation: free clusters may be being counted and someone has already written something on the volume at the same time.
                 //-- we do not know the exact number of free clustes and can't wait until scan finishes. Invalidate FSInfo.
                __PRINT(_L("#- CFatMountCB::FinaliseMountL() invalidating FSInfo"));
                DoUpdateFSInfoSectorsL(ETrue);
                }
             else
                {//-- no changes on the volume, just do not update FSInfo
                __PRINT(_L("#- CFatMountCB::FinaliseMountL() FAT state inconsistent; FSInfo isn't updated"));
                }

            }//if(FAT().ConsistentState())

        }//if(Is32BitFat())



    //-- mark the volume as clean
    SetVolumeCleanL(ETrue);

    //-- finally, put the volume into RO mode if required
    if(aOperation == RFs::EFinal_RO)
        SetReadOnly(ETrue);

    SetState(EFinalised);
    }


//-------------------------------------------------------------------------------------------------------------------

/**
@return ETrue if "VolumeClean" flag is supported i.e. this is not FAT12
*/
TBool CFatMountCB::VolCleanFlagSupported() const
    {
        const TFatType fatType=FatType();

        ASSERT(fatType == EFat12 || fatType == EFat16 || fatType == EFat32);
        return (fatType != EFat12);
    }

//-----------------------------------------------------------------------------------------


/**
    Obtain the volume information.
    All information except iSize and iFree has been added by TDrive::Volume().

    @param  aVolume on return will contain iSize & iFree fields filled with actual data.
*/
void CFatMountCB::VolumeL(TVolumeInfo& aVolume) const
    {

    //-- if true, this operation will be synchronous, i.e the client will be suspended until FAT32 scanning thread finishes, if running.
    //-- the information if this operation is synchronous or not can be passed by client in TVolumeInfo::iFileCacheFlags field.
    //-- if the client sets aVolume.iVolSizeAsync flag there, RFs::Volume() will be asynchronous, i.e the _current_ number of free clusters
    //-- will be returned.
    const TBool bSyncOp = !aVolume.iVolSizeAsync;
    aVolume.iVolSizeAsync = EFalse; //-- reset this flag in order it not to be reused on the client side

    __PRINT2(_L("CFatMountCB::VolumeL() drv:%d, synch:%d"), DriveNumber(), bSyncOp);
    const TDriveInfo& drvInfo=aVolume.iDrive;

#if defined(__EPOC32__)
    // if RAM drive, cap size according to HAL.
    if (drvInfo.iType==EMediaRam)
        {
        TLocalDriveCapsV2Buf caps;
        LocalDrive()->Caps(caps);

        const TInt max_drive_size=TInt(caps().iEraseBlockSize);
        const TInt cur_drive_size=I64INT(caps().iSize);

        aVolume.iSize=max_drive_size;
        aVolume.iFree=max_drive_size-cur_drive_size;

        aVolume.iSize=aVolume.iFree+iSize;

        TInt maxSize;
        if (HAL::Get(HAL::EMaxRAMDriveSize, maxSize) == KErrNone)
            {
            // iSize will never grow beyond maxRam because of a check in medint.
            // d <= f; (s{f} + f) - m <= f; s{f} <= m
            __ASSERT_DEBUG(iSize <= maxSize, Fault(EFatRAMDriveSizeInvalid));
            if (aVolume.iSize > maxSize)
                {
                TInt64 d = aVolume.iSize - maxSize;
                __ASSERT_DEBUG(d <= aVolume.iFree, Fault(EFatRAMDriveFreeInvalid));
                aVolume.iSize -= d;
                aVolume.iFree -= d;
                }
            }

        aVolume.iSize-=ClusterBasePosition(); // Allow for bytes used by FAT etc
        aVolume.iSize=(aVolume.iSize>>ClusterSizeLog2())<<ClusterSizeLog2();  //-- round down to cluster size

        return;
        }//if (drvInfo.iType==EMediaRam)

#endif


    const TUint32 freeClusters = FAT().NumberOfFreeClusters(bSyncOp);

    __PRINT1(_L("CFatMountCB::VolumeL() free clusters:%d"), freeClusters);

    aVolume.iFree = (TInt64)freeClusters << ClusterSizeLog2();

    if (drvInfo.iType==EMediaRam)
        aVolume.iSize=aVolume.iFree+iSize;

    aVolume.iSize-=ClusterBasePosition(); // Allow for bytes used by FAT etc
    aVolume.iSize=(aVolume.iSize >> ClusterSizeLog2()) << ClusterSizeLog2();  //-- round down to cluster size

    }


//-----------------------------------------------------------------------------------------

//
//  Set the volume label (write aVolume label into BPB & Volume Label File)
//  aName string may be zero length but is assumed to contain no illegal characters or NULLs.
//
void CFatMountCB::SetVolumeL(TDes& aName)
    {

    __PRINT(_L("CFatMountCB::SetVolumeL"));

    CheckStateConsistentL();
    CheckWritableL();

    __ASSERT_ALWAYS(aName.Length()<=KVolumeLabelSize,User::Leave(KErrBadName));

    TBuf8<KVolumeLabelSize> buf8(KVolumeLabelSize);
    buf8.Zero();
    LocaleUtils::ConvertFromUnicodeL(buf8, aName, TFatUtilityFunctions::EOverflowActionLeave);
    aName.Zero();
    LocaleUtils::ConvertToUnicodeL(aName, buf8); // adjust aName (which may contain more underscores after this line than before)

    const TInt lengthOfBuf8=buf8.Length();
    // Pad to end with spaces if not empty.
    if (lengthOfBuf8>0 && lengthOfBuf8<KVolumeLabelSize)
        {
        buf8.SetLength(KVolumeLabelSize);
        Mem::Fill(&buf8[lengthOfBuf8],KVolumeLabelSize-lengthOfBuf8,' ');
        }

    // Write a volume label file
    WriteVolumeLabelFileL( buf8 );

    // Write the boot sector volume label
    // Always pad to full length with spaces
    if (lengthOfBuf8==0)
        {
        buf8.Fill(' ',KVolumeLabelSize);
        }

    WriteVolumeLabelL(buf8);
    }

//-----------------------------------------------------------------------------------------

/**
    Make a directory.
    @param aName full path to the directory to create. Name validity is checked by file server.
    all trailing dots from the name will be removed
*/
void CFatMountCB::MkDirL(const TDesC& aName)
    {
    __PRINT2(_L("CFatMountCB::MkDirL, drv:%d, %S"), DriveNumber(), &aName);

    CheckStateConsistentL();
    CheckWritableL();

    TPtrC dirName = RemoveTrailingDots(aName); //-- remove trailing dots from the name

    TInt namePos=dirName.LocateReverse(KPathDelimiter)+1; // There is always a path delimiter
    TPtrC name=dirName.Mid(namePos);
    TLeafDirData leafDir;
    const TEntryPos dirPos(FindLeafDirL(dirName.Left(namePos), leafDir),0);
    TEntryPos dumPos=dirPos;
    TFatDirEntry dumEntry;

    TBool isOriginalNameLegal = IsLegalDosName(name,EFalse,EFalse,EFalse,EFalse,ETrue);
    iFileCreationHelper.InitialiseL(name);
    TFileName fileName;
    TEntryPos startPos;
    TFatDirEntry startEntry;
    
    TRAPD(ret,DoFindL(name,KEntryAttMaskSupported,
                        startPos,startEntry,dumPos,dumEntry,
                        fileName,KErrNotFound,
                        &iFileCreationHelper,
                        leafDir));

    if (ret!=KErrNotFound && ret!=KErrNone)
        User::Leave(ret);
    if (ret!=KErrNotFound)
        {
        if (dumEntry.Attributes()&KEntryAttDir)
            User::Leave(KErrAlreadyExists);
        else
            User::Leave(KErrAccessDenied);
        }
    TShortName shortName;

    if (iFileCreationHelper.GetValidatedShortName(shortName) == KErrNotFound)
        {
        GenerateShortNameL(dirPos.iCluster,name,shortName,ETrue);
        }

    TInt numEntries=1;
    if (isOriginalNameLegal==EFalse)
        numEntries=NumberOfVFatEntries(name.Length());
    dumPos=dirPos;

    if (iFileCreationHelper.IsNewEntryPosFound())
        {
        dumPos = iFileCreationHelper.EntryAddingPos();
        }

    AddDirEntryL(dumPos,numEntries);    //  Directory entry in leaf directory
    TInt startCluster;
    FOREVER
        {
        //-- FAT().FreeClusterHint() will give us a hint of the last free cluster
        startCluster=FAT().AllocateSingleClusterL(dumPos.iCluster ? dumPos.iCluster : FAT().FreeClusterHint());

        FAT().FlushL();
        TRAPD(r, InitializeFirstDirClusterL(startCluster,dirPos.iCluster));
        if(r == KErrNone)
            break;
        if(r != KErrCorrupt)
            User::Leave(r);
        FAT().MarkAsBadClusterL(startCluster);
        }
    TFatDirEntry fatDirEntry;
    fatDirEntry.SetName(shortName);
    fatDirEntry.SetAttributes(KEntryAttDir);
    TTime now;
    now.UniversalTime();
    fatDirEntry.SetTime(now, TimeOffset());
    fatDirEntry.SetCreateTime(now, TimeOffset());
    fatDirEntry.SetStartCluster(startCluster);
    fatDirEntry.SetSize(0);
    if (isOriginalNameLegal)
        WriteDirEntryL(dumPos,fatDirEntry);
    else
        WriteDirEntryL(dumPos,fatDirEntry,name);

    iFileCreationHelper.Close();
    }

//-----------------------------------------------------------------------------------------

/**
    Setup 1st cluster of the new directory

    @param  aStartCluster   this entry start cluster number
    @param  aParentCluster  parent entry start cluster number
*/
void CFatMountCB::InitializeFirstDirClusterL(TInt aStartCluster,TInt aParentCluster)
    {
    const TUint32 KClusterSz= 1<<ClusterSizeLog2();
    const TUint32 KMaxBufSz = KClusterSz;           //-- max. nuffer size is a cluster
    const TUint32 KMinBufSz = 1<<SectorSizeLog2();  //-- min. buffer size is 1 sector (for OOM case)

    //-- allocate a buffer for directory file 1st cluster initialisation
    RBuf8 buf;
    CleanupClosePushL(buf);

    if(buf.CreateMax(KMaxBufSz) != KErrNone)
        buf.CreateMaxL(KMinBufSz); //-- OOM, try to create smaller buffer

    buf.FillZ();

    //-- copy "." directory entry to the buffer

    //-- "." directory entry
    TFatDirEntry entry;
    TTime now;
    now.UniversalTime();
    entry.SetTime(now, TimeOffset() );
    entry.SetAttributes(KEntryAttDir);
    entry.SetCurrentDirectory();
    entry.SetStartCluster(aStartCluster);
    Mem::Copy(&buf[0],&entry,KSizeOfFatDirEntry);

    //-- append ".." directory entry
    entry.SetParentDirectory();
    entry.SetStartCluster(aParentCluster==RootIndicator() ? 0 : aParentCluster);
    Mem::Copy(&buf[0]+KSizeOfFatDirEntry,&entry,KSizeOfFatDirEntry);

    TEntryPos entryPos(aStartCluster,0);

    //-- write buffer to the beginning of the directory file.
    DirWriteL(entryPos, buf); //-- use special interface to access FAT directory file

    //-- fill in the rest of the cluster if we used a small buffer
    if((TUint32)buf.Size() < KClusterSz) //-- use special interface to access FAT directory file
    {
        buf.FillZ();
        const TInt restCnt = SectorsPerCluster() - 1;
        ASSERT(restCnt >=1);

        for(TInt i=0; i<restCnt; ++i)
        {
            entryPos.iPos += KMinBufSz;
            DirWriteL(entryPos, buf); //-- use directory cache when dealing with directories
        }

    }

    CleanupStack::PopAndDestroy(&buf);

    }

//-----------------------------------------------------------------------------------------

/**
    Remove a directory.
    @param aName directory name
    all trailing dots from the name will be removed
*/
void CFatMountCB::RmDirL(const TDesC& aName)
    {
    __PRINT2(_L("CFatMountCB::RmDirL, drv:%d, %S"), DriveNumber(), &aName);

    CheckStateConsistentL();
    CheckWritableL();

    TPtrC dirName = RemoveTrailingDots(aName); //-- remove trailing dots from the name

    TFatDirEntry dirEntry;
    TEntryPos dirEntryPos(RootIndicator(),0); // Already checked entry is a directory
    FindEntryStartL(dirName,KEntryAttMatchMask|KEntryAttMatchExclusive,dirEntry,dirEntryPos);
    TEntryPos dosEntryPos=dirEntryPos;
    TFatDirEntry dosEntry=dirEntry;
    MoveToDosEntryL(dosEntryPos,dosEntry);
    if (!IsDirectoryEmptyL(StartCluster(dosEntry)))
        User::Leave(KErrInUse);
    // Remove the directory from cache before erasing
    if(iLeafDirCache && iLeafDirCache->CacheCount() > 0)
        {
        iLeafDirCache->RemoveDirL(StartCluster(dosEntry));
        }

    EraseDirEntryL(dirEntryPos,dirEntry);
    FAT().FreeClusterListL(StartCluster(dosEntry));
    FAT().FlushL();
    }

//-----------------------------------------------------------------------------------------

/**
    Delete a file
    @param aName file name
    all trailing dots from the name will be removed
*/
void CFatMountCB::DeleteL(const TDesC& aName)
    {
    __PRINT2(_L("CFatMountCB::DeleteL, drv:%d, %S"), DriveNumber(), &aName);

    CheckStateConsistentL();
    CheckWritableL();

    TPtrC fullName = RemoveTrailingDots(aName); //-- remove trailing dots from the name

    TFatDirEntry firstEntry;
    TEntryPos firstEntryPos(RootIndicator(),0);
    FindEntryStartL(fullName,KEntryAttMaskSupported,firstEntry,firstEntryPos);
    TEntryPos dosEntryPos=firstEntryPos;
    TFatDirEntry dosEntry=firstEntry;
    MoveToDosEntryL(dosEntryPos,dosEntry);
    if ((dosEntry.Attributes()&KEntryAttReadOnly) || (dosEntry.Attributes()&KEntryAttDir))
        User::Leave(KErrAccessDenied);
    // Can not delete a file if it is clamped
    CMountCB* basePtr=(CMountCB*)this;
    TInt startCluster=StartCluster(dosEntry);
    if(basePtr->IsFileClamped(MAKE_TINT64(0,startCluster)) > 0)
        User::Leave(KErrInUse);
    EraseDirEntryL(firstEntryPos,firstEntry);
    FAT().FreeClusterListL(StartCluster(dosEntry));
    FAT().FlushL();
    }

//-----------------------------------------------------------------------------------------

/**

    Rename or replace a directory entry.
    Assumes all files are closed and replace is only passed files.
    Assumes rename target does not exist or is the source file.

    --------------- operating mode --------------------------------------------

    * rename mode

    aOldName exists  |  aNewName exists |   result
        N                    N              leave KErrNotFound
        N                    Y              leave KErrNotFound
        Y                    N              rename aOldName -> aNewName
        Y                    Y              leave KErrAlreadyExists if(aOldName!=aNewName); otherwise do nothing

    * replace mode

        N                    N              leave KErrNotFound
        N                    Y              leave KErrNotFound
        Y                    N              rename aOldName -> aNewName
        Y                    Y              contents and all file attributes of the "aNewName" are replaced with aOldName's. "aOldName" entries are deleted then.


    @param   aOldName           entry name to be renamed or replaced
    @param   aNewName           a new entry name
    @param   aMode              specifies renaming / replacing
    @param   aNewDosEntryPos    on exit contains new entry Pos.
*/
void CFatMountCB::DoRenameOrReplaceL(const TDesC& aOldName, const TDesC& aNewName, TRenMode aMode, TEntryPos& aNewName_DosEntryPos)
    {
    __PRINT3(_L("CFatMountCB::DoRenameOrReplaceL() mode:%d old:%S, new:%S"), aMode, &aOldName, &aNewName);

    const TBool namesAreIdentical = FileNamesIdentical(aOldName, aNewName); //-- this is case-insensitive.
    const TBool renameMode = (aMode == EModeRename);
    const TBool replaceMode = !renameMode;
    TInt  nRes;

    if(namesAreIdentical && replaceMode)
        return; //-- nothing to do, replacing file with itself

    //---------------------------------------------------------------------------------------------------------------------------
    //-- 1. find the entries of 'aOldName' file. It must always succeed, because FileServer firstly tries to locate 'aOldName'

    TFatDirEntry oldName_FirstEntry; //-- first entry of the "aOldName" entryset
    TEntryPos    oldName_FirstEntryPos(RootIndicator(), 0); //-- dir. pos of the start "aOldName" VFAT entry set

    FindEntryStartL(aOldName, KEntryAttMaskSupported, oldName_FirstEntry, oldName_FirstEntryPos);

    TFatDirEntry oldName_DosEntry    = oldName_FirstEntry;   //-- "aOldName" entry set DOS entry
    TEntryPos    oldName_DosEntryPos = oldName_FirstEntryPos;//-- dir. pos of the "aOldName" DOS entry

    MoveToDosEntryL(oldName_DosEntryPos, oldName_DosEntry);

    const TBool bOldNameIsVFAT = !(oldName_DosEntryPos == oldName_FirstEntryPos); //-- ETrue if "aOldName" is VFAT name, i.e. consists of mode than 1 entry

    //-- check if the file "aOldName" is clamped. In this case it can't be replaced.
    if(replaceMode && (IsFileClamped(StartCluster(oldName_DosEntry)) > 0))
        User::Leave(KErrInUse);

    //---------------------------------------------------------------------------------------------------------------------------
    //-- 2. find the entry of 'aNewName' file. Further behavior depends on rename/replace mode and if this file exists or not

    //-- extract new file name from the full path
    TPtrC ptrNewName;
    TPtrC ptrNewNameParentDir;
    const TInt delimPos = aNewName.LocateReverse(KPathDelimiter);
    ptrNewName.Set(aNewName.Mid(delimPos+1));
    ptrNewNameParentDir.Set(aNewName.Left(delimPos+1));

    //-- find the parent directory of the "aNewName" and create iterator for it
    TLeafDirData leafDir;
    const TEntryPos aNewName_ParentDirPos = TEntryPos(FindLeafDirL(ptrNewNameParentDir, leafDir), 0); //-- 'aNewName' parent directory iterator
    aNewName_DosEntryPos = aNewName_ParentDirPos;

    TEntryPos    newName_VFatEntryPos; //-- dir. pos of the start "aNewName" VFAT entry set
    TFatDirEntry newName_DosEntry;

    TFileName fileName;
    iFileCreationHelper.InitialiseL(ptrNewName);
    TFatDirEntry startEntry;

    TRAP(nRes, DoFindL(ptrNewName, KEntryAttMaskSupported,
                newName_VFatEntryPos, startEntry, aNewName_DosEntryPos, newName_DosEntry,
                fileName, KErrNotFound,
                &iFileCreationHelper,
                leafDir));

    if (nRes!=KErrNone && nRes!=KErrNotFound)
        User::Leave(nRes);

    const TBool newFileExists = (nRes == KErrNone); //-- ETrue if 'aNewName' file exists.
    const TBool bNewNameIsVFAT = !IsLegalDosName(ptrNewName, EFalse, EFalse, EFalse, EFalse, ETrue);

    if(renameMode && newFileExists)
        {
        if(!namesAreIdentical)
        {
        if ((newName_DosEntry.Attributes()&KEntryAttDir) != (oldName_DosEntry.Attributes()&KEntryAttDir))
            {
            User::Leave(KErrAccessDenied);  //-- leave with KErrAccessDenied if it is trying to rename a file
                                            //      to a dir or vice versa.
            }
        User::Leave(KErrAlreadyExists); //-- can't rename file if the file with 'aNewName' already exists
        }
        else
            {
            if(!bNewNameIsVFAT && !bOldNameIsVFAT)
                return; //-- renaming DOS name to itself
            }
        //-- allow renaming entry to itself. "namesAreIdentical" is case-insensitive. use case: "FILE" -> "File"
        }

    //---------------------------------------------------------------------------------------------------------------------------

    if(replaceMode && newFileExists)
        {
        //---------------------------------------------------------------------------------------------------------------------------
        //-- replace contents of the 'aNewName' with 'aOldName' and remove 'aOldName' entries.

        //-- check if we are still trying to replace the file with itself, probably using short name alias
        if(aNewName_DosEntryPos == oldName_DosEntryPos)
            return; //-- nothing to do, it's the same file

        const TInt oldNameStartCluster = StartCluster(oldName_DosEntry);
        const TInt newNameStartCluster = StartCluster(newName_DosEntry); //-- store starting cluster of the chain to be unlinked

        newName_DosEntry.SetStartCluster(oldNameStartCluster);
        newName_DosEntry.SetSize(oldName_DosEntry.Size());
        newName_DosEntry.SetTime(oldName_DosEntry.Time(TTimeIntervalSeconds(0)), TTimeIntervalSeconds(0));
        newName_DosEntry.SetAttributes(oldName_DosEntry.Attributes());

            if(IsRuggedFSys())
                {
                //-- Note 1.
                //-- set a special Id in reserved section for old and new entries.
                //-- if write fails before the old entry gets erased, we will have 2 entries pointing to the same clusterchain.
                //-- ScanDrive is responsible for fixing this situation by erasing entry with ID KReservedIdOldEntry.
                //-- note that  SetRuggedFatEntryId() uses "LastAccessTime" DOS FAT entry field to store the ID.
                //-- in normal situation this field isn't used, though Windows checkdisk can chack its validiy.
                //-- KReservedIdNewEntry == 0x0000 that corresponds to year 1980.

                newName_DosEntry.SetRuggedFatEntryId(KReservedIdNewEntry);
                oldName_DosEntry.SetRuggedFatEntryId(KReservedIdOldEntry);
                WriteDirEntryL(oldName_DosEntryPos, oldName_DosEntry);
                }

        //-- write 'aNewName' DOS dir. entry data back
        WriteDirEntryL(aNewName_DosEntryPos, newName_DosEntry);

        //-- erase "oldName" entryset.
        EraseDirEntryL(oldName_FirstEntryPos, oldName_FirstEntry);

        //-- free 'aNewName' cluster list
        FAT().FreeClusterListL(newNameStartCluster);

        if(IsRuggedFSys())
            FAT().FlushL();

        }
    else //if(replaceMode && newFileExists)
        {
        //---------------------------------------------------------------------------------------------------------------------------
        //-- Renaming 'aOldName' to 'aNewName': add 'aNewName' entry set and remove 'aOldName' entryset

        TFatDirEntry newDosEntry = oldName_DosEntry;
        //-- generate short name for the 'aNewName' entryset and make new DOS entry
        if(bNewNameIsVFAT)
            {//-- need to generate a short name for VFAT entryset DOS entry
            TShortName shortName;

            if (iFileCreationHelper.GetValidatedShortName(shortName) == KErrNotFound)
                {
                GenerateShortNameL(aNewName_DosEntryPos.Cluster(), ptrNewName, shortName, ETrue);
                }

            newDosEntry.SetName(shortName);
            }
        else
            {//-- just use 'aNewName' as DOS name.
            TBuf8<KFatDirNameSize+1> tmp; //-- the name may be "XXXXXXXX.YYY"
            tmp.Copy(ptrNewName);
            newDosEntry.SetName(DosNameToStdFormat(tmp));
            }

        if(IsRuggedFSys())
            {//-- the the note(1) above
            newDosEntry.SetRuggedFatEntryId(KReservedIdNewEntry);
            oldName_DosEntry.SetRuggedFatEntryId(KReservedIdOldEntry);
            WriteDirEntryL(oldName_DosEntryPos, oldName_DosEntry);
            }

        //-- add new entryset to the directory
        aNewName_DosEntryPos.iPos = 0;
        aNewName_DosEntryPos.iCluster = aNewName_ParentDirPos.Cluster();

        if (iFileCreationHelper.IsNewEntryPosFound())
            {
            aNewName_DosEntryPos = iFileCreationHelper.EntryAddingPos();
            }

        if(bNewNameIsVFAT)
            {
            const TInt numEntries = NumberOfVFatEntries(ptrNewName.Length());
            AddDirEntryL(aNewName_DosEntryPos, numEntries);
            WriteDirEntryL(aNewName_DosEntryPos, newDosEntry, ptrNewName);
            }
        else
            {//-- new name is one DOS entry only
            AddDirEntryL(aNewName_DosEntryPos, 1);
            WriteDirEntryL(aNewName_DosEntryPos, newDosEntry);
            }

        //-- erase old entryset.
        EraseDirEntryL(oldName_FirstEntryPos, oldName_FirstEntry);

        //-- if we have renamed (moved) a directory, need to update its pointer to parent directory ('..' entry)
        if((newDosEntry.Attributes() & KEntryAttDir))
            {
            TEntryPos parentPtrEntPos(StartCluster(newDosEntry), 1*KSizeOfFatDirEntry);

            TFatDirEntry chFatEnt;
            ReadDirEntryL(parentPtrEntPos, chFatEnt);

            const TUint parentDirStartCluster_Old = StartCluster(chFatEnt);
                  TUint parentDirStartCluster_New = aNewName_ParentDirPos.Cluster();

            if(parentDirStartCluster_New == RootClusterNum() && parentDirStartCluster_New != 0)
                {//-- we are in the root directory. for some reason, '..' entries of the directories in the root dir.
                //-- must have starting cluster 0
                parentDirStartCluster_New = 0;
                }

            if(parentDirStartCluster_Old != parentDirStartCluster_New)
                {
                chFatEnt.SetStartCluster(parentDirStartCluster_New);
                WriteDirEntryL(parentPtrEntPos, chFatEnt);
                }
            // Invalidate leaf dir cache as it is hard to track the dir structure changes now
            if (iLeafDirCache)
                {
                iLeafDirCache->Reset();
                }
            }
        }//else if(replaceMode && newFileExists)

    iFileCreationHelper.Close();
    }

//-----------------------------------------------------------------------------------------

/**
    Rename 'aOldName' file/directory to 'aNewName'
    all trailing dots from the names will be removed

    @param  aOldName        existing object name
    @param  aNewName        new object name
*/
void CFatMountCB::RenameL(const TDesC& aOldName, const TDesC& aNewName)
    {
    __PRINT3(_L("CFatMountCB::RenameL, drv:%d, %S %S"), DriveNumber(), &aOldName, &aNewName);

    CheckStateConsistentL();
    CheckWritableL();

    TEntryPos newEntryPos;
    DoRenameOrReplaceL(RemoveTrailingDots(aOldName), RemoveTrailingDots(aNewName) ,EModeRename, newEntryPos);

    if(!IsRuggedFSys())
        FAT().FlushL();
    }

//-----------------------------------------------------------------------------------------

/**
    Replace contents of the 'aNewName' with the contents of 'aOldName'
    all trailing dots from the names will be removed

    @param  aOldName        existing object name
    @param  aNewName        new object name
*/
void CFatMountCB::ReplaceL(const TDesC& aOldName,const TDesC& aNewName)
    {

    __PRINT3(_L("CFatMountCB::ReplaceL, drv:%d, %S %S"), DriveNumber(), &aOldName, &aNewName);

    CheckStateConsistentL();
    CheckWritableL();

    TEntryPos newEntryPos;
    DoRenameOrReplaceL(RemoveTrailingDots(aOldName), RemoveTrailingDots(aNewName), EModeReplace, newEntryPos);
    if(!IsRuggedFSys())
        FAT().FlushL();
    }

//-----------------------------------------------------------------------------------------


/**
    Try to find a directory entry by the given name and path.
    This method _must_ leave if the entry is not found. See the caller.

    @param  aName   path to the directory object. all trailing dots from the name will be removed.
    @param  anEntry on return will contain the entry data

    @leave  KErrPathNotFound if there is no path to the aName
            KErrNotFound     if the entry corresponding to the aName is not found
            system-wide erorr code of media read failure.
*/
void CFatMountCB::EntryL(const TDesC& aName,TEntry& anEntry) const
    {
    __PRINT2(_L("CFatMountCB::EntryL, drv:%d, %S"), DriveNumber(), &aName);

    CheckStateConsistentL();

    TEntryPos entryPos(RootIndicator(),0);
    TFatDirEntry entry;
    TPtr fileName(anEntry.iName.Des());

    TPtrC fullName = RemoveTrailingDots(aName);
    TInt namePos=fullName.LocateReverse(KPathDelimiter)+1; // There is always a path delimiter
    TLeafDirData leafDir;
    entryPos.iCluster=FindLeafDirL(fullName.Left(namePos), leafDir);
    entryPos.iPos=0;
    TEntryPos startPos;
    TFatDirEntry startEntry;

    DoFindL(fullName.Mid(namePos),KEntryAttMaskSupported,
            startPos,startEntry,entryPos,entry,
            fileName,KErrNotFound,
            NULL,
            leafDir);


    anEntry.iAtt=entry.Attributes();
    anEntry.iSize=entry.Size();
    anEntry.iModified=entry.Time(TimeOffset());

    if (fileName.Length()==0)
        {
        TBuf8<0x20> dosName(DosNameFromStdFormat(entry.Name()));
        LocaleUtils::ConvertToUnicodeL(fileName,dosName);
        }
    if ((TUint)anEntry.iSize>=sizeof(TCheckedUid))
        ReadUidL(StartCluster(entry),anEntry);
    }

//-----------------------------------------------------------------------------------------

/**
    Set directory entry details.
    @param  aName           entry name; all trailing dots from the name will be removed
    @param  aTime           entry modification time (and last access as well)
    @param  aSetAttMask     entry attributes OR mask
    @param  aClearAttMask   entry attributes AND mask

*/
void CFatMountCB::SetEntryL(const TDesC& aName,const TTime& aTime,TUint aSetAttMask,TUint aClearAttMask)
    {
    __PRINT2(_L("CFatMountCB::SetEntryL, drv:%d, %S"), DriveNumber(), &aName);

    CheckStateConsistentL();
    CheckWritableL();

    TEntryPos firstEntryPos(RootIndicator(),0);
    TFatDirEntry firstEntry;
    FindEntryStartL(RemoveTrailingDots(aName),KEntryAttMaskSupported,firstEntry,firstEntryPos);
    MoveToDosEntryL(firstEntryPos,firstEntry);
    TUint setAttMask=aSetAttMask&KEntryAttMaskSupported;
    if (setAttMask|aClearAttMask)
        {
        TInt att=firstEntry.Attributes();
        att|=setAttMask;
        att&=(~aClearAttMask);
        firstEntry.SetAttributes(att);
        }
    if (aSetAttMask&KEntryAttModified)
        {
        firstEntry.SetTime(aTime,TimeOffset());
        }
    WriteDirEntryL(firstEntryPos,firstEntry);
    }

//-----------------------------------------------------------------------------------------

void CFatMountCB::DoCheckFatForLoopsL(TInt aCluster, TInt& aPreviousCluster, TInt& aChangePreviousCluster, TInt& aCount) const
//
// Check one fat cluster for loops.
//
    {

    if (aCluster==aPreviousCluster)
        User::Leave(KErrCorrupt); // Found loop

    aCount++;
    if (aCount==aChangePreviousCluster)
        {
        aCount=0;
        aChangePreviousCluster<<=1;
        aPreviousCluster=aCluster;
        }
    }

//-----------------------------------------------------------------------------------------

void CFatMountCB::CheckFatForLoopsL(const TFatDirEntry& anEntry) const
//
// Check for loops
//
    {

    TInt cluster=StartCluster(anEntry);
    if (cluster==0 && anEntry.Size()==0)
        return;

    TInt previousCluster=cluster;
    TInt changePreviousCluster=1;
    TInt count=0;


    for(;;)
        {
        if ((TUint)cluster < KFatFirstSearchCluster || (!IsEndOfClusterCh(cluster) && (TUint)cluster>MaxClusterNumber()))
            User::Leave(KErrCorrupt);

         if(!FAT().GetNextClusterL(cluster))
            break;

         DoCheckFatForLoopsL(cluster, previousCluster, changePreviousCluster, count);
        }

    }

//-----------------------------------------------------------------------------------------

/**
    Open/Create/Replace a file on the current mount.

    @param  aName   file name; all trailing dots from the name will be removed
    @param  aMode   File open mode, See TFileMode
    @param  anOpen  specifies action: open, create or replace the file
    @param  aFile   pointer to the CFileCB object to populate

*/
void CFatMountCB::FileOpenL(const TDesC& aName,TUint aMode,TFileOpen anOpen,CFileCB* aFile)
    {
    __PRINT3(_L("CFatMountCB::FileOpenL, drv:%d, mode:%d, name:%S"), DriveNumber(), anOpen, &aName);

    CheckStateConsistentL();

    TPtrC fullName = RemoveTrailingDots(aName); //-- remove trailing dots from the name

    TFatDirEntry firstEntry;
    TEntryPos firstEntryPos(RootIndicator(),0);
    TInt nPos=fullName.LocateReverse(KPathDelimiter)+1; // There is always a path delimiter
    TPtrC name(fullName.Mid(nPos));
    TInt ret = KErrNone;

    iFileCreationHelper.Close();
    if (anOpen == EFileCreate || anOpen == EFileReplace)
        {
        iFileCreationHelper.InitialiseL(name);
        TRAP(ret,FindEntryStartL(fullName,KEntryAttMaskSupported,firstEntry,firstEntryPos,&iFileCreationHelper));
        }
    else
        {
        TRAP(ret,FindEntryStartL(fullName,KEntryAttMaskSupported,firstEntry,firstEntryPos));
        }

    if (ret!=KErrNone && ret!=KErrNotFound)
        User::Leave(ret);

    if (ret==KErrNone)
        {
        MoveToDosEntryL(firstEntryPos,firstEntry);
        if ((firstEntry.Attributes()&KEntryAttDir) || (firstEntry.Attributes()&KEntryAttVolume))
            User::Leave(KErrAccessDenied);
        if (anOpen==EFileCreate)
            User::Leave(KErrAlreadyExists);
        if ((firstEntry.Attributes()&KEntryAttReadOnly) && aMode&EFileWrite)
            User::Leave(KErrAccessDenied);
        if((aMode & EFileWrite) && (IsFileClamped(StartCluster(firstEntry))>0))
            User::Leave(KErrInUse);
        CheckFatForLoopsL(firstEntry);
        }
    else
        {
        if (anOpen==EFileOpen)
            User::Leave(KErrNotFound);

        //-- here we try to either create or replace file
        CheckWritableL();

        TLeafDirData leafDir;

        TInt numEntries = iFileCreationHelper.NumOfAddingEntries();
        TShortName shortName;
        if (iFileCreationHelper.GetValidatedShortName(shortName) == KErrNotFound)
            {
            firstEntryPos.iCluster=FindLeafDirL(fullName.Left(nPos), leafDir);
            GenerateShortNameL(firstEntryPos.iCluster,name,shortName,ETrue);
            }

        if (iFileCreationHelper.IsNewEntryPosFound())
            {
            firstEntryPos = iFileCreationHelper.EntryAddingPos();
            }
        else
            {
            firstEntryPos.iCluster=FindLeafDirL(fullName.Left(nPos), leafDir);
            firstEntryPos.iPos=0;
            }

        AddDirEntryL(firstEntryPos,numEntries);
        firstEntry.InitZ();
        firstEntry.SetName(shortName);
        firstEntry.SetStartCluster(0);

        TTime now;
        now.UniversalTime();
        firstEntry.SetCreateTime(now, TimeOffset() );

        if (iFileCreationHelper.IsTrgNameLegalDosName())
            WriteDirEntryL(firstEntryPos,firstEntry);
        else
            WriteDirEntryL(firstEntryPos,firstEntry,name);
        }

    CFatFileCB& file=(*((CFatFileCB*)aFile));
    file.SetL(firstEntry,(TShare)(aMode&KFileShareMask),firstEntryPos);
    if (anOpen==EFileReplace && file.Size())
        {
        file.SetSizeL(0);
        file.SetSize(0);
        }
    if (file.IsSeekIndex()==EFalse)
        file.CreateSeekIndex();
    if (anOpen==EFileReplace || anOpen==EFileCreate)
        file.SetArchiveAttribute();

    if(!IsRuggedFSys())
        FAT().FlushL();

    iFileCreationHelper.Close();
    }

//-----------------------------------------------------------------------------------------


/**
    Open a directory on the current mount.

    @param  aName   path to the object in the directory we want to open; all trailing dots from the name will be removed
    @param  aDir    dir. CB to be filled in.

    If there is no such a path, this method must leave with KErrPathNotFound

    @leave  KErrPathNotFound if thereis no such path
    @leave  error code on media read fault
*/
void CFatMountCB::DirOpenL(const TDesC& aName,CDirCB* aDir)
    {
    __PRINT2(_L("CFatMountCB::DirOpenL, drv:%d, %S"), DriveNumber(), &aName);

    CheckStateConsistentL();

    const TPtrC dirName = RemoveTrailingDots(aName); //-- remove trailing dots from the name

    TInt namePos=dirName.LocateReverse(KPathDelimiter);

    TFatDirEntry dosEntry;
    TEntryPos dosEntryPos(RootIndicator(),0);
    if (namePos==0)
        InitializeRootEntry(dosEntry);
    else
        {
        TPtrC dirPath=dirName.Left(namePos);
        TInt dirPos=dirPath.LocateReverse(KPathDelimiter)+1;
        TLeafDirData leafDir;
        dosEntryPos.iCluster=FindLeafDirL(dirPath.Left(dirPos), leafDir); // Find directory before leaf
        dosEntryPos.iPos=0;

        TFileName fileName;
        TEntryPos startPos;
        TFatDirEntry startEntry;
        DoFindL(dirPath.Mid(dirPos),
                KEntryAttMatchMask|KEntryAttMatchExclusive,
                startPos, startEntry, dosEntryPos, dosEntry,
                fileName, KErrPathNotFound,
                NULL,
                leafDir);


        }

    TPtrC matchName(dirName.Mid(namePos+1));
    if (matchName.Length()==0)
        matchName.Set(_L("*"));

    ((CFatDirCB*)aDir)->SetDirL(dosEntry,matchName);

    }

//-----------------------------------------------------------------------------------------

TBool CFatMountCB::IsDirectoryEmptyL(TInt aCluster)
//
// Check aCluster contains no directory entries other than . and ..
//
    {

    __PRINT(_L("CFatMountCB::IsDirectoryEmptyL"));
    TEntryPos dirEntryPos(aCluster,0);
    TFatDirEntry dirEntry;
    FOREVER
        {
        ReadDirEntryL(dirEntryPos,dirEntry);
        MoveToDosEntryL(dirEntryPos,dirEntry);
        if (dirEntry.IsParentDirectory() || dirEntry.IsCurrentDirectory())
            goto LoopEnd;
        if (dirEntry.IsEndOfDirectory())
            return ETrue;
        if (IsRootDir(dirEntryPos)&&(dirEntryPos.iPos+StartOfRootDirInBytes()==RootDirEnd()))
            return ETrue;   //  Root Directory has no end of directory marker
        if (!dirEntry.IsErased())
            return EFalse;
LoopEnd:
        MoveToNextEntryL(dirEntryPos);
        }
    }

//-----------------------------------------------------------------------------------------

/**
    Overwrite as many contiguous file clusters as possible.
*/
void CFatMountCB::DoWriteToClusterListL(TEntryPos& aPos,TInt aLength,const TAny* aSrc,const RMessagePtr2& aMessage,TInt anOffset, TInt aLastcluster, TInt &aBadcluster, TInt &aGoodcluster)
    {

    __PRINT(_L("CFatMountCB::DoWriteToClusterListL"));
    __ASSERT_ALWAYS(aPos.Cluster()>=KFatFirstSearchCluster,User::Leave(KErrCorrupt));

    TInt endCluster=0;

    const TInt clusterRelativePos=ClusterRelativePos(aPos.iPos);
    const TInt maxClusters=((aLength+clusterRelativePos-1)>>ClusterSizeLog2())+1;
    const TInt clusterListLen=FAT().CountContiguousClustersL(aPos.iCluster,endCluster,maxClusters);
    const TInt writeLength=Min(aLength,(clusterListLen<<ClusterSizeLog2())-clusterRelativePos);
    TInt64 dataStart=FAT().DataPositionInBytes(aPos.iCluster)+clusterRelativePos;

    TRAPD(r, iRawDisk->WriteL(dataStart,writeLength,aSrc,aMessage,anOffset));

    if(r == KErrNone) // Write succeded
        {
        aPos.iPos+=writeLength;
        aPos.iCluster=endCluster;
        return;
        }

    if(r != KErrCorrupt) // failure not due to corruption so propogate up
        User::Leave(r);

    TErrorInfoBuf errinf;
    r = iRawDisk->GetLastErrorInfo(errinf);

    if(r == KErrNone && errinf().iReasonCode == TErrorInfo::EBadSector) // GetLastErrorInfo succeded and Last Error was caused by bad sector
        {

        const TInt badcluster = (TInt)(((dataStart + errinf().iErrorPos) - ClusterBasePosition())>>ClusterSizeLog2())+KFatFirstSearchCluster;
              TInt goodcluster = FAT().AllocateSingleClusterL(badcluster);

        //Calculate cluster number to check whether this write started at the beginning of new cluster or middle of previous cluster.
        TInt cluster = aPos.iCluster;
        if ( (aPos.iPos) && ((aPos.iPos)==((aPos.iPos >> ClusterSizeLog2())<<ClusterSizeLog2())))
            cluster--;

        if((aPos.iPos != 0) && (badcluster == aPos.iCluster) && (aLastcluster == 0) && (aPos.iCluster == cluster))
            { //Copy the contents already present in this cluster to new cluster allocated.
            const TInt sizeToRead = aPos.iPos - ((aPos.iPos >> ClusterSizeLog2()) << ClusterSizeLog2());
            dataStart = FAT().DataPositionInBytes(aPos.iCluster) + ClusterRelativePos((aPos.iPos - sizeToRead));


            //-- Allocate the buffer required to copy the contents from bad cluster
            RBuf8 clustBuf;
            CleanupClosePushL(clustBuf);
            if(clustBuf.CreateMax(sizeToRead) != KErrNone)
                {
                FAT().FreeClusterListL(goodcluster);
                User::Leave(KErrNoMemory);
                }

            r = LocalDrive()->Read(dataStart, sizeToRead, clustBuf); //Read the contents into buffer
            if(r != KErrNone) //If read fails dont do anything not even marking bad cluster.
                {
                FAT().FreeClusterListL(goodcluster);
                User::Leave(r);
                }

            //Copy the bad and good cluster,required to adjust the start cluster number.
            if(aBadcluster == 0)
                aBadcluster = badcluster;

            aGoodcluster = goodcluster;

            FOREVER
                {
                //Calculate and copy the contents to new cluster.
                aPos.iCluster = goodcluster;
                dataStart = FAT().DataPositionInBytes(aPos.iCluster) + ClusterRelativePos(aPos.iPos - sizeToRead);

                r = LocalDrive()->Write(dataStart, clustBuf);
                if(r == KErrNone)
                    { // Copied contents to new cluster so fix up the chain and mark the cluster as bad.
                    FAT().WriteL(goodcluster, FAT().ReadL(badcluster));
                    FAT().MarkAsBadClusterL(badcluster);
                    aGoodcluster = goodcluster;
                    CleanupStack::PopAndDestroy(&clustBuf); //-- deallocate a cluster buffer
                    return;
                    }
                else if(r == KErrCorrupt)
                    {
                    r = LocalDrive()->GetLastErrorInfo(errinf);
                    if(r == KErrNone && errinf().iReasonCode == TErrorInfo::EBadSector)
                        { //Allocate new cluster and adjust the cluster list.
                        goodcluster = FAT().AllocateSingleClusterL(aPos.iCluster);
                        FAT().MarkAsBadClusterL(aPos.iCluster);
                        continue;
                        }
                        r = KErrCorrupt;
                    }
                    //Not able to write successfully so dont alter the original list.
                    aBadcluster = aGoodcluster = 0;
                    FAT().FreeClusterListL(goodcluster);
                    User::Leave(r);
                }

        }//if((aPos.iPos != 0) && (badcluster == aPos.iCluster) && (aLastcluster == 0) && (aPos.iCluster == cluster))

        if((badcluster == aPos.iCluster) && (aLastcluster == 0)) //bad cluster at beginning of original clusterlist
            {
            // return bad and good clusters for CFatFileCB to fix up
            FAT().WriteL(goodcluster, FAT().ReadL(badcluster));
            aBadcluster = badcluster;
            aGoodcluster = goodcluster;
            aPos.iCluster = goodcluster;
            }
        else    //fix up chain
            {
            FAT().WriteL(goodcluster, FAT().ReadL(badcluster));
            if(badcluster > aPos.iCluster)  //bad cluster not first in this contiguous list
                FAT().WriteL(badcluster-1, goodcluster);
            else    //first cluster of this contigous list bad so update last cluster of previous contiguous list
                FAT().WriteL(aLastcluster, goodcluster);
            }

        FAT().MarkAsBadClusterL(badcluster);


        return;
        }
    User::Leave(KErrCorrupt);
    }

//-----------------------------------------------------------------------------------------

void CFatMountCB::WriteToClusterListL(TEntryPos& aPos,TInt aLength,const TAny* aSrc,const RMessagePtr2& aMessage,TInt anOffset, TInt &aBadcluster, TInt& aGoodcluster)
//
// Overwrite cluster list.
//
    {

    __PRINT(_L("CFatMountCB::WriteToClusterListL"));
    __ASSERT_ALWAYS(aPos.Cluster()>=KFatFirstSearchCluster,User::Leave(KErrCorrupt));

    const TUint startPos=aPos.iPos;
    const TUint temp=startPos>>ClusterSizeLog2();
    const TUint length = (TUint)aLength;

    if ( (startPos) && ((startPos)==(temp<<ClusterSizeLog2())) )
        {
        __ASSERT_ALWAYS(FAT().GetNextClusterL(aPos.iCluster),User::Leave(KErrCorrupt));
        }

    TUint offset=0;
    TInt previouscluster=0;
    FOREVER
        {
        DoWriteToClusterListL(aPos,length-offset,aSrc,aMessage,anOffset+offset, previouscluster, aBadcluster, aGoodcluster);
        if (offset == (aPos.iPos-startPos))
            continue;
        offset=aPos.iPos-startPos;
        __ASSERT_ALWAYS(aPos.iPos>startPos,User::Leave(KErrCorrupt));
        previouscluster=aPos.iCluster;
        if (offset<length)
            {__ASSERT_ALWAYS(FAT().GetNextClusterL(aPos.iCluster),User::Leave(KErrCorrupt));}
        if (offset>=length)
            return;
        }
    }

//-----------------------------------------------------------------------------------------

void CFatMountCB::DoReadFromClusterListL(TEntryPos& aPos,TInt aLength,const TAny* aTrg,const RMessagePtr2& aMessage,TInt anOffset) const
//
// Read from as many contiguous file clusters as possible
//
    {

    __PRINT(_L("CFatMountCB::DoReadFromClusterListL"));

    TInt endCluster=0;

    const TInt clusterRelativePos=ClusterRelativePos(aPos.iPos);
    const TInt maxClusters=((aLength+clusterRelativePos-1)>>ClusterSizeLog2())+1;
    const TInt clusterListLen=FAT().CountContiguousClustersL(aPos.iCluster,endCluster,maxClusters);
    const TInt readLength=Min(aLength,(clusterListLen<<ClusterSizeLog2())-clusterRelativePos);
    const TInt64 dataStart=FAT().DataPositionInBytes(aPos.iCluster)+clusterRelativePos;

    TRAPD(r, iRawDisk->ReadL(dataStart,readLength,aTrg,aMessage,anOffset));

    if(r == KErrNone) // Read succeded
        {
        aPos.iPos+=readLength;
        aPos.iCluster=endCluster;
        return;
        }
    if(r != KErrCorrupt) // failure not due to corruption so propogate up
        User::Leave(r);

    TErrorInfoBuf errinf;
    r = iRawDisk->GetLastErrorInfo(errinf);

    if(r == KErrNone && errinf().iReasonCode == TErrorInfo::EBadSector) // GetLastErrorInfo succeded and Last Error was caused by bad sector
        {
        TInt badcluster = (TInt)(((dataStart + errinf().iErrorPos) - ClusterBasePosition())>>ClusterSizeLog2())+KFatFirstSearchCluster;
        FAT().MarkAsBadClusterL(badcluster);
        }

    User::Leave(KErrCorrupt);
    }

//-----------------------------------------------------------------------------------------

void CFatMountCB::ReadFromClusterListL(TEntryPos& aPos,TInt aLength,const TAny* aTrg,const RMessagePtr2& aMessage,TInt anOffset) const
//
// Read from cluster list
//
    {

    __PRINT(_L("CFatMountCB::ReadFromClusterListL"));
    __ASSERT_ALWAYS(aPos.Cluster()>=KFatFirstSearchCluster,User::Leave(KErrCorrupt));

    const TInt startPos=aPos.iPos;
    const TInt temp=startPos>>ClusterSizeLog2();

    if ( (startPos) && ((startPos)==(temp<<ClusterSizeLog2())) )
        {
        __ASSERT_ALWAYS(FAT().GetNextClusterL(aPos.iCluster),User::Leave(KErrCorrupt));
        }

    TInt offset=0;
    FOREVER
        {
        DoReadFromClusterListL(aPos,aLength-offset,aTrg,aMessage,anOffset+offset);
        offset=aPos.iPos-startPos;
        if ((offset<aLength))
            {
            __ASSERT_ALWAYS(FAT().GetNextClusterL(aPos.iCluster),User::Leave(KErrCorrupt));
            }
        if (offset>=aLength)
            return;
        }
    }

//-----------------------------------------------------------------------------------------

TInt CFatMountCB::FindLeafDirL(const TDesC& aName, TLeafDirData& aLeafDir) const
//
// Navigate the path to find the leaf directory.
// Returns the startcluster of data for the directory found.
//
    {

    __PRINT(_L("CFatMountCB::FindLeafDirL"));

    TLex lex(aName);
    TInt r;
    TEntryPos entryPos(RootIndicator(),0);

    if (iLeafDirCache == NULL)
        {
        TInt leaflen=(iLastLeafDir) ? iLastLeafDir->Length() : 0;
        TInt namelen=aName.Length();
        if (leaflen>1 && namelen>=leaflen && *iLastLeafDir==aName.Left(leaflen))
            {
            if (leaflen==namelen)
                return(iLastLeafDirCluster);
            lex.Inc(leaflen-1);
            entryPos.iCluster=iLastLeafDirCluster;
            }
        }
    else
        {
        // Skip root directory
        if (iLeafDirCache->CacheCount() > 0 && aName.Length() > 1)
            {
            TInt err = iLeafDirCache->FindInCache(aName, aLeafDir);
            if (err == KErrNone)
                {
                ASSERT(aLeafDir.iClusterNum > 0);
                return aLeafDir.iClusterNum;
                }
            else if (err != KErrNotFound)
                {
                User::LeaveIfError(err);
                }
            }
        }

    FOREVER
        {
        lex.Inc(); // Skip path delimiter
        lex.Mark();
        r=lex.Remainder().Locate(KPathDelimiter);
        if (r==KErrNotFound)
            r=lex.Remainder().Length();
        if (r==0) // End of the path
            break;
        lex.Inc(r); // Set the token length
        TFatDirEntry entry;

        TFileName fileName;
        TEntryPos startPos;
        TFatDirEntry startEntry;
        DoFindL(lex.MarkedToken(),
                KEntryAttMatchMask|KEntryAttMatchExclusive,
                startPos, startEntry, entryPos, entry,
                fileName, KErrPathNotFound,
                NULL,
                aLeafDir);


        entryPos.iCluster=StartCluster(entry);
        entryPos.iPos=0;
        }

    if (iLeafDirCache == NULL)
        {
        AllocBufferL(((CFatMountCB*)this)->iLastLeafDir,aName);
        ((CFatMountCB*)this)->iLastLeafDirCluster=entryPos.iCluster;
        }
    else
        {
        if (aName.Length() > 1)
            {
            aLeafDir = TLeafDirData(entryPos.iCluster);
            iLeafDirCache->AddToCacheL(aName, aLeafDir);
            }
        }

    return entryPos.iCluster;
    }

//-----------------------------------------------------------------------------------------

/**
    Search for a specified name winthin directory cache
    Works similary to TBool CFatMountCB::DoFindL()

    @param  anAtt           attributes of the object to find
    @param  aStartEntryPos  on return in case of VFAT entry will contain start position of the VFAT dir. entry
    @param  aStartEntry     on return will contain first VFAT dir entry
    @param  aDosEntryPos    the search will start from this position of dir entry, on return it will contain result DOS entry position, last one for VFAT case
    @param  aDosEntry       on return will contain DOS dir entry (the last one for VFAT case)
    @param  aFileName       in the case of VFAT entry and on success here will be returned a long filename
    @param  aAuxParam       some parameters package
    @param  aFileCreationHelper       a helper package for file creations

    @return ETrue if the specified name is found in the cache. In this case aStartEntryPos, aStartEntry, aDosEntryPos, aDosEntry, aFileName will contain valid values
*/
TBool CFatMountCB::DoRummageDirCacheL(const TUint anAtt, TEntryPos& aStartEntryPos,  
                                        TFatDirEntry& aStartEntry,  TEntryPos& aDosEntryPos,
                                        TFatDirEntry& aDosEntry,    TDes& aFileName,
                                        const TFindHelper& aAuxParam,
                                        XFileCreationHelper* aFileCreationHelper,
                                        const TLeafDirData& aLeafDir) const
    {
    TBool bCacheMatchFound = EFalse;

    //-- get an interface to the Dir. cache
    MWTCacheInterface* pDirCache = iRawDisk->DirCacheInterface();
    ASSERT(pDirCache);
    if(!pDirCache)
        return EFalse;

    //-- save original values in order to restore them in the case of negative search results
    TEntryPos       StartEntryPos1(aStartEntryPos);
    TEntryPos       DosEntryPos1(aDosEntryPos);
    TFatDirEntry    StartEntry1(aStartEntry);
    TFatDirEntry    DosEntry1(aDosEntry);

    TInt64          nCachedLinPos;

    const TUint32 clSize = 1 << ClusterSizeLog2(); //-- media cluster size
    const TUint32 cacheSz = pDirCache->CacheSizeInBytes(); //-- cache size in bytes
    const TUint32 maxDirEntries = cacheSz >> KSizeOfFatDirEntryLog2;  //-- maximal number of dir entries that can be in the cache

    const TUint   pageSzLog2 = pDirCache->PageSizeInBytesLog2();
    TBool ScanMRUPageFirst  = EFalse;
    TBool MRUPageScanned    = EFalse;

    // if MRU pos is availale, start with MRU page
    if (aLeafDir.iMRUPos.Cluster())
        {
        ScanMRUPageFirst = ETrue;
        DosEntryPos1 = aLeafDir.iMRUPos;
        }

    TInt numFound = 0;
    TEntryPos startPos = DosEntryPos1;
    TInt clusterNum = DosEntryPos1.iCluster;

    for(TUint32 entryCnt=0; entryCnt < maxDirEntries; ++entryCnt)
        {//-- walk through directory cluster list. The loop is limited by maximal number of dir entries
         //-- that can be cached. Helps to avoid problems with infinite (looped) directories

        if (IsEndOfClusterCh(DosEntryPos1.iCluster))
            {
            // refer back to the last stored cluster position
            //  note aFileCreationHelper may not be initialised for file opening operations
            if (aFileCreationHelper && aFileCreationHelper->IsInitialised() && clusterNum != DosEntryPos1.iCluster)
                {
                TEntryPos dummyPos(clusterNum, clSize - KSizeOfFatDirEntry);
                aFileCreationHelper->SetEntryAddingPos(dummyPos);
                aFileCreationHelper->SetIsNewEntryPosFound(ETrue);
                }

            if (ScanMRUPageFirst && !MRUPageScanned)
                {
                DosEntryPos1 = aDosEntryPos;
                MRUPageScanned = ETrue;
                continue;
                }
            break; //-- this was the last cluster in this directory
            }

        const TUint32 pageStartPos = CalculatePageOffsetInCluster(DosEntryPos1.iPos, pageSzLog2);
        DosEntryPos1.iPos = pageStartPos;
        TBool   PassedPageBoundary = EFalse;

        const TInt64  entryLinPos = MakeLinAddrL(DosEntryPos1); //-- linear media position of the cluster for this directory
        const TUint32 cachePageSz = pDirCache->PosCached(entryLinPos, nCachedLinPos); //-- indicates if entryLinPos is cached
        if(cachePageSz)
            {//-- current page is in the directory cache
             //__PRINT2(_L("#-!! CFatMountCB::DoRummageDirCacheL() Searching cl:%d, lin Pos:%X"),DosEntryPos1.iCluster,(TUint32)entryLinPos);

            //-- search to the end of the cached page.
            // Note GetDirEntry() will read data beyond cache page boundary
            const TUint32 nEntries = (1 << pageSzLog2) >> KSizeOfFatDirEntryLog2;

            TInt nErr;
            //-- extract dir entries from the cached page and see if they match given name (aName)
            /// until it reaches the next page
            for(;;)
                {
                StartEntryPos1 = DosEntryPos1;
                TInt clSave = DosEntryPos1.iCluster; //-- need to save current cluster number because GetDirEntry() & MoveToNextEntryL() can change it

                //-- get directory entry from the cache. We know that the DosEntryPos1 is cached.
                nErr = GetDirEntry(DosEntryPos1, DosEntry1, StartEntry1, aFileName);
                if(nErr != KErrNone)
                    break;

                if(DosEntry1.IsEndOfDirectory())
                    {
                    if (aFileCreationHelper && aFileCreationHelper->IsInitialised() && !aFileCreationHelper->IsNewEntryPosFound())
                        {
                        // note it is impossible to be at the end of the cluster chain here.
                        aFileCreationHelper->SetEntryAddingPos(DosEntryPos1);
                        aFileCreationHelper->SetIsNewEntryPosFound(ETrue);
                        }

                    if (ScanMRUPageFirst && !MRUPageScanned)
                        {
                        break;
                        }

                    // if (!ScanMRUPageFirst || ScanMRUPageFirst && MRUPageScanned)
                    goto Exit; //-- this was the last entry in this directory, no reason to look further
                    }

                if (aFileCreationHelper && aFileCreationHelper->IsInitialised() && !aFileCreationHelper->IsNewEntryPosFound())
                    {
                    if (!DosEntry1.IsErased() && !DosEntry1.IsGarbage())
                        {
                        numFound = 0;
                        }
                    else
                        {
                        if (numFound == 0)
                            {
                            startPos = DosEntryPos1;
                            }
                        numFound++;
                        if (numFound == aFileCreationHelper->NumOfAddingEntries())
                            {
                            aFileCreationHelper->SetEntryAddingPos(startPos);
                            aFileCreationHelper->SetIsNewEntryPosFound(ETrue);
                            }
                        }
                    }
                if(MatchEntryAtt(DosEntry1.Attributes(),anAtt))
                    {//-- FAT or VFAT dir entry is extracted and attributes match. Compare names then.

                    if(StartEntry1.IsVFatEntry())
                        {//-- extracted entry is VFAT one, name can be in UNICODE

                        // we only check short name candidates for long file names with VFAT entries,
                        //  if it is a valid dos name, it will be checked by default
                        // note here target name is always fully specified
                        if (aFileCreationHelper && aFileCreationHelper->IsInitialised())
                            {
                            aFileCreationHelper->CheckShortNameCandidates(DosEntry1.Name().Ptr());
                            }

                        TPtrC ptrAssembledName = RemoveTrailingDots(aFileName);

                        if(ptrAssembledName.MatchF(aAuxParam.iTargetName) != KErrNotFound)
                            {//-- found match in cache
                            bCacheMatchFound = ETrue;
                            goto Exit;
                            }
                        else if(aAuxParam.TrgtNameIsLegalDos())
                            {
                            if(aAuxParam.MatchDosEntryName(DosEntry1.Name().Ptr()))
                                {
                                bCacheMatchFound = ETrue;
                                goto Exit;
                                }
                            }
                        }//if(StartEntry1.IsVFatEntry())
                    else if(aAuxParam.TrgtNameIsLegalDos())
                        {//-- this is an old DOS FAT entry

                          if(aAuxParam.MatchDosEntryName(DosEntry1.Name().Ptr()))
                            {
                            //-- Here is the trick that helps with the situation when VFAT entry is split into 2 halves
                            //-- between 2 clusters (or/and cache pages). I.e. 1st part of this long entry belongs to one cluster and even more might not be cached,
                            //-- While the rest of the entry, DOS part of it is the 1st entry in the cluster and cached.
                            //-- In this case if we search for short file name, we find it, but the aStartEntryPos will be incorrect, which leads to the directory corruption.
                            //-- The simple and quick solution - discard 1st DOS entry and return to old search. It shall be quite rare.
                            if(StartEntryPos1.iPos == 0)
                                {//-- this is the 1st FAT entry in the cluster. Discard it, see comments above.
                                __PRINT(_L("#------ CFatMountCB::DoRummageDirCacheL() discarding FAT Entry!!"));
                                goto Exit;
                                }

                            bCacheMatchFound = ETrue;
                            goto Exit;
                            }
                        }

                    }//if(bGotEntry && MatchEntryAtt(DosEntry1.Attributes(),anAtt))

                // check boundaries after GetDirEntry()
                // if we have cross the cluster boundary, break the for loop
                if(DosEntryPos1.iCluster != clSave)
                    {//-- GetDirEntry() has decided to move to the next cluster.
                    DosEntryPos1.iCluster = clSave;
                    break;
                    }

                // if we are still in the same cluster, check the page boundary by
                /// exam how many entries we have scanned within the cluster
                const TUint entriesLooked =  ((DosEntryPos1.iPos + KSizeOfFatDirEntry)- pageStartPos) >> KSizeOfFatDirEntryLog2;
                if(entriesLooked > nEntries)
                    {
                    PassedPageBoundary = ETrue;
                    break;
                    }


                // move to next entry before scanning next file
                TRAP(nErr,MoveToNextEntryL(DosEntryPos1));
                if(nErr != KErrNone)
                    goto Exit;

                // check boundaries after MoveToNextEntryL()
                if(DosEntryPos1.iCluster != clSave)
                    {
                    DosEntryPos1.iCluster = clSave;
                    break;
                    }

                if (entriesLooked + 1 > nEntries)
                    {
                    PassedPageBoundary = ETrue;
                    break;
                    }

                } //for(;;)

            } //if(iRawDisk->PosCached(...))

        // scanning did not happen because the page is not cached,
        // or
        // scanning finished in last page and file is not found

        // if MRU page is not cached or
        // we scan MRU page first and it is not scanned yet, then this must be the MRU page,
        //  we now start to scan from the beginning
        if (ScanMRUPageFirst && !MRUPageScanned)
            {
            MRUPageScanned = ETrue;
            DosEntryPos1 = aDosEntryPos;
            DosEntryPos1.iPos = 0;
            continue;
            }

        // if we just finished scanning a page and still in the same cluster, then we crossed page
        //  boundary, continue with next page.
        // note: although we are in the 'next page' already, this page might not be cached, so we need to
        //  check it via pDirCache->PosCached(entryLinPos, nCachedLinPos) and scan it properly.
        if (PassedPageBoundary)
            {
            DosEntryPos1.iPos = CalculatePageOffsetInCluster(DosEntryPos1.iPos, pageSzLog2);
            PassedPageBoundary = EFalse;
            continue;
            }

        //-- try to move to the next cluster of the directory file

        if(DosEntryPos1.Cluster() < KFatFirstSearchCluster)  //-- small trick to get rid of TRAPping GetNextClusterL()
            break;

        // record previous cluster number before move on
        clusterNum = DosEntryPos1.iCluster;

        if(! FAT().GetNextClusterL(DosEntryPos1.iCluster))
            break;


    } //for(TUint32 entryCnt=0; entryCnt< maxDirEntries; ++entryCnt)

    //---------------------------------
    Exit:

    if(bCacheMatchFound)
        {
        //-- if the position of the found in cache object is less than given, pretend that we haven't found anything
        //-- Return to the old search, because it can be the case of the end of directory, which is quite difficult to
        //-- detect in this situation. Note that the old part of DoFindL() leaves when the search reaches the end of dir.
        TBool bFallBack=EFalse;

        if(DosEntryPos1.iCluster == aDosEntryPos.iCluster)
            {
            if(DosEntryPos1.iPos < aDosEntryPos.iPos)
                bFallBack = ETrue;
            }
        else
            {
            if(MakeLinAddrL(DosEntryPos1) < MakeLinAddrL(aDosEntryPos))
                bFallBack = ETrue;
            }

        if(bFallBack)
            {
            return EFalse;
            }

        //-- Update parameters with new values
        aStartEntryPos= StartEntryPos1;
        aDosEntryPos  = DosEntryPos1;
        aStartEntry   = StartEntry1;
        aDosEntry     = DosEntry1;

        const TInt64  mruPos = MakeLinAddrL(aDosEntryPos);
        
        pDirCache->MakePageMRU(mruPos);

        // only update the leaf dir cache when the original cache index is provided
        if (aLeafDir.iClusterNum)
            {
            iLeafDirCache->UpdateMRUPos(TLeafDirData(aLeafDir.iClusterNum, aStartEntryPos));
            }
        }
    return bCacheMatchFound;
    }

//-----------------------------------------------------------------------------------------

/**
    initialise find helper with the target file name.
    This is a quite expensive operation and initialisation is done only once. After this we know if the name is a legal dos one
    and also have the corresponding generated DOS name for it.

    @param aTargetName target file name we are looking for in ::DoFindL()
*/
void CFatMountCB::TFindHelper::InitialiseL(const TDesC& aTargetName)
    {
    if(isInitialised)
        return;

     TInt count = 1;

     iTargetName.Set(aTargetName);
     isLegalDosName = IsLegalDosName(aTargetName, ETrue, EFalse, EFalse, ETrue, EFalse);

     if(isLegalDosName)
        {//-- iShortName will contain generated short DOS name by long filename
        iShortName = DoGenerateShortNameL(aTargetName, count, ETrue);
        }

     isInitialised = ETrue;
    }

/**
    Perform binary comparison between a given the DOS entry name and the DOS name we generated in TFindHelper::Initialise().
    @param  apDosEntryName pointer to the DOS entry name in XXXXXXXXYYY format
    @return ETrue if the apDosEntryName is the same as generated iShortName
*/
TBool CFatMountCB::TFindHelper::MatchDosEntryName(const TUint8* apDosEntryName) const
    {
    ASSERT(isInitialised);

    if(!isLegalDosName)
        return EFalse;

    return (Mem::Compare(iShortName.Ptr(), KFatDirNameSize, apDosEntryName, KFatDirNameSize) == 0);
    }

//-----------------------------------------------------------------------------------------
const TInt KShortNameCandidatesNum = 4;
/**
Constructor of XFileCreationHelper class
*/
CFatMountCB::XFileCreationHelper::XFileCreationHelper()
    {
    isInitialised = EFalse;
    }

/**
Destructor of XFileCreationHelper class
*/
CFatMountCB::XFileCreationHelper::~XFileCreationHelper()
    {
    Close();
    }

/**
Initialises a TFileCreationHelper object, generate a short name candidate pool.

@param  aTargetName Target file name for the potential new file.
@post   TFileCreationHelper is fully initialised.
*/
void CFatMountCB::XFileCreationHelper::InitialiseL(const TDesC& aTargetName)
    {
    // close before use, to avoid memory leak
    Close();

    iTargetName.Set(aTargetName);
    // generates short name candidate(s)
    TInt count = 1;
    while (count <= KShortNameCandidatesNum)
        {
        TShortName shortNameCandidate = DoGenerateShortNameL(aTargetName, count, ETrue);
        TInt err = iShortNameCandidates.Append(shortNameCandidate);
        User::LeaveIfError(err);

        if (count == -1)    // No tilde and number is needed
            {
            break;
            }
        else
            count++;
        }

    // calculate number of new entries needed
    iNumOfAddingEntries = 1;
    isTrgNameLegalDosName = IsLegalDosName(aTargetName, EFalse, EFalse, EFalse, EFalse, ETrue);
    if (!isTrgNameLegalDosName)
        iNumOfAddingEntries = (TUint16) NumberOfVFatEntries(iTargetName.Length());

    isNewEntryPosFound = EFalse;
    isInitialised = ETrue;
    }

/**
Close function of XFileCreationHelper class
*/
void CFatMountCB::XFileCreationHelper::Close()
    {
    iShortNameCandidates.Close();
    isInitialised = EFalse;
    }

/**
Validates short name candidates. If the input dos entry name is found in the short name
 candidate pool, the corresponding short name candidate will be removed from the pool.

@param  apDosEntryName  An existing short name, to compare with the candidates.
@pre    Object should be initialised
*/
void CFatMountCB::XFileCreationHelper::CheckShortNameCandidates(const TUint8* apDosEntryName)
    {
    ASSERT(isInitialised);
    if (!isInitialised)
        return;

    if (iShortNameCandidates.Count() > 0)
        {
        for (TInt i = 0; i < iShortNameCandidates.Count(); i++)
            {
            if (Mem::Compare(iShortNameCandidates[i].Ptr(), KFatDirNameSize, apDosEntryName, KFatDirNameSize) == 0)
                {
                iShortNameCandidates.Remove(i);
                break;
                }
            }
        }
    }

/**
Gets a validated short name from the short name candidate pool.

@param  aShortName  On return, contains a validated short name if found, otherwise zeroed.
@return TInt        Returns KErrNone if a validated short name found successfully,
                     else KErrNotFound is returned.
                    Returns KErrNotReady if object is not initialised.
@pre    Object should be initialised
*/
TInt CFatMountCB::XFileCreationHelper::GetValidatedShortName(TShortName& aShortName) const
    {
    aShortName.Zero();

    ASSERT(isInitialised);
    if (!isInitialised)
        return KErrNotReady;

    if (iShortNameCandidates.Count() > 0)
        {
        aShortName.Copy(iShortNameCandidates[0]);
        return KErrNone;
        }

    return KErrNotFound;
    }

//-----------------------------------------------------------------------------------------


/**
    Scan a directory looking for aName.

    @param  aTrgtName       a name of an object we are looking up in directory
    @param  anAtt           attributes of this object
    @param  aStartEntryPos  on return in case of VFAT entry will contain start position of the VFAT dir. entry
    @param  aStartEntry     on return will contain first VFAT dir entry
    @param  aDosEntryPos    the search will start from this position of dir entry, on return it will contain result DOS entry position, last one for VFAT case
    @param  aDosEntry       on return will contain DOS dir entry (the last one for VFAT case)
    @param  aFileName       in the case of VFAT entry and on success here will be returned a long filename
    @param  anError         This function might leave with this given error code
    @param  aFileCreationHelper       a helper package for file creations

    @return ETrue if extracted entry is VFAT one, EFalse, if it's old DOS-style one
    @leave  can leave with anError code on error or if the search has reached the end of directory (!)
*/
TBool CFatMountCB::DoFindL(const TDesC& aTrgtName,TUint anAtt,
                        TEntryPos& aStartEntryPos,TFatDirEntry& aStartEntry,
                        TEntryPos& aDosEntryPos,TFatDirEntry& aDosEntry,
                        TDes& aFileName,TInt anError,
                        XFileCreationHelper* aFileCreationHelper,
                        const TLeafDirData& aLeafDirData) const
    {
    // check that the entry position to be read next is not past the end of the
    // root directory. If this is the case then when GetDirEntryL(..) is called
    // this will lead to MakeLinAddr(..) leaving with KErrDirFull.

    if (IsRootDir(aDosEntryPos)&&(aDosEntryPos.iPos+StartOfRootDirInBytes()>=RootDirEnd()))
        User::Leave(anError);//Allows maximum number of entries in root directory

    __PRINT2(_L("CFatMountCB::DoFindL() drv:%d, %S"),Drive().DriveNumber(),&aTrgtName);

    TInt previousCluster=aDosEntryPos.iCluster;
    TUint previousPosition=aDosEntryPos.iPos;
    TInt changePreviousCluster=1;
    TInt count=0;

    TBool trgNameIsWildCard     = EFalse; //-- ETrue if the name we are looking for is a wildcard
    TBool trgNameFullySpecified = ETrue;  //-- ETrue if the name we are looking for doesn't contain wildcards


    {
    //-- find out if the name we are looking for is a wildcard ("*" or "*.*")
    const TInt len = aTrgtName.Length();

    if(len == 1)
        trgNameIsWildCard = (aTrgtName[0] == '*');
    else if(len == 3)
        {
        _LIT(KAllFiles, "*.*");
        trgNameIsWildCard = (aTrgtName==KAllFiles);
        }

    //-- find out if the name we are looking for contains wildcharacters: "*" or "?"
    if(trgNameIsWildCard)
        trgNameFullySpecified = EFalse;
    else
        {
        for(TInt i=0; i<len; ++i)
            {
            const TChar ch = aTrgtName[i];
            if(ch == (TChar)'*' || ch == (TChar)'?')
                {
                trgNameFullySpecified = EFalse;
                break;
                }
            }
        }
    }


    TPtrC trgtNameNoDot(aTrgtName);

    TFindHelper findHelper;
    //---------------------------------------------------
    //-- if we have fully specified name and directory cache is present, try to
    //-- locate the name in the cache first to avoid reading from media
    //-- if the entry belongs to the root directory (for FAT12,16) skip the lookup, because root directory isn't aligned by cluster size boundary,
    //-- while directory cache pages are. For FAT32 it doesn't matter, because root dir is a usual file.
    if(iRawDisk->DirCacheInterface() && trgNameFullySpecified && !IsRootDir(aDosEntryPos) && !aFileCreationHelper)
        {//-- aName is fully specified, i.e doesn't contain wildcards

        findHelper.InitialiseL(trgtNameNoDot);

        const TBool bMatchFound = DoRummageDirCacheL(anAtt, aStartEntryPos, aStartEntry, aDosEntryPos, aDosEntry, aFileName, findHelper, aFileCreationHelper, aLeafDirData);
        if(bMatchFound)
            {
            return(aStartEntry.IsVFatEntry());
            }
        }
    //---------------------------------------------------

    // we need to scan ahead from the mru pos then come back to beginning, if startcluster is provided
    TBool scanAhead = EFalse;
    // if we have a starting cluster number (and it's not root directory in FAT16/12 case)&&
    //  we found a lastScanned entry's cluster (and it's not root directory in FAT16/12 case)&&
    // if we don't have a starting cluster number, we draw back to original scanning algorithm
    if (!IsRootDir(aDosEntryPos)            // we don't do forward scanning for root dir &
            && aLeafDirData.iClusterNum != 0    // if we have a starting cluster number &
            && aLeafDirData.iMRUPos.Cluster() != 0)     // if we have a starting cluster number &
        {
        scanAhead = ETrue;
        aDosEntryPos = aLeafDirData.iMRUPos;
        }

    TInt numFound = 0;
    TEntryPos startPos = aDosEntryPos;
    TInt clustNum = aDosEntryPos.Cluster();

    for (TInt scanCnt = 1; scanCnt <= 2; ++scanCnt)
        {
        // if we are not scanning ahead, we don't need this outer for loop
        if (!scanAhead)
            scanCnt++;

        TBool found = EFalse;

        FOREVER //FOREVER2 -- walk through all directory entries in the current directory until find a match or directory end
            {
            //-- read full directory entry starting from aDosEntryPos. On return aFileName may contain assembled long filename (if the entry is VFAT)
            //-- aDosEntry will contain a DOS entry of the directory entry we have read.
            aStartEntryPos=aDosEntryPos;
            User::LeaveIfError(GetDirEntry(aDosEntryPos, aDosEntry, aStartEntry, aFileName));

            if (aDosEntry.IsEndOfDirectory())
                {//-- the end of directory reached.

                // if new entry position for adding has not been found yet.
                // note aFileCreationHelper may not be initialised for pure file opening operations
                if (aFileCreationHelper && aFileCreationHelper->IsInitialised() && !aFileCreationHelper->IsNewEntryPosFound())
                    {
                    // if MoveToNextEntryL have gone to the next cluster which is the end of cluster chain,
                    //  we pass the last scanned entry position to AddDirEntryL
                    if (IsEndOfClusterCh(aDosEntryPos.iCluster))
                        {
                        TInt clusterSize=1<<ClusterSizeLog2();
                        TEntryPos dummyPos(clustNum, clusterSize - KSizeOfFatDirEntry);
                        aFileCreationHelper->SetEntryAddingPos(dummyPos);
                        aFileCreationHelper->SetIsNewEntryPosFound(ETrue);
                        }
                    // or we reached the end of the directory.
                    else
                        {
                        aFileCreationHelper->SetEntryAddingPos(aDosEntryPos);
                        aFileCreationHelper->SetIsNewEntryPosFound(ETrue);
                        }
                    }

                // if we are scanning ahead and this is the first scanning, we break out to restart scanning
                if (scanAhead && scanCnt == 1)
                    {
                    break; // from FOREVER, restart scanning
                    }

                // if (!scanAhead || scanAhead && scanCnt == 2)
                User::Leave(anError);
                }


            // entry space searching for potential new file/directory creation
            if (aFileCreationHelper && aFileCreationHelper->IsInitialised() && !aFileCreationHelper->IsNewEntryPosFound())
                {
                if (!aDosEntry.IsErased() && !aDosEntry.IsGarbage())
                    {
                    numFound = 0;
                    }
                else
                    {
                    if (numFound == 0)
                        {
                        startPos = aDosEntryPos;
                        }
                    numFound++;
                    if (numFound == aFileCreationHelper->NumOfAddingEntries())
                        {
                        aFileCreationHelper->SetEntryAddingPos(startPos);
                        aFileCreationHelper->SetIsNewEntryPosFound(ETrue);
                        }
                    }
                }


            if (IsRootDir(aDosEntryPos)&&(aDosEntryPos.iPos+StartOfRootDirInBytes()==(RootDirEnd()-KSizeOfFatDirEntry)))
                if (aDosEntry.IsErased())
                    {
                    User::Leave(anError);//Allows maximum number of entries in root directory
                    }


            const TBool bFileNameEntry = !aDosEntry.IsCurrentDirectory() && !aDosEntry.IsParentDirectory() && !aDosEntry.IsErased() && !aDosEntry.IsGarbage();

            if (bFileNameEntry && MatchEntryAtt(aDosEntry.Attributes(), anAtt))
                {//-- we have read a filename entry and entry's attributes match required; compare names then.

                if (trgNameIsWildCard)
                    {
                    found = ETrue;
                    break; //-- we were looking for '*' or '*.*', so will be satisfied with any current file name.
                    }


                if (aStartEntry.IsVFatEntry())
                    {//-- we've read a VFAT entry, aFileName is supposed to contain long filename, aDosEntry - DOS entry for this name.
                     //-- note: aFileName.Length() may be 0, while DOS entry (short name is OK) in the case of orphaned VFAT entries


                    // we only check short name candidates for long file names with VFAT entries,
                    //  if it is a valid dos name, it will be checked by default
                    // note, for file creation cases, target name will be always fully specified
                    if (aFileCreationHelper && aFileCreationHelper->IsInitialised() && trgNameFullySpecified)
                         {
                         aFileCreationHelper->CheckShortNameCandidates(aDosEntry.Name().Ptr());
                         }

                    //-- discard trailing dots from aFileName if present
                     TPtrC ptrAssembledName = RemoveTrailingDots(aFileName);

                     if(ptrAssembledName.MatchF(trgtNameNoDot) != KErrNotFound)
                         {
                         found = ETrue;
                         break; //-- OK, found a match.
                         }
                     else if (trgNameFullySpecified)
                         {
                         //-- long name assembled by GetDirEntry() doesn't match the target. But if he target name is fully specified,
                         //-- we need to compare corresponding DOS entries, because VFAT entries may be damaged, while DOS ones are OK.
                         findHelper.InitialiseL(trgtNameNoDot);

                         if(findHelper.MatchDosEntryName(aDosEntry.Name().Ptr()))
                             {
                             found = ETrue;
                             break; //-- DOS entries match, success.
                             }
                         }
                     else if (!trgNameFullySpecified)
                         {//-- target name contains wildcards, we need to use MatchF with dos name
                         TBuf8<0x20> dosName8(DosNameFromStdFormat(aDosEntry.Name()));
                         TBuf<0x20>  dosName;
                         LocaleUtils::ConvertToUnicodeL(dosName, dosName8); //-- convert DOS name to unicode (implies locale settings)
                         if (dosName.MatchF(trgtNameNoDot)!=KErrNotFound)
                             {
                             found = ETrue;
                             break;
                             }
                         }


                    }
                else //if (aStartEntry.IsVFatEntry())
                    {//-- we've read a legacy FAT entry, so compare DOS entries
                    findHelper.InitialiseL(trgtNameNoDot);

                    if(findHelper.TrgtNameIsLegalDos())
                        {//-- we are looking for a legal DOS name
                        if(trgNameFullySpecified)
                            {//-- if the target name is fully specified, we can yse binary comparison of the DOS entries
                            if(findHelper.MatchDosEntryName(aDosEntry.Name().Ptr()))
                                {
                                found = ETrue;
                                break;
                                }
                            }
                        else
                            {//-- target name contains wildcards, we neeed to use MatchF
                            TBuf8<0x20> dosName8(DosNameFromStdFormat(aDosEntry.Name()));
                            TBuf<0x20>  dosName;
                            LocaleUtils::ConvertToUnicodeL(dosName, dosName8); //-- convert DOS name to unicode (implies locale settings)
                            if (dosName.MatchF(trgtNameNoDot)!=KErrNotFound)
                                {
                                found = ETrue;
                                break;
                                }

                            }
                        } //if(findHelper.TrgtNameIsLegalDos())

                    } //else if (aStartEntry.IsVFatEntry())

                } //if (bFileNameEntry && MatchEntryAtt(aDosEntry.Attributes(),anAtt))


            // record previous cluster number
            clustNum = aDosEntryPos.iCluster;

            // this is the 2nd scanning and we have just passed the pos we started.
            if (scanAhead && scanCnt == 2)
                {
                if (aDosEntryPos.Cluster() == aLeafDirData.iMRUPos.Cluster()
                        && aDosEntryPos.Pos() >= aLeafDirData.iMRUPos.Pos())
                    {
                    User::Leave(anError);
                    }
                }


            MoveToNextEntryL(aDosEntryPos); //-- goto the next entry in the directory

            if (IsRootDir(aDosEntryPos)&&(aDosEntryPos.iPos+StartOfRootDirInBytes()>=RootDirEnd()))
                {
                User::Leave(anError);//Allows maximum number of entries in root directory
                }


            if (!scanAhead || scanCnt == 2)
                {
                if (aDosEntryPos.iCluster && (aDosEntryPos.iPos <= previousPosition))
                    DoCheckFatForLoopsL(aDosEntryPos.iCluster,previousCluster,changePreviousCluster,count);

                previousPosition=aDosEntryPos.iPos;
                }
            }   // FOREVER -- the actual scanning is done inside this loop


        if (found)
            {
            break;
            }

        // if not found:
        // if we have not found in the first scanning and we are doing scanning ahead,
        //  we need to go back to the starting pos of this dir and scan from start until
        //  we reach lastscannedPos
        if (scanAhead && scanCnt == 1)
            {
            aDosEntryPos = TEntryPos(aLeafDirData.iClusterNum, 0);
            continue;
            }
        else
            {
            // there are only two exits: either found or reached end of dir in the 1st scanning
            ASSERT(0);
            break;
            }
        } // for (TInt scanCnt = 1; scanCnt <= 2; ++scanCnt)

    //---------------------------------------------------
    if (iRawDisk->DirCacheInterface() && aDosEntryPos.Cluster())
        {
        TInt64 mruPos = MakeLinAddrL(aDosEntryPos);
        iRawDisk->DirCacheInterface()->MakePageMRU(mruPos);

        // only update the leaf dir cache when the original cache index is provided
        if (aLeafDirData.iClusterNum)
            {
            iLeafDirCache->UpdateMRUPos(TLeafDirData(aLeafDirData.iClusterNum, aDosEntryPos));
            }
        }

    return (aStartEntry.IsVFatEntry());
    }

//-----------------------------------------------------------------------------------------
/**
    Locate an directory entry entry from its full path name.

    @param  aName           a name of an object we are looking for
    @param  anAtt           attributes of this object
    @param  anEntry         on return will contain first VFAT dir entry
    @param  anEntryPos      on return in case of VFAT entry will contain start position of the VFAT dir. entry

    @leave  can leave with KErrNotFound if the search has reached the end of directory
*/
void CFatMountCB::FindEntryStartL(const TDesC& aName,TUint anAtt,TFatDirEntry& anEntry,TEntryPos& anEntryPos) const
    {
    __PRINT(_L("CFatMountCB::FindEntryStartL()"));
    TInt namePos=aName.LocateReverse(KPathDelimiter)+1; // There is always a path delimiter
    TFileName fileName;
    TLeafDirData leafDir;
    TEntryPos dosEntryPos(FindLeafDirL(aName.Left(namePos),leafDir),0);
    TFatDirEntry dosEntry;

    DoFindL(aName.Mid(namePos),anAtt,anEntryPos,anEntry,dosEntryPos,dosEntry,fileName,KErrNotFound,NULL,leafDir);
    }


//-----------------------------------------------------------------------------------------

/**
    Locate an directory entry entry from its full path name.

    @param  aName           a name of an object we are looking for
    @param  anAtt           attributes of this object
    @param  anEntry         on return will contain first VFAT dir entry
    @param  anEntryPos      on return in case of VFAT entry will contain start position of the VFAT dir. entry

    @leave  can leave with KErrNotFound if the search has reached the end of directory
*/
void CFatMountCB::FindEntryStartL(const TDesC& aName,TUint anAtt,TFatDirEntry& anEntry,TEntryPos& anEntryPos,XFileCreationHelper* aFileCreationHelper) const
    {
    __PRINT(_L("CFatMountCB::FindEntryStartL()"));
    TInt namePos=aName.LocateReverse(KPathDelimiter)+1; // There is always a path delimiter
    TFileName fileName;
    TLeafDirData leafDir;
    TEntryPos dosEntryPos(FindLeafDirL(aName.Left(namePos),leafDir),0);
    TFatDirEntry dosEntry;
    DoFindL(aName.Mid(namePos),anAtt,anEntryPos,anEntry,dosEntryPos,dosEntry,fileName,KErrNotFound,aFileCreationHelper,leafDir);
    }

//-----------------------------------------------------------------------------------------
void CFatMountCB::FindDosNameL(const TDesC& aName,TUint anAtt,TEntryPos& aDosEntryPos,TFatDirEntry& aDosEntry,TDes& aFileName,TInt anError) const
//
// Scan a directory looking for aName.
// aCluster and anEntryAddr give the location of the entry.
//
    {

    __PRINT(_L("CFatMountCB::FindDosNameL()"));
    TEntryPos startPos;
    TFatDirEntry startEntry;

    TLeafDirData leafDir;           // leaf dir data is zero initialized, no scannig ahead
    DoFindL(aName,anAtt,startPos,startEntry,aDosEntryPos,aDosEntry,aFileName,anError,NULL,leafDir);
    }
//-----------------------------------------------------------------------------------------

void CFatMountCB::AddDirEntryL(TEntryPos& aPos,TInt aNumOfEntries)
//
// Find space for a new directory entry. Leave KErrEof if no space
//
    {

    __PRINT(_L("CFatMountCB::AddDirEntryL"));
    TInt numFound=0;
    TFatDirEntry entry;
    TEntryPos startPos(RootIndicator(),0);
    TInt clusterNum=aPos.iCluster;
    FOREVER
        {
        ReadDirEntryL(aPos,entry);
        if (entry.IsEndOfDirectory())
            break;
        if (!entry.IsErased() && !entry.IsGarbage())
            numFound=0;
        else
            {
            if (numFound==0)
                startPos=aPos;
            numFound++;
            if (numFound==aNumOfEntries)
                {
                aPos=startPos;
                return;
                }
            }
        clusterNum=aPos.iCluster;
        MoveToNextEntryL(aPos);
        if (IsRootDir(aPos)&&(StartOfRootDirInBytes()+aPos.iPos==RootDirEnd()))
    //  No end of directory marker at end of root directory
            User::Leave(KErrDirFull);
        }

    TUint clusterSize=1<<ClusterSizeLog2();
    if (IsEndOfClusterCh(aPos.iCluster))
        { // End of last cluster in directory
        aPos.iCluster=clusterNum;
        aPos.iPos=clusterSize;
        }

    TEntryPos eofPos(aPos.iCluster,aPos.iPos+KSizeOfFatDirEntry*aNumOfEntries);

    if (IsRootDir(aPos))
        { // Special case of root directory
        if (eofPos.iPos+StartOfRootDirInBytes()>RootDirEnd())
            User::Leave(KErrDirFull);
        else
            return;
        }

    if (eofPos.iPos==clusterSize)
        return; // No need to allocate
    if (eofPos.iPos>clusterSize)
        {
        TInt numNeeded=eofPos.iPos>>ClusterSizeLog2();
        if(IsRuggedFSys())
            {
            ExtendClusterListZeroedL(numNeeded,eofPos.iCluster);
            }
        else
            {
            FAT().ExtendClusterListL(numNeeded,eofPos.iCluster);
            ZeroDirClusterL(eofPos.iCluster);
            }

        eofPos.iPos-=numNeeded<<ClusterSizeLog2();
        if(aPos.iPos==clusterSize)
            {
            if (!FAT().GetNextClusterL(aPos.iCluster))
                {
                __PRINT(_L("CFatMountCB::AddDirEntryL corrupt#1"))
                User::Leave(KErrCorrupt);
                }
            aPos.iPos=0;
            }
        }
    else if(Drive().IsRemovable())
        {
        // check if entry is already zeroed
        ReadDirEntryL(eofPos,entry);
        if(!entry.IsEndOfDirectory())
            {
            // some removable media may not have directory zeroed
            entry.SetEndOfDirectory();
            WriteDirEntryL(eofPos,entry);
            }
        }
    }

/**
    Zero fill a cluster
    @param  aCluster cluster number to zero-fill
*/
void CFatMountCB::ZeroDirClusterL(TInt aCluster)
    {

    __PRINT1(_L("CFatMountCB::ZeroDirClusterL %d"),aCluster);

    const TUint32 KClusterSz= 1<<ClusterSizeLog2();
    const TUint32 KMaxBufSz = KClusterSz;           //-- max. nuffer size is a cluster
    const TUint32 KMinBufSz = 1<<SectorSizeLog2();  //-- min. buffer size is 1 sector (for OOM case)

    //-- allocate a buffer for zero-filling a cluster
    RBuf8 buf;
    CleanupClosePushL(buf);

    if(buf.CreateMax(KMaxBufSz) != KErrNone)
        buf.CreateMaxL(KMinBufSz); //-- OOM, try to create smaller buffer

    buf.FillZ();

    TEntryPos entryPos(aCluster,0);

    //-- write buffer to the beginning of the directory file.
    DirWriteL(entryPos, buf); //-- use special interface to access FAT directory file

    //-- fill in the rest of the cluster if we used a small buffer
    if((TUint32)buf.Size() < KClusterSz) //--  KMaxBufSz may == KMinBufSz if we have 1 sector per cluster
    {
        const TInt restCnt = SectorsPerCluster() - 1;
        ASSERT(restCnt >=1);

        for(TInt i=0; i<restCnt; ++i)
        {
            entryPos.iPos += KMinBufSz;
            DirWriteL(entryPos, buf); //-- use special interface to access FAT directory file
        }

    }

    CleanupStack::PopAndDestroy(&buf);
    }


/**
    Internal method. Retrieves directory entry from given position.

    @param  aPos            on enter shall contain start position, from where the entry will be read. On return contains position of the DOS entry (the last one for object name for the VFAT case)
    @param  aDosEntry       On return contains DOS entry for the VFAT case
    @param  aStartEntry     On return contains start entry of the directory object for the VFAT case
    @param  aLongFileName   On return contains VFAT or long filename

    @return  ETrue  if whole FAT entry is OK: only 1 entry for DOS name or _ALL_ entries for a long name
             EFalse if there was an error in assembling entries to the long file name. In this case this entry shall be ignored by upper level.

    can leave because of ReadDirEntryL() and MoveToNextEntryL() [end of dir].
*/
TBool CFatMountCB::DoGetDirEntryL(TEntryPos& aPos, TFatDirEntry& aDosEntry, TFatDirEntry& aStartEntry, TDes& aLongFileName) const
    {

//    __PRINT3(_L("CFatMountCB::GetDirEntryL() drv:%d, pos:%d:%d"), Drive().DriveNumber(), aPos.iCluster, aPos.iPos);

    ReadDirEntryL(aPos,aStartEntry);
    aDosEntry=aStartEntry;
    if (!aDosEntry.IsVFatEntry() || aDosEntry.IsErased() || aDosEntry.IsGarbage())
        {//-- This is either a 8.3 FAT entry or garbage
        aLongFileName.SetLength(0);
        return ETrue;
        }

    //-- process VFAT entries

    if(!aDosEntry.IsLongNameStart())
        return EFalse; //-- wrong counter in the 1st VFat entry, consider it as orphaned


    TInt count = aDosEntry.NumFollowing(); //-- count of the following VFat entries

    TBuf16<KMaxVFatEntryName> vBuf(KMaxVFatEntryName);
    aDosEntry.ReadVFatEntry(vBuf);

    TInt vLength=vBuf.Locate('\0');
    if (vLength==KErrNotFound)
        vLength=KMaxVFatEntryName;

    vBuf.SetLength(vLength);

    const TInt nameLen = vLength+KMaxVFatEntryName*(count-1);
    if(nameLen <= 0 || nameLen > KMaxFileName)
        return EFalse; //--  wrong long file name length, consider VFAT entry as orphaned

    aLongFileName.SetLength(nameLen);

    const TUint8 entryCheckSum = aDosEntry.CheckSum(); //-- check sum from the 1st VFat entry

    while (count--)
        {
        TPtr fileNamePtr(&aLongFileName[0]+KMaxVFatEntryName*count,aLongFileName.Length()-KMaxVFatEntryName*count);
        fileNamePtr.Copy(vBuf);
        if (count==0)
            break; //-- all VFat entries read, only DOS entry remained

        MoveToNextEntryL(aPos);
        ReadDirEntryL(aPos,aDosEntry);

        //-- check if it is correct VFat entry.
        //-- if not, this is the "orphaned" entry and will be ignored
        if(!aDosEntry.IsVFatEntry() || aDosEntry.IsErased() || entryCheckSum != aDosEntry.CheckSum() || aDosEntry.NumFollowing() != count)
            return EFalse;  //-- bad VFAT entry

        aDosEntry.ReadVFatEntry(vBuf);
        }

    if (IsRootDir(aPos)&&(aPos.iPos+StartOfRootDirInBytes()==(RootDirEnd()-KSizeOfFatDirEntry)))
        return ETrue;//Allows maximum number of entries in root directory

    //-- read the last, DOS FAT entry
    MoveToNextEntryL(aPos);
    ReadDirEntryL(aPos,aDosEntry);

    //-- check if it is corect
    if(aDosEntry.IsEndOfDirectory() || aDosEntry.IsErased() || aDosEntry.IsVFatEntry())
        return EFalse; //-- Bad DOS entry

    //-- verify ChechSum here if it is incorrect, use DOS name only
    const TUint8 calcNameChkSum = CalculateShortNameCheckSum(aDosEntry.Name());
    if(calcNameChkSum != entryCheckSum)
        {
        aLongFileName.SetLength(0);//-- don't use long filename
        __PRINT2(_L("CFatMountCB::GetDirEntryL() CheckSum mismatch: VFat:0x%x, DOS:0x%d"),entryCheckSum, calcNameChkSum);
        }

    return ETrue;
    }


/**
    Read a number of VFAT entries from the directory file.
    for parameters see DoGetDirEntryL()

    @return KErrNone if everything is OK, system wide error code otherwise

*/
TInt CFatMountCB::GetDirEntry(TEntryPos& aPos,TFatDirEntry& aDosEntry,TFatDirEntry& aStartEntry,TDes& aLongFileName) const
    {

    TBool bEntryOK=ETrue;
    TRAPD(nErr, bEntryOK = DoGetDirEntryL(aPos, aDosEntry, aStartEntry, aLongFileName));

    if(nErr !=KErrNone)
        return nErr;

    if(!bEntryOK)
        {//-- DoGetDirEntryL could not assemble whole VFat entry, probably some parts of it are damaged.
         //-- consider it as an "orphaned" entry and skip
        aDosEntry.iData[0] = 0xFF;      // Mark entry as garbage
        aLongFileName.SetLength(0);     // No long filename
        }

    return KErrNone;
    }

void CFatMountCB::MoveToNextEntryL(TEntryPos& aPos) const
//
// If anEntry is at the end of the cluster, and we are not the root dir,
// move it to the next in the list.
//
    {

//  __PRINT(_L("CFatMountCB::MoveToNextEntryL"));
    if (IsEndOfClusterCh(aPos.iCluster))
        return;
    const TUint temp = 1<<ClusterSizeLog2();
    if (aPos.iPos+KSizeOfFatDirEntry!=temp || IsRootDir(aPos))
        {
        aPos.iPos+=KSizeOfFatDirEntry;
        }
    else
        {
        if (FAT().GetNextClusterL(aPos.iCluster)==EFalse)
            {
            SetEndOfClusterCh(aPos.iCluster);
            }
        aPos.iPos=0;
        }
    }

//-----------------------------------------------------------------------------------------

/**
    Starting from a VFat entry walk down the directory until the associated dos entry is found

    @param aPos     in: VFAT entry position. out: if this is a VFAT entry set, it will be DOS entry position. otherwise not changed
    @param anEntry  on return will contain DOS dir. entry contents (if aPos points to the VFAT entry)
*/
void CFatMountCB::MoveToDosEntryL(TEntryPos& aPos,TFatDirEntry& anEntry) const
    {

    //__PRINT(_L("CFatMountCB::MoveToDosEntryL"));
    if (anEntry.IsVFatEntry()==EFalse)
        return;
    FOREVER
        {
        MoveToNextEntryL(aPos);
        ReadDirEntryL(aPos,anEntry);
        if (anEntry.IsVFatEntry()==EFalse)
            break;
        if (IsRootDir(aPos)&&(aPos.iPos+StartOfRootDirInBytes()==(RootDirEnd()-KSizeOfFatDirEntry)))
            break;  //  Allows maximum number of entries in root directory
        }
    }

//-----------------------------------------------------------------------------------------

/** Read the Uid of the entry starting at aCluster */
void CFatMountCB::ReadUidL(TInt aCluster,TEntry& anEntry) const
    {

    __PRINT1(_L("CFatMountCB::ReadUidL(%d)"), aCluster);

    if((TUint)aCluster < KFatFirstSearchCluster || (TUint)aCluster >= UsableClusters()+KFatFirstSearchCluster)
        User::Leave(KErrCorrupt);

    TBuf8<sizeof(TCheckedUid)> uidBuf;
    iRawDisk->ReadCachedL(FAT().DataPositionInBytes(aCluster),sizeof(TCheckedUid),uidBuf);
    __ASSERT_DEBUG(uidBuf.Length()==sizeof(TCheckedUid),Fault(EFatReadUidFailed));
    TCheckedUid uid(uidBuf);
    anEntry.iType=uid.UidType();
    }

//-----------------------------------------------------------------------------------------

/**
    Read file section without opening this file on a file server side.

    @param  aName       file name; all trailing dots from the name will be removed
    @param  aFilePos    start read position within a file
    @param  aLength     how many bytes to read; on return will be how many bytes actually read
    @param  aDes        local buffer desctriptor
    @param  aMessage    from file server, used to write data to the buffer in different address space.

    @leave on media read error
*/
void CFatMountCB::ReadSectionL(const TDesC& aName,TInt aPos,TAny* aTrg,TInt aLength,const RMessagePtr2& aMessage)
    {
    __PRINT4(_L("CFatMountCB::ReadSectionL, drv:%d, pos:%d, len:%d, FN:%S"), DriveNumber(), aPos, aLength, &aName);

    CheckStateConsistentL();

    TEntryPos dosEntryPos(RootIndicator(),0);
    TFatDirEntry dosEntry;
    TFileName fileName;


    TInt namePos=RemoveTrailingDots(aName).LocateReverse(KPathDelimiter)+1; // There is always a path delimiter
    TLeafDirData leafDir;
    dosEntryPos.iCluster=FindLeafDirL(RemoveTrailingDots(aName).Left(namePos), leafDir);
    dosEntryPos.iPos=0;
    TEntryPos startPos;
    TFatDirEntry startEntry;
    DoFindL(RemoveTrailingDots(aName).Mid(namePos),KEntryAttMaskSupported,
            startPos,startEntry,dosEntryPos,dosEntry,
            fileName,KErrNotFound,
            NULL,
            leafDir);

//  Check that reading from aPos for aLength lies within the file
//  if aPos is within the file, and aLength is too long, read up to EOF
//  If aPos is beyond the end of the file, return a zero length descriptor

    TUint32 fileSize = dosEntry.Size();
    if ((TUint)aPos>=fileSize)
        User::Leave(KErrEof);

    if ((TUint)(aPos+aLength)>fileSize)
        aLength=fileSize-aPos;

    TInt cluster=StartCluster(dosEntry);
    TInt pos = aPos;

    TInt endCluster;
    TInt clusterSize=1<<ClusterSizeLog2();      //  Size of file clusters
    TInt readTotal = 0;

    // Total number of clusters in file
    TInt maxClusters=((fileSize+clusterSize-1)>>ClusterSizeLog2());

    // Read data
    FOREVER
        {
        //  Get the maximum number of clusters that can be read contiguously
        TInt clusterListLen=FAT().CountContiguousClustersL(cluster,endCluster,maxClusters);
        __ASSERT_DEBUG(clusterListLen>0,Fault(EReadFileSectionFailed));

        //  If start position within this block, then read some data
        if (pos<(clusterListLen<<ClusterSizeLog2()))
            {
            //  Read the remaining length or the entire cluster block whichever is smaller
            TInt readLength = Min(aLength-readTotal,(clusterListLen<<ClusterSizeLog2())-pos);
            __ASSERT_DEBUG(readLength>0,Fault(EReadFileSectionFailed));
            TInt64 dataAddress=(FAT().DataPositionInBytes(cluster))+pos;
            iRawDisk->ReadL(dataAddress,readLength,aTrg,aMessage,readTotal);
            readTotal += readLength;

            if (readTotal == aLength)
                return;

            pos += readLength;
            }

        // Get the next cluster in file
        pos-=(clusterListLen<<ClusterSizeLog2());
#if defined(_DEBUG)
        TBool remainingClusters=
#endif
            ((CFatMountCB*)this)->FAT().GetNextClusterL(endCluster);
        __ASSERT_DEBUG(remainingClusters,Fault(EReadFileSectionFailed));
        cluster=endCluster;
        }
    }


//-----------------------------------------------------------------------------------------

void CFatMountCB::RawReadL(TInt64 aPos,TInt aLength,const TAny* aTrg,TInt anOffset,const RMessagePtr2& aMessage) const
//
// Read aLength of data from disk directly to thread relative descriptor
//
    {
    iRawDisk->ReadL(aPos,aLength,aTrg,aMessage,anOffset);
    }

//-----------------------------------------------------------------------------------------

void CFatMountCB::RawWriteL(TInt64 aPos,TInt aLength,const TAny* aSrc,TInt anOffset,const RMessagePtr2& aMessage)
//
// Write aLength of data from thread relative descriptor to disk
//
    {
    CheckWritableL();

    //-- check if we are trying to write to the FAT directly and wait until FAT scan thread finishes in this case.
    FAT().RequestRawWriteAccess(aPos, aLength);

    iRawDisk->WriteL(aPos,aLength,aSrc,aMessage,anOffset);
    //-- Note: FAT directory cache will be invalidated in MountL()
    }

//-----------------------------------------------------------------------------------------
/**
    This method must be used when writing to the FAT directory file.
    If FAT directory cache is present on this drive, it will be used.
    @param  aPos    entry media position
    @param  aDes    data descriptor
*/
void CFatMountCB::DirWriteL(const TEntryPos& aPos,const TDesC8& aDes)
    {
        CheckWritableL();
        const TInt64 posAddr=MakeLinAddrL(aPos);

        if(!iRawDisk->DirCacheInterface())
            {
            iRawDisk->WriteCachedL(posAddr,aDes);
            }
        else
            {//-- if there is an interface to the FAT directory cache, use it
            iRawDisk->DirCacheInterface()->WriteL(posAddr, aDes);
            }
    }

//-----------------------------------------------------------------------------------------

/**
    This method must be used when reading from the FAT directory file.
    If FAT directory cache is present on this drive, it will be used.

    @param  aPos    entry media position
    @param  aLength how many bytes to read
    @param  aDes    input data descriptor
*/
void CFatMountCB::DirReadL(const TEntryPos& aPos, TInt aLength, TDes8& aDes) const
    {
        const TInt64 posAddr=MakeLinAddrL(aPos);

        if(!iRawDisk->DirCacheInterface())
            {
            iRawDisk->ReadCachedL(posAddr,aLength,aDes);
            }
        else
            {//-- if there is an interface to the FAT directory cache, use it
            iRawDisk->DirCacheInterface()->ReadL(posAddr, aLength, aDes);
            }
    }

//-----------------------------------------------------------------------------------------

void CFatMountCB::WriteDirEntryL(const TEntryPos& aPos,const TFatDirEntry& aDirEntry)
//
// Write a FAT directory entry to disk.
// Assumes sufficient space has been created for it by AddDirEntry.
//
    {

    __PRINT(_L("CFatMountCB::WriteDirEntryL"));

    //-- use special interface to access FAT directory file
    DirWriteL(aPos,TPtrC8((TUint8*)&aDirEntry,KSizeOfFatDirEntry));
    }

//-----------------------------------------------------------------------------------------

void CFatMountCB::EraseDirEntryL(const TEntryPos& aPos)
//
// Mark a dir entry as erased
//
    {

    __PRINT(_L("CFatMountCB::EraseDirEntryL"));
    if(!iLeafDirCache && iLastLeafDir)
        iLastLeafDir->Des().SetLength(0);

    //-- use special interface to access FAT directory file
    DirWriteL(aPos,TPtrC8((TUint8*)&KEntryErasedMarker,sizeof(TUint8)));
    }

//-----------------------------------------------------------------------------------------

void CFatMountCB::ReadDirEntryL(const TEntryPos& aPos,TFatDirEntry& aDirEntry) const
//
// Read a FAT directory entry to disk
//
    {

//  __PRINT(_L("CFatMountCB::ReadDirEntryL"));
    if (IsEndOfClusterCh(aPos.iCluster))
        {
        aDirEntry.InitZ();
        return;
        }
    TPtr8 buf=TPtr8((TUint8*)&aDirEntry,KSizeOfFatDirEntry);

    //-- use special interface to access FAT directory file
    DirReadL(aPos,KSizeOfFatDirEntry,buf);
    }

//-----------------------------------------------------------------------------------------

/**
    Enlarge the disk's size.
    This method can be called only for variable size media, i.e. RAM drive

    @param aSize size increment (bytes)
*/
void CFatMountCB::EnlargeL(TInt aSize)
    {
    __PRINT2(_L("CFatMountCB::EnlargeL by 0x%x currentsize=0x%x"),aSize,iSize);

    ASSERT(iRamDrive);

    TInt maxSize;
    if (HAL::Get(HAL::EMaxRAMDriveSize, maxSize) == KErrNone && iSize + aSize > maxSize)
        User::Leave(KErrDiskFull);
    User::LeaveIfError(LocalDrive()->Enlarge(aSize));
    iSize+=aSize;

    if (&FAT())
        {
        FAT().InitializeL();
        }

    if (&RawDisk())
        {
        RawDisk().InitializeL();
        }

    }

//-----------------------------------------------------------------------------------------

void CFatMountCB::ReduceSizeL(TInt aPos,TInt aLength)
//
// Reduce the disk's size
//
    {

    __PRINT2(_L("CFatMountCB::ReduceSizeL aPos=0x%x aLength=0x%x"),aPos,aLength);
    User::LeaveIfError(LocalDrive()->ReduceSize(aPos,aLength));
    iSize-=aLength;
    }

//-----------------------------------------------------------------------------------------

TInt64 CFatMountCB::MakeLinAddrL(const TEntryPos& aPos) const
//
// Convert cluster/position into linear address
//
    {

    //__PRINT2(_L("CFatMountCB::MakeLinAddrL, cl:%d, pos:%d"), aPos.iCluster, aPos.iPos);
    if (!IsRootDir(aPos))
        {
        TInt relPos=ClusterRelativePos(aPos.iPos);
        return FAT().DataPositionInBytes(aPos.iCluster)+relPos;
        }
    if (aPos.iPos+StartOfRootDirInBytes()>=RootDirEnd())
        User::Leave(KErrDirFull); // Past last root dir entry
    return StartOfRootDirInBytes()+aPos.iPos;
    }

//-----------------------------------------------------------------------------------------

void CFatMountCB::GetShortNameL(const TDesC& aLongName,TDes& aShortName)
//
// Get the short name associated with a long file name
//
    {
    __PRINT(_L("CFatMountCB::GetShortNameL"));
    TEntryPos firstEntryPos(RootIndicator(),0);
    TFatDirEntry firstEntry;
    FindEntryStartL(aLongName,KEntryAttMaskSupported,firstEntry,firstEntryPos);
    MoveToDosEntryL(firstEntryPos,firstEntry);
    TBuf8<0x20> dosName(DosNameFromStdFormat(firstEntry.Name()));
    LocaleUtils::ConvertToUnicodeL(aShortName, dosName);
    }

//-----------------------------------------------------------------------------------------

void CFatMountCB::GetLongNameL(const TDesC& aShortName,TDes& aLongName)
//
// Get the long name associated with a short file name
//
    {
    __PRINT(_L("CFatMountCB::GetLongNameL"));
    TEntryPos pos(RootIndicator(),0);
    TFatDirEntry entry;
    const TInt namePos=aShortName.LocateReverse(KPathDelimiter)+1; // There is always a path delimiter
    const TPtrC shortNameWithoutPathDelimiter(aShortName.Mid(namePos));
    __ASSERT_ALWAYS(shortNameWithoutPathDelimiter.Length()<=12,User::Leave(KErrBadName));

    TLeafDirData leafDir;
    pos.iCluster=FindLeafDirL(aShortName.Left(namePos), leafDir);
    FOREVER
        {
        TFatDirEntry startEntry;
        User::LeaveIfError(GetDirEntry(pos,entry,startEntry,aLongName));
        if (entry.IsEndOfDirectory())
            User::Leave(KErrNotFound);
        TBool entryIsVFat=EFalse;
        if (startEntry.IsVFatEntry())
            entryIsVFat=ETrue;
        if (!entry.IsParentDirectory() && !entry.IsCurrentDirectory() && !entry.IsGarbage() && !entry.IsErased())
            {
            TBuf8<0x20> entryName8(DosNameFromStdFormat(entry.Name()));
            TBuf<0x20> entryName;
            LocaleUtils::ConvertToUnicodeL(entryName, entryName8);
            if (shortNameWithoutPathDelimiter.MatchF(entryName)!=KErrNotFound)
                {
                if (entryIsVFat==EFalse)
                    aLongName=shortNameWithoutPathDelimiter;
                return;
                }
            }
        MoveToNextEntryL(pos);
        }
    }



//-----------------------------------------------------------------------------------------

/**
    Extend a file or directory, zeroing cluster chain and flushing after every write to FAT.
    This method is called for rugged FAT only.
    for parameters see CFatTable::ExtendClusterListL
*/
void CFatMountCB::ExtendClusterListZeroedL(TInt aNumber,TInt& aCluster)
    {
    __PRINT(_L("CFatMountCB::ExtendClusterListZeroedL"));
    __ASSERT_DEBUG(aNumber>0,Fault(EFatBadParameter));

    while(aNumber && FAT().GetNextClusterL(aCluster))
        aNumber--;

    //-- request aNumber free clusters from the FAT, this request may wait until FAT scan thread counted enough free clusters if it is running.
    if(!FAT().RequestFreeClusters(aNumber))
        {
        __PRINT(_L("CFatMountCB::ExtendClusterListL - leaving KErrDirFull"));
        User::Leave(KErrDiskFull);
        }
    while (aNumber--)
        {
        TInt freeCluster=FAT().AllocateSingleClusterL(aCluster);
        FAT().FlushL();
        ZeroDirClusterL(freeCluster);
        FAT().WriteL(aCluster,freeCluster);
        FAT().FlushL();
        aCluster=freeCluster;
        }
    }

//-----------------------------------------------------------------------------------------

#if defined(_DEBUG)
TInt CFatMountCB::ControlIO(const RMessagePtr2& aMessage,TInt aCommand,TAny* aParam1,TAny* aParam2)
//
// Debug function
//
    {
    if(aCommand>=EExtCustom)
        {
        if(LocalDrive())
            return LocalDrive()->ControlIO(aMessage,aCommand-EExtCustom,aParam1,aParam2);
        else
            return KErrNotSupported;
        }
    switch(aCommand)
        {
        case ECriticalWriteFailOn:
            {
            TInt r;
            TInt16 args[2];
            TPtr8 des((TUint8*)args,4,4);
            TRAP(r,aMessage.ReadL(2,des,0));
            if(r!=KErrNone)
                return(r);
            SetWriteFail(ETrue);
            SetWriteFailCount(args[0]);
            SetWriteFailError(args[1]);
            break;
            }
        case ECriticalWriteFailOff:SetWriteFail(EFalse);break;
        case ERuggedFSysOn: SetRuggedFSys(ETrue);break;
        case ERuggedFSysOff: SetRuggedFSys(EFalse);break;
        case EIsRuggedFSys:
            {
            TInt r;
            TUint8 val = (IsRuggedFSys()!=0); // val = 0 or 1 for false/true
            TPtr8 pVal(&val,1,1);
            TRAP(r,aMessage.WriteL(2,pVal,0));
            if(r!=KErrNone)
                return(r);
            break;
            }
        case ELocalTimeForRemovableMediaOn:
            {
            FatFileSystem().SetUseLocalTime(ETrue);
            break;
            }
        case ELocalTimeForRemovableMediaOff:
            {
            FatFileSystem().SetUseLocalTime(EFalse);
            break;
            }
        case ELocalTimeUsedOnRemovableMedia:
            {
            TBool flag = FatFileSystem().GetUseLocalTime();
            TPckgC<TBool> flagPckg(flag);
            TInt r = aMessage.Write(2, flagPckg);
            if(r!=KErrNone)
                return r;
            break;
            }
        case ECreationTime:
            {
            CheckStateConsistentL();

            TEntryPos firstEntryPos(RootIndicator(),0);
            TFatDirEntry firstEntry;
            //RFs::ControlIO restricts you to use narrow descriptors
            //so convert narrow back to wide.
            TBuf8<KMaxPath> fileNameNarrow;
            aMessage.Read(2, fileNameNarrow);

            TFileName fileNameWide;
            fileNameWide.Copy(fileNameNarrow);

            //find the long file name entry
            TRAPD(r, FindEntryStartL(fileNameWide,KEntryAttMaskSupported,firstEntry,firstEntryPos) );
            if(r!=KErrNone)
              return(r);
            //Find the corresponding 8.3 short name entry, for metadata
            MoveToDosEntryL(firstEntryPos,firstEntry);
            TTime creationTime=0;
            TPckg<TTime> timePckg(creationTime);
            SFatDirEntry* sEntry = reinterpret_cast<SFatDirEntry*>(firstEntry.iData);
            creationTime = DosTimeToTTime(sEntry->iTimeC, sEntry->iDateC);
            r = aMessage.Write(3, timePckg);
            if(r!=KErrNone)
                return r;
            break;
            }
        case EDisableFATDirCache:
            {
            MWTCacheInterface* pDirCache = iRawDisk->DirCacheInterface();
            TUint32 KEDisableFATDirCache = CDynamicDirCache::EDisableCache;
            pDirCache->Control(KEDisableFATDirCache, (TUint32) aParam1, NULL);
            break;
            }
        case EDumpFATDirCache:
            {
            MWTCacheInterface* pDirCache = iRawDisk->DirCacheInterface();
            TUint32 KEDumpFATDirCache = CDynamicDirCache::EDumpCache;
            pDirCache->Control(KEDumpFATDirCache, 0, NULL);
            break;
            }
        case EFATDirCacheInfo:
            {
            MWTCacheInterface* pDirCache = iRawDisk->DirCacheInterface();
            TUint32 KEFATDirCacheInfo = CDynamicDirCache::ECacheInfo;
            pDirCache->Control(KEFATDirCacheInfo, 0, NULL);
            break;
            }


        default: return(KErrNotSupported);
        }
    return(KErrNone);
    }
#else
TInt CFatMountCB::ControlIO(const RMessagePtr2& /*aMessage*/,TInt /*aCommand*/,TAny* /*aParam1*/,TAny* /*aParam2*/)
    {return(KErrNotSupported);}
#endif


//-----------------------------------------------------------------------------------------

TInt CFatMountCB::Lock(TMediaPassword& aOld,TMediaPassword& aNew,TBool aStore)
//
// lock media device
//
    {
    __PRINT(_L("CFatMountCB::Lock"));
    TInt r=CreateDrive(Drive().DriveNumber());
    if (r!=KErrNone)
        return r;

    TBusLocalDrive* local;
    r=LocalDrive()->GetLocalDrive(local);
    if (r!=KErrNone)
        return r;

#ifdef _LOCKABLE_MEDIA
    if(local->Status()==KErrLocked)
        local->Status() = KErrNotReady;
#endif
    r=local->SetPassword(aOld,aNew,aStore);
    if(r==KErrNone&&aStore)
        WritePasswordData();
    return(r);
    }

//-----------------------------------------------------------------------------------------

TInt CFatMountCB::Unlock(TMediaPassword& aPassword,TBool aStore)
//
// Unlock media device
//
    {
    __PRINT(_L("CFatMountCB::Unlock"));
    TInt r=CreateDrive(Drive().DriveNumber());
    if (r!=KErrNone)
        return r;

    TBusLocalDrive* local;
    r=LocalDrive()->GetLocalDrive(local);
    if (r!=KErrNone)
        return r;

#ifdef _LOCKABLE_MEDIA
    if(local->Status()==KErrLocked)
        local->Status() = KErrNotReady;
#endif
    r=local->Unlock(aPassword,aStore);
    if(r==KErrNone&&aStore)
        WritePasswordData();
    return(r);
    }

//-----------------------------------------------------------------------------------------

TInt CFatMountCB::ClearPassword(TMediaPassword& aPassword)
//
// Clear password from media device
//
    {
    __PRINT(_L("CFatMountCB::ClearPassword"));
    TInt r=CreateDrive(Drive().DriveNumber());
    if (r!=KErrNone)
        return r;

    TBusLocalDrive* local;
    r=LocalDrive()->GetLocalDrive(local);
    if (r!=KErrNone)
        return r;

#ifdef _LOCKABLE_MEDIA
    if(local->Status()==KErrLocked)
        local->Status() = KErrNotReady;
#endif
    r=local->Clear(aPassword);
    if(r==KErrNone)
        WritePasswordData();
    return(r);
    }

//-----------------------------------------------------------------------------------------

TInt CFatMountCB::ErasePassword()
//
// Forcibly erase the password from a media device
//
    {
    __PRINT(_L("CFatMountCB::ErasePassword"));

    TInt r=CreateDrive(Drive().DriveNumber());
    if (r!=KErrNone)
        return r;

    TBusLocalDrive* local;
    r=LocalDrive()->GetLocalDrive(local);
    if (r!=KErrNone)
        return r;

#ifdef _LOCKABLE_MEDIA
    if(local->Status()==KErrLocked)
        local->Status() = KErrNotReady;
#endif
    r=local->ErasePassword();
    if(r==KErrNone)
        {
        // ...media change to ensure a fresh remount the drive
        r = local->ForceRemount(0);
        local->Status() = KErrNotReady;
        WritePasswordData();
        }
    return(r);
    }

//-----------------------------------------------------------------------------------------

TInt CFatMountCB::ForceRemountDrive(const TDesC8* aMountInfo,TInt aMountInfoMessageHandle,TUint aFlags)
//
// Force a remount of the drive
//
    {
    __PRINT(_L("CFatMountCB::ForceRemountDrive"));
    TInt r=CreateDrive(Drive().DriveNumber());
    if (r==KErrNone)
        r=LocalDrive()->SetMountInfo(aMountInfo,aMountInfoMessageHandle);
    if (r==KErrNone)
        r=LocalDrive()->ForceRemount(aFlags);
    return(r);
    }

//-----------------------------------------------------------------------------------------

void CFatMountCB::WritePasswordData()
//
// Write store password data to disk
//
    {
    __PRINT(_L("CFatMountCB::WritePasswordData"));
    TBuf<sizeof(KMediaPWrdFile)> mediaPWrdFile(KMediaPWrdFile);
    mediaPWrdFile[0] = (TUint8) RFs::GetSystemDriveChar();
    __PRINT1TEMP(_L("disk file = %S"),mediaPWrdFile);
    TBusLocalDrive& local=GetLocalDrive(Drive().DriveNumber());
    TInt length=local.PasswordStoreLengthInBytes();
    if(length==0)
        {
        WriteToDisk(mediaPWrdFile,_L8(""));
        return;
        }
    HBufC8* hDes=HBufC8::New(length);
    if(hDes==NULL)
        return;
    TPtr8 pDes=hDes->Des();
    TInt r=local.ReadPasswordData(pDes);
    if(r==KErrNone)
        WriteToDisk(mediaPWrdFile,pDes);
    delete hDes;
    }

//-----------------------------------------------------------------------------------------

/**
Trim trailing spaces of volume label descriptor and adjust its length
*/
void CFatMountCB::TrimVolumeLabel(TDes8& aLabel) const
    {
    // Locate first '\0'
    TInt nullPos = aLabel.Locate('\0');
    if (nullPos == KErrNotFound)
        nullPos = KVolumeLabelSize;

    // Trim trailing spaces
    TInt i;
    for (i=nullPos-1; i>=0; --i)
        if (aLabel[i] != 0x20)
            break;
    aLabel.SetLength(i+1);
    }

//-----------------------------------------------------------------------------------------

/**
Searches for the volume label file

@param aLabel The name of the volume label file returned upon successful search
@return KErrNone if it finds the volume label file, otherwise KErrNotFound
*/
TInt CFatMountCB::ReadVolumeLabelFile(TDes8& aLabel)
    {
    __PRINT(_L("+CFatMountCB::ReadVolumeLabelFile"));
    TEntryPos pos(RootIndicator(),0);
    TFatDirEntry entry;
    TRAPD(r, FindVolumeLabelFileL(aLabel, pos, entry));
    __PRINT1(_L("-CFatMountCB::ReadVolumeLabelFile: %d"),r);
    return r;
    }

//-----------------------------------------------------------------------------------------

/**
Creates or updates the volume label file with name aNewName

@param aNewName The new name for the volume label file
*/
void CFatMountCB::WriteVolumeLabelFileL(const TDesC8& aNewName)
    {
    __PRINT1(_L("+CFatMountCB::WriteVolumeLabelFileL: [%S]"), &aNewName);
    TEntryPos pos(RootIndicator(),0);
    TFatDirEntry entry;

    TBuf8<KVolumeLabelSize> oldName;
    TRAPD(r, FindVolumeLabelFileL(oldName, pos, entry));

    if( KErrNone == r )
        {
        // Found existing volume label file, rename or delete
        if(oldName == aNewName)
            {
            __PRINT(_L("-CFatMountCB::WriteVolumeLabelFileL: found: names match"));
            return;
            }
        else
            {
            if(aNewName.Length() == 0)
                {
                // delete the volume label file
                __PRINT(_L("CFatMountCB::WriteVolumeLabelFileL: found: delete"));
                EraseDirEntryL(pos, entry);
                }
            else
                {
                __PRINT(_L("CFatMountCB::WriteVolumeLabelFileL: found: replace"));
                entry.SetName(aNewName);
                WriteDirEntryL(pos, entry);
                }
            FAT().FlushL();
            }
        }
    else if( KErrNotFound == r )
        {
        // Not found, need to create if aNewName is not empty
        // Windows allows a volume label file to have the same name as
        // an existing file or directory
        if(aNewName.Length() > 0)
            {
            __PRINT(_L("CFatMountCB::WriteVolumeLabelFileL: not found: create"));
            TEntryPos dirPos(RootIndicator(),0);
            AddDirEntryL(dirPos,1);
            TFatDirEntry fatDirEntry;
            fatDirEntry.SetName(aNewName);
            fatDirEntry.SetAttributes(KEntryAttVolume);

            TTime now;
            now.UniversalTime();
            fatDirEntry.SetTime(now, TimeOffset() );
            fatDirEntry.SetStartCluster(0);
            fatDirEntry.SetSize(0);
            WriteDirEntryL(dirPos, fatDirEntry);
            FAT().FlushL();
            }
        }
    else
        {
        // Some other error
        User::Leave(r);
        }
    }


//-----------------------------------------------------------------------------------------

/**
Scans the root directory for a volume label file. Leaves with an error if not found

@param aLabel Name of the volume label file upon successful search
@param aDosEntryPos Pointer to position of the volume label file upon successful search
@param aDosEntry Contains the entry for the volume label file upon successful search
*/
void CFatMountCB::FindVolumeLabelFileL(TDes8& aLabel, TEntryPos& aDosEntryPos, TFatDirEntry& aDosEntry)
    {
    __PRINT(_L("+CFatMountCB::FindVolumeLabelFileL"));

    if(IsRootDir(aDosEntryPos) && (aDosEntryPos.iPos+StartOfRootDirInBytes()>=RootDirEnd()))
        {
        __PRINT(_L("-CFatMountCB::FindVolumeLabelFileL: abort, exceeds root"));
        User::Leave(KErrNotFound); // Allows maximum number of entries in root directory
        }

    TInt previousCluster= aDosEntryPos.iCluster;
    TUint previousPosition= aDosEntryPos.iPos;
    TInt changePreviousCluster=1;
    TInt count=0;

    TFatDirEntry startEntry;
    TFileName dummyLongName;

    FOREVER
        {
#ifdef _DEBUG
        const TInt e= GetDirEntry(aDosEntryPos, aDosEntry, startEntry, dummyLongName);
        __PRINT1(_L("CFatMountCB::FindVolumeLabelFileL: GetDir %d"), e);
        User::LeaveIfError(e);
#else
        User::LeaveIfError(GetDirEntry(aDosEntryPos, aDosEntry, startEntry, dummyLongName));
#endif
        if(aDosEntry.IsEndOfDirectory())
            {
            __PRINT(_L("-CFatMountCB::FindVolumeLabelFileL: end of dir"));
            User::Leave(KErrNotFound);
            }
        if(IsRootDir(aDosEntryPos) && (aDosEntryPos.iPos+StartOfRootDirInBytes()==(RootDirEnd()-KSizeOfFatDirEntry)))
            {
            if(aDosEntry.IsErased())
                {
                __PRINT(_L("-CFatMountCB::FindVolumeLabelFileL: erased end of root"));
                User::Leave(KErrNotFound); //Allows maximum number of entries in root directory
                }
            }
        if(!aDosEntry.IsCurrentDirectory() && !aDosEntry.IsParentDirectory() && !aDosEntry.IsErased() && !aDosEntry.IsGarbage())
            {
            if(aDosEntry.Attributes() & KEntryAttVolume)
                {
                aLabel = aDosEntry.Name();
#ifdef _DEBUG
                dummyLongName.Copy(aLabel);
                __PRINT1(_L("-CFatMountCB::FindVolumeLabelFileL: found [%S]"), &dummyLongName);
#endif
                break;
                }
            }
        MoveToNextEntryL(aDosEntryPos);
        if(IsRootDir(aDosEntryPos) && (aDosEntryPos.iPos+StartOfRootDirInBytes()>=RootDirEnd()))
            {
            __PRINT(_L("-CFatMountCB::FindVolumeLabelFileL: passed end of root"));
            User::Leave(KErrNotFound); //Allows maximum number of entries in root directory
            }
        if(aDosEntryPos.iCluster && (aDosEntryPos.iPos <= previousPosition))
            {
            DoCheckFatForLoopsL(aDosEntryPos.iCluster, previousCluster, changePreviousCluster, count);
            }
        previousPosition=aDosEntryPos.iPos;
        }
    }

//-----------------------------------------------------------------------------------------

/**
Read volume label from disk and trim trailing 0x20 & 0x00 characters
*/
void CFatMountCB::GetVolumeLabelFromDiskL(const TFatBootSector& aBootSector)
    {
    // Read volume label as 8 bit descriptor
    TBuf8<KVolumeLabelSize> volName8;
    TInt r = ReadVolumeLabelFile(volName8);
    if(r != KErrNone)   // No volume label file in root directory
        volName8 = aBootSector.VolumeLabel();
    TrimVolumeLabel(volName8);

    TBuf16<KVolumeLabelSize> volName16;
    LocaleUtils::ConvertToUnicodeL(volName16, volName8);
    SetVolumeName(volName16.AllocL());
    }


//-----------------------------------------------------------------------------------------

/**
Populates iMap member of aInfo with contiguous block group maps.

@param aPos     Start position for a desired section of the file.
@param sLength  Length of the desired data to produce the block map for.
@param aInfo    A structure describing a group of block maps.
*/
void CFatMountCB::BlockMapReadFromClusterListL(TEntryPos& aPos, TInt aLength, SBlockMapInfo& aInfo)
    {
    __PRINT(_L("CFatMountCB::BlockMapReadFromClusterListL"));
    __ASSERT_ALWAYS(aPos.Cluster()>=KFatFirstSearchCluster,User::Leave(KErrCorrupt));
    TBlockMapEntry blockMapEntry;

    TUint i = 0;
    TInt clusterRelativePos;
    TInt maxClusters;
    TInt endCluster;
    TInt clusterListLen;
    TInt readLength;
    TInt temp;
    TInt currentPos;
    TLocalDriveCapsBuf caps;
    TInt r;
    TInt64 realPosition = 0;

    do
        {
        currentPos = aPos.iPos;
        temp = currentPos>>ClusterSizeLog2();
        if ( (currentPos) && ( (currentPos) == (temp<<ClusterSizeLog2()) ) )
            {
            if (!FAT().GetNextClusterL(aPos.iCluster))
                {
                __PRINT(_L("CFatMountCB::BlockMapReadFromClusterListL corrupt#1"))
                User::Leave(KErrCorrupt);
                }
            }
        clusterRelativePos = ClusterRelativePos( aPos.iPos );
        maxClusters = ((aLength + clusterRelativePos - 1)>>ClusterSizeLog2())+1;
        clusterListLen = FAT().CountContiguousClustersL(aPos.iCluster, endCluster, maxClusters);
        readLength = Min( aLength, (clusterListLen<<ClusterSizeLog2()) - clusterRelativePos);

        blockMapEntry.SetNumberOfBlocks( clusterListLen );
        if (aPos.iCluster < 2)
            User::Leave(KErrCorrupt);
        r = LocalDrive()->Caps(caps);
        if ( r != KErrNone )
            User::LeaveIfError(r);
        if ( caps().iType&EMediaRam )
            {
            realPosition = FAT().DataPositionInBytes( aPos.iCluster );
            aPos.iCluster = I64LOW((realPosition - aInfo.iStartBlockAddress)>>ClusterSizeLog2());
            blockMapEntry.SetStartBlock( aPos.iCluster );
            }
        else
            blockMapEntry.SetStartBlock( aPos.iCluster - 2);
        aInfo.iMap.Append(TPckgC<TBlockMapEntry>(blockMapEntry));
        aPos.iPos += readLength;
        aPos.iCluster = endCluster;
        aLength -= readLength;
        }
    while( ( aLength > 0 ) && ( ++i < KMaxMapsPerCall ) );
    }


//-----------------------------------------------------------------------------------------

TInt CFatMountCB::GetDosEntryFromNameL(const TDesC& aName, TEntryPos& aDosEntryPos, TFatDirEntry& aDosEntry)
    {
    TFatDirEntry firstEntry;
    TEntryPos firstEntryPos(RootIndicator(),0); // Already checked entry is a directory
    FindEntryStartL(aName,KEntryAttMaskSupported,firstEntry,firstEntryPos);

    aDosEntryPos=firstEntryPos;
    aDosEntry=firstEntry;
    MoveToDosEntryL(aDosEntryPos,aDosEntry);

    return KErrNone;
    }

//-----------------------------------------------------------------------------------------

TInt CFatMountCB::GetFileUniqueId(const TDesC& aName, TInt64& aUniqueId)
    {
    // Get first cluster of file
    TEntryPos dosEntryPos(RootIndicator(),0);
    TFatDirEntry dosEntry;
    InitializeRootEntry(dosEntry);  // Nugatory initialisation to placate warnings
    TRAPD(err,GetDosEntryFromNameL(aName,dosEntryPos,dosEntry));
    if(err!=KErrNone)
        return err;
    TInt startCluster=StartCluster(dosEntry);
    // Empty files will return a cluster of zero
    if(startCluster==0)
        return KErrEof;
    aUniqueId=MAKE_TINT64(0,startCluster);
    return KErrNone;
    }
//-----------------------------------------------------------------------------------------


TInt CFatMountCB::Spare3(TInt /*aVal*/, TAny* /*aPtr1*/, TAny* /*aPtr2*/)
    {
    return KErrNotSupported;
    }

TInt CFatMountCB::Spare2(TInt /*aVal*/, TAny* /*aPtr1*/, TAny* /*aPtr2*/)
    {
    return KErrNotSupported;
    }

TInt CFatMountCB::Spare1(TInt /*aVal*/, TAny* /*aPtr1*/, TAny* /*aPtr2*/)
    {
    return KErrNotSupported;
    }

//-----------------------------------------------------------------------------------------

//-- maximal level of recursion for the CheckDisk. i.e. the max. number of folded directories to check.
const TInt KCheckDskMaxRecursionLevel = 50;

//
// Walks a directory cluster list then checks all the entries.
//
void  CFatMountCB::ChkDirL(RBitVector& aFatBitVec, TInt aDirCluster)
    {
    //__PRINT1(_L("### CFatMountCB::ChkDirL() level:%d"),iChkDiscRecLevel);

    //-- check if we have reached the recursion limit. on hardware the stack is very limited
    //-- and its overflow will lead to crash.
    if(iChkDiscRecLevel++ >= KCheckDskMaxRecursionLevel)
        {
         __PRINT1(_L("CFatMountCB::ChkDirL() max recursion level(%d) reached. Leaving!"),iChkDiscRecLevel);
         User::Leave(KErrTooBig);
        }

    if(/*Is32BitFat() &&*/aDirCluster != 0 && (aDirCluster == RootIndicator()))//the bit in comments maybe required
        WalkClusterListL(aFatBitVec, RootIndicator());

    TEntryPos entryPos(aDirCluster,0);
    FOREVER
        {
        TFatDirEntry entry;
        ReadDirEntryL(entryPos,entry);
        MoveToDosEntryL(entryPos,entry);
        if (entry.IsEndOfDirectory())
            break;
        if (IsRootDir(entryPos)&&(StartOfRootDirInBytes()+entryPos.iPos==(RootDirEnd()-KSizeOfFatDirEntry)))
            {
            if(!entry.IsErased())
                ChkEntryL(aFatBitVec, entry);
            break;  //  Allows maximum number of entries in root directory
            }
        MoveToNextEntryL(entryPos);
        if (entry.IsParentDirectory() || entry.IsCurrentDirectory() || entry.IsErased())
            continue;
        ChkEntryL(aFatBitVec, entry);
        }

    iChkDiscRecLevel--;
    }

//-----------------------------------------------------------------------------------------

//
// Check FAT is valid for anEntry
//
void  CFatMountCB::ChkEntryL(RBitVector& aFatBitVec, const TFatDirEntry& anEntry)
    {
    TInt listLength=0;

    if ((anEntry.Attributes()&(KEntryAttDir)) || anEntry.Size())
        listLength=WalkClusterListL(aFatBitVec, StartCluster(anEntry));
    else if (anEntry.StartCluster() != 0)        // zero length file
        User::Leave(EFatChkDskInvalidEntrySize); // shouldn't have clusters

    if (anEntry.Attributes()&KEntryAttDir)
        ChkDirL(aFatBitVec, StartCluster(anEntry));

    //  Check that the correct number of clusters have been allocated for the size of the file.

    else if ((anEntry.Attributes()&KEntryAttVolume)==0)
        {
        TInt clustersForFileSize;
        TInt clusterSize=1<<ClusterSizeLog2();
        clustersForFileSize = (TInt) ( (TInt64(anEntry.Size()) + TInt64(clusterSize-1)) >> ClusterSizeLog2() );

        if (listLength!=clustersForFileSize)
            User::Leave(EFatChkDskInvalidEntrySize);
        }
    }

//-----------------------------------------------------------------------------------------

//
// Walks cluster list from aCluster to EOF
// Reports an error if an invalid cluster is found before EOF or
// a cluster has been visited before.
//
TInt  CFatMountCB::WalkClusterListL(RBitVector& aFatBitVec, TInt aCluster)
    {

    TInt i=0;
    do  {
        i++;
        if (!ValidClusterNumber(aCluster))
            {
            __PRINT1(_L("Bad Cluster number %d"),aCluster);
            User::Leave(EFatChkDskIllegalClusterNumber);
            }

        if (IsClusterVisited(aFatBitVec, aCluster))
            {
            __PRINT1(_L("Cluster already in use %d"),aCluster);
            User::Leave(EFatChkDskClusterAlreadyInUse);
            }

        MarkClusterVisited(aFatBitVec, aCluster);

        } while (FAT().GetNextClusterL(aCluster));

    return(i);
    }

//-----------------------------------------------------------------------------------------

//
// Checks that all unvisited clusters are marked as free in the FAT
//
void  CFatMountCB::CheckUnvisitedClustersL(const RBitVector& aFatBitVec) const
    {

    TInt cluster=2;
    TInt maxCluster=cluster + UsableClusters();
    while (cluster<maxCluster)
        {
        cluster=NextUnvisitedCluster(aFatBitVec, cluster); //-- move to the next unvisited cluster
        if(cluster < 0 || cluster >= maxCluster)
            break;

        TInt clusterVal=FAT().ReadL(cluster);
        if (clusterVal!=0 && IsEndOfClusterCh(clusterVal)==EFalse && !IsBadCluster(clusterVal))
            {
            __PRINT1(_L("\n*****Bad cluster Num = %d"),cluster);
            User::Leave(EFatChkDskBadCluster);
            }
        }
    }

//-----------------------------------------------------------------------------------------

/**
@param  aCluster cluster number to check for validity
@returns ETrue if aCluster is a valid cluster number
*/
TBool CFatMountCB::ValidClusterNumber(TUint32 aCluster) const
    {
    return (aCluster>=KFatFirstSearchCluster && aCluster<=MaxClusterNumber());
    }

//-----------------------------------------------------------------------------------------

//
// Walk the FAT, returns error if find an unterminated list or
// lists that merge.
//
TInt CFatMountCB::CheckDisk()
    {

    __PRINT1(_L("CFatMountCB::CheckDisk() drv:%d"), DriveNumber());

    if(!ConsistentState())
        return KErrCorrupt;

    //-- create a bit representation of the FAT
    const TUint32 MaxClusters = UsableClusters()+KFatFirstSearchCluster; //-- UsableClusters() doesn't count first 2 unused clusers


    // cluster count may be zero if boot sector failed to be read (e.g. if the media is locked)
    // or if TDrive::MountMedia(ETrue) has been called (in which case the boot sector may
    if (MaxClusters == 0)
        return KErrCorrupt;

    RBitVector bitVec; //-- each bit in this vector represents a FAT cluster

    TInt nRes = bitVec.Create(MaxClusters);
    if(nRes != KErrNone)
        {
        ASSERT(nRes == KErrNoMemory); //-- the only one possible reason.
        return KErrNoMemory;
        }

    iChkDiscRecLevel = 0; //-- reset CheckDisk recursion counter
    TRAPD(r,ChkDirL(bitVec, RootIndicator())); // Check from root directory
    if (r==KErrNone)
        {
        TRAP(r,CheckUnvisitedClustersL(bitVec));
        }


    bitVec.Close();

    switch(r)
        {

    case KErrNone:
        return KErrNone;

    case EFatChkDskIllegalClusterNumber:
        return(1);

    case EFatChkDskClusterAlreadyInUse:
        return(2);

    case EFatChkDskBadCluster:
        return(3);

    case EFatChkDskInvalidEntrySize:
        return(4);

    default:
        break;
        }

    return(r);
    }


//-----------------------------------------------------------------------------------------
//  Helper functions for Check Disk functionality
//-----------------------------------------------------------------------------------------

/**
    Find the next unvisited cluster number in the bit array.

    @param  aBitList    bit array, where '0' bits represent unvisited clusters.
    @param  aCluster    cluster number to start search with.

    @return positive integer indicating next unvisited cluster number
            KErrNotFound (-1) if there are no unvisited clusters

*/
static TInt  NextUnvisitedCluster(const RBitVector& aFatBitVec, TUint32 aCluster)
{
    __ASSERT_DEBUG(aCluster >= KFatFirstSearchCluster, Fault(EFatChkDskIllegalClusterNumber)); //-- 1st 2 FAT entries are reserved

    TUint32 searchPos = aCluster; //-- bit number to start search with

    //-- look for the unvisited cluster (bit '0') in the bit array from the searchPos to the right
    if(aFatBitVec.Find(searchPos, 0, RBitVector::ERight))
        return searchPos;

    return KErrNotFound;
}


/**
    Check if we have visited cluster aCluster

    @param  aFatBitVec  bit array, where '0' bits represent unvisited clusters.
    @param  aCluster    cluster number to check
    @return ETrue if aCluster has been visited.
*/
static TBool IsClusterVisited(const RBitVector& aFatBitVec, TUint32 aCluster)
{
    __ASSERT_DEBUG(aCluster >= KFatFirstSearchCluster, Fault(EFatChkDskIllegalClusterNumber)); //-- 1st 2 FAT entries are reserved

    return aFatBitVec[aCluster];
}

/**
    Mark aCluster as visited
    @param  aFatBitVec  bit array, where '0' bits represent unvisited clusters.
    @param  aCluster    cluster number to mark
*/
static void MarkClusterVisited(RBitVector& aFatBitVec, TUint32 aCluster)
{
    __ASSERT_DEBUG(aCluster >= KFatFirstSearchCluster, Fault(EFatChkDskIllegalClusterNumber)); //-- 1st 2 FAT entries are reserved

    aFatBitVec.SetBit(aCluster); //-- '1' bit in a bit array means that the corresponding cluster is visited
}



//-------------------------------------------------------------------------------------------------------------------

/**
    Creates a scan drive object and starts the scan.
*/
TInt CFatMountCB::DoRunScanDrive()
{
    TInt nRes;

    CScanDrive* pScnDrv = NULL;
    TRAP(nRes, pScnDrv=CScanDrive::NewL(this));
    if(nRes != KErrNone)
        return nRes;

    TRAPD(nScnDrvRes, pScnDrv->StartL());

    const TBool bNeedFatRemount = (nScnDrvRes!=KErrNone) || pScnDrv->ProblemsDiscovered();
    delete pScnDrv;


    if(bNeedFatRemount)
        {//-- ScanDrive found and probably fixed some errors.
        // ensure cached fat and free cluster count are updated
        DoDismount(); //-- dismount
        TRAP(nRes, MountL(EFalse)); //-- mount again
        }

    if(nScnDrvRes != KErrNone)
        return nScnDrvRes;


    //-- if ScanDrive hasn't found anything wrong or has fixed recoverable erros, mark the volume clean
    if(VolCleanFlagSupported())
        {
        //-- if there is a background FAT scanning thread, we need to wait until it finishes its work.
        //-- otherwise it's possible to have incorrect amount of free space on the volume until next remounting.
        (void)FAT().NumberOfFreeClusters(ETrue);
        TRAP(nRes, FinaliseMountL());
        ASSERT(nRes == KErrNone);
        }

    return KErrNone;
}

//-------------------------------------------------------------------------------------------------------------------

/**
    Run scan drive on the given volume.
    The ScanDrive may be skipped on the finalised volumes, i.e. those, that had been shut down properly.


    @return Either  KErrCorrupt if an error was found that is not caused by write failure due to power removal.
                    KErrNone if no error was found. One of four positive codes explaining what type of error was rectified
*/
TInt CFatMountCB::ScanDrive()
{
    __PRINT1(_L("CFatMountCB::ScanDrive() starting on drive %d"), DriveNumber());

    if(!ConsistentState())
        return KErrCorrupt;

    TInt nRes;

    if(LockStatus()!=0)
        {
        __PRINT(_L("CFatMountCB::ScanDrive() locked!\n"));
        return KErrInUse;
        }

    if(iRamDrive)
        {//-- Do not check internal RAM drive
        __PRINT(_L("CFatMountCB::ScanDrive() Skipping Internal RAM drive."));
        return KErrNone;
        }

    //-- check if the volume is finalised and skip running ScanDrive if this option is enabled in estart.txt
    if(VolCleanFlagSupported() && FatConfig().ScanDrvSkipFinalisedVolume())
        {
        TBool bVolClean = EFalse;
        TRAP(nRes, bVolClean = VolumeCleanL());

        if(nRes == KErrNone && bVolClean)
            {
            __PRINT(_L("Skipping ScanDrive on finalised volume!"));
            return KErrNone; //-- skip ScanDrive on a clean volume
            }
        }

    //-- run ScanDrive
    nRes = Open();
    if(nRes != KErrNone)
        return nRes;

    nRes = DoRunScanDrive();

    Close();

    __PRINT2(_L("~ CFatMountCB::ScanDrive() finished for drive %d with the code %d"),DriveNumber(), nRes);

    return nRes;

}

//-----------------------------------------------------------------------------------------
/**
Returns the offset between UTC time and timestamps on the filesystem. This will return User::UTCOffset
if the flag iUseLocalTime has been set in CFatFileSystem and this mount is on a removable drive. If not
a null offset is returned.

@return The offset in seconds that timestamps on the filesystem have, relative to UTC.
*/
TTimeIntervalSeconds CFatMountCB::TimeOffset() const
    {
    if((Drive().Att() & KDriveAttRemovable) && FatFileSystem().GetUseLocalTime() )
        {
        return User::UTCOffset();
        }
    else
        {
        return TTimeIntervalSeconds(0);
        }
    }




//-----------------------------------------------------------------------------------------
/** 
    Check is this file system can be mounted on the drive at all.
    Just read and validate boot region, no real mounting overhead. 
    
    @return KErrNone    boot region is OK, the file system can be mounted.
            KErrLocked  the media is locked on a physical level.
            other error codes depending on the implementation

*/
TInt CFatMountCB::MntCtl_DoCheckFileSystemMountable()
    {
    TInt nRes;
    
    const TInt driveNo = Drive().DriveNumber();
    __PRINT1(_L("CFatMountCB::MntCtl_DoCheckFileSystemMountable() drv:%d"),driveNo);

    nRes = CreateDrive(driveNo);
    if(nRes != KErrNone)
        {
        __PRINT1(_L(" ..CreateDrive() err:%d \n"), nRes);    
        return nRes;
        }

    //-- try reading boot sector. This doesn't require iDriverInterface setup, it uses LocalDrive()
    TFatBootSector bootSector;
    nRes = ReadBootSector(bootSector);

    DismountedLocalDrive();

    return nRes;
    }



