// 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:
// f32\sfat32\sl_mnt32.cpp
// CFatMountCB code, specific to the EFAT32.FSY
// 
//

/**
 @file 
*/

#include "sl_std.h"
#include "sl_cache.h"
#include "sl_leafdir_cache.h"

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


/**
    Write aligned members of TFatBootSector to media
 
    @param  aMediaPos   media position the data will be written to
    @param  aBootSector data to write
    @return Media write error code
*/
TInt CFatMountCB::DoWriteBootSector(TInt64 aMediaPos, const TFatBootSector& aBootSector)  const
    {
    __PRINT2(_L("#- CFatMountCB::DoWriteBootSector() drv:%d, pos:0x%x"),Drive().DriveNumber(), (TUint32)aMediaPos);   

    ASSERT(aMediaPos>=0);

    TBuf8<KDefaultSectorSize> bootSecBuf(KDefaultSectorSize);
    bootSecBuf.FillZ();

    //-- externalize boot sector to the data buffer
    aBootSector.Externalize(bootSecBuf);

    //-- put a boot sector signature to the last 2 bytes
    bootSecBuf[KDefaultSectorSize-2] = 0x55;
    bootSecBuf[KDefaultSectorSize-1] = 0xaa;

    //-- write boot sector to the media
    TInt r=LocalDrive()->Write(aMediaPos, bootSecBuf);
    if (r!=KErrNone)
        {//-- write failure
        __PRINT2(_L("CFatMountCB::DoWriteBootSector() failed! drv:%d, code:%d"),Drive().DriveNumber(),r);
        }

    return r;
    }

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

/**
    Read non aligned boot data from media into TFatBootSector structure

    @param  aMediaPos   media position the data will be read from
    @param  aBootSector refrence to TFatBootSector populate
    @return Media read error code
*/
TInt CFatMountCB::DoReadBootSector(TInt64 aMediaPos, TFatBootSector& aBootSector)  const
    {
    __PRINT2(_L("#- CFatMountCB::DoReadBootSector() drv:%d, pos:0x%x"),Drive().DriveNumber(), (TUint32)aMediaPos);   

    ASSERT(aMediaPos>=0);

    TBuf8<KSizeOfFatBootSector> bootSecBuf(KSizeOfFatBootSector);
    
    //-- read boot sector from the media
    TInt r=LocalDrive()->Read(aMediaPos, KSizeOfFatBootSector, bootSecBuf);
    
    if (r != KErrNone)
        {
        __PRINT2(_L("CFatMountCB::DoReadBootSector() failed! drv:%d, code:%d"),Drive().DriveNumber(),r);

	    //-- fiddling with the error code; taken from MountL()
        if (r==KErrNotSupported)
		    return KErrNotReady;
    #if defined(_LOCKABLE_MEDIA)
	    else if(r==KErrLocked)
		    return KErrLocked;
    #endif
	    else if (r!=KErrNoMemory && r!=KErrNotReady && r!=KErrCorrupt && r!=KErrUnknown)
                return KErrCorrupt; 

        return r;
        }

    ASSERT(r==KErrNone);

    //-- initialise TFatBootSector object
    aBootSector.Internalize(bootSecBuf);

    //-- Validate the partition size, and fix up if the out of bounds
    TLocalDriveCapsV2Buf localDriveCaps;
    r = LocalDrive()->Caps(localDriveCaps);
    if (r != KErrNone)
        {
        //-- fiddling with the error code; taken from MountL()
        if (r!=KErrNoMemory && r!=KErrNotReady && r!=KErrCorrupt && r!=KErrUnknown)
            return KErrCorrupt;
        else
            return r;
        }

    if(!(localDriveCaps().iMediaAtt & KMediaAttVariableSize))
        {//-- this is not a RAM drive.
        const TUint32 maxSectors = I64LOW(localDriveCaps().iSize >> KDefSectorSzLog2);

        if(aBootSector.TotalSectors())
            aBootSector.SetTotalSectors(Min(aBootSector.TotalSectors(), maxSectors));
        else
            aBootSector.SetHugeSectors(Min(aBootSector.HugeSectors(), maxSectors));
        }

    return KErrNone;
    }

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

/**
    Read and validate the boot sector.
    If there is an error in reading the main boot sector (sec:0) or it is invalid, tries to read backup boot sector from sec:6.
    Flag iMainBootSecValid indicates the validity of the main boot sector. if it is false, but ret. value is KErrNone, it means that
    the backup boot sector was used and it is valid.

    @param      aBootSector reference to the boot sector object to be read.
    @param      aDoNotReadBkBootSec if true, there won't be an attempt to read backup sector
    @return     standard error code.

*/
TInt CFatMountCB::ReadBootSector(TFatBootSector& aBootSector, TBool aDoNotReadBkBootSec/*=EFalse*/)
    {
    iMainBootSecValid = EFalse; 

    //-- read main boot sector from the sector 0
    TInt nRes = DoReadBootSector(KBootSectorNum << KDefSectorSzLog2, aBootSector); 
    if(nRes == KErrNone)
        {
        if(aBootSector.IsValid())
            {
            iMainBootSecValid = ETrue; //-- main boot sector is valid, everything is OK
            return KErrNone;
            }
        else
            {
            __PRINT(_L("MainBoot Sector is invalid! dump:\n"));
            aBootSector.PrintDebugInfo();
            nRes = KErrCorrupt;
            }
        }

    ASSERT(nRes!= KErrNone && !iMainBootSecValid);

    if(aDoNotReadBkBootSec)
        return nRes;

    //-- main boot sector is invalid, try backup one (it might not present at all) 
    __PRINT(_L("Using backup boot sector...\n"));
    nRes=DoReadBootSector(KBkBootSectorNum << KDefSectorSzLog2, aBootSector); 
    if(nRes == KErrNone )
        {
        if(aBootSector.IsValid())
            return KErrNone; //-- main boot sector is bad, but backup one is OK
        else
            {//-- backup boot sector is invalid
            __PRINT(_L("Backup Sector is invalid! dump:\n"));    
            aBootSector.PrintDebugInfo();
            nRes = KErrCorrupt;
            }
        }
    
    //-- can't read boot sectors, or both are invalid
    return nRes;
    }

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

/**
Write a new volume label to BPB in media

@param aVolumeLabel Descriptor containing the new volume label
@leave 
*/
void CFatMountCB::WriteVolumeLabelL(const TDesC8& aVolumeLabel) const
    {
    if(aVolumeLabel.Length() > KVolumeLabelSize)
        User::Leave(KErrArgument);

    const TUint32 posVolLabel = Is32BitFat() ? KFat32VolumeLabelPos : KFat16VolumeLabelPos;
    User::LeaveIfError(LocalDrive()->Write(posVolLabel, aVolumeLabel)); 
    
    }


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

const TUint32 KFat32CleanShutDownMask	= 0x08000000; ///< Mask used to indicate test clean/dirty bit for Fat32
const TUint16 KFat16CleanShutDownMask	= 0x08000;    ///< Mask used to indicate test clean/dirty bit for Fat16

/**
Set or reset "VolumeClean" (ClnShutBitmask) flag.

@param  aClean if ETrue, marks the volume as clean, otherwise as dirty.
@leave  if write error occured.        
*/
void CFatMountCB::SetVolumeCleanL(TBool aClean) 
    {

	//-- The volume can't be set clean if there are objects opened on it. This precondition must be checked before calling this function
    if(aClean && LockStatus()!=0)
        {
        __PRINT1(_L("#- CFatMountCB::SetVolumeCleanL drive:%d isn't free!"),DriveNumber());
        ASSERT(0);
        User::Leave(KErrInUse);
        return;
        }
    
    if(FatType() == EFat12)
        {//-- Fat12 doesn't support this feature; do nothing other than notify the underlying drive (ignoring any error for now as there's nothing we can do with it)
		(void)LocalDrive()->Finalise(aClean);
        return;
        }

    //-- further read and write will be directly from the CProxyDrive, bypassing FAT cache. 
    //-- this is because CFatTable doesn't allow access to FAT[0] & FAT[1]
    //-- We also need to write data through CProxyDrive, because TDriveInterface has a call back that can call this method

    if(Is32BitFat())
        {//-- Fat32
		__PRINT2(_L("#- CFatMountCB::SetVolumeCleanL, drive:%d, param:%d, FAT32"),DriveNumber(), aClean);
		
        TFat32Entry fatEntry;
        const TInt  KFatEntrySize=sizeof(fatEntry); //-- FAT entry size in bytes
        TPtr8       ptrFatEntry((TUint8*)&fatEntry,KFatEntrySize);
        
        User::LeaveIfError(LocalDrive()->Read(StartOfFatInBytes()+KFatEntrySize, KFatEntrySize, ptrFatEntry)); //read FAT32[1] entry

        const TFat32Entry tmp = fatEntry;
        
        if(aClean)
            fatEntry |= KFat32CleanShutDownMask;  //-- set ClnShutBit flag
        else
            fatEntry &= ~KFat32CleanShutDownMask; //-- reset ClnShutBit flag

        if(tmp != fatEntry)
            {//-- write FAT[1] entry to all available FATs
                for(TInt i=0; i<NumberOfFats(); ++i)
                {
                const TInt64 pos = StartOfFatInBytes()+KFatEntrySize+(FatSizeInBytes()*i);
                User::LeaveIfError(LocalDrive()->Write(pos, ptrFatEntry)); //write FAT32[1] entry
                }
            }

        __PRINT2(_L("#- CFatMountCB::SetVolumeCleanL() entry:  %x->%x"), tmp, fatEntry);
        }
    else 
    if(Is16BitFat())
        {//-- Fat16. 
            __PRINT2(_L("#- CFatMountCB::SetVolumeCleanL, drive:%d, param:%d, FAT16"),DriveNumber(), aClean);

            if(FatConfig().FAT16_UseCleanShutDownBit())
                {
            TFat16Entry fatEntry;
            const TInt  KFatEntrySize=sizeof(fatEntry); //-- FAT entry size in bytes
            TPtr8       ptrFatEntry((TUint8*)&fatEntry,KFatEntrySize);
        
            User::LeaveIfError(LocalDrive()->Read(StartOfFatInBytes()+KFatEntrySize, KFatEntrySize, ptrFatEntry)); //read FAT16[1] entry

            const TFat16Entry tmp = fatEntry;
        
            if(aClean)
                fatEntry |= KFat16CleanShutDownMask;  //-- set ClnShutBit flag
            else
                fatEntry &= ~KFat16CleanShutDownMask; //-- reset ClnShutBit flag

            if(tmp != fatEntry)
                {//-- write FAT[1] entry to all available FATs
                for(TInt i=0; i<NumberOfFats(); ++i)
                    {
                    const TInt64 pos = StartOfFatInBytes()+KFatEntrySize+(FatSizeInBytes()*i);
                    User::LeaveIfError(LocalDrive()->Write(pos, ptrFatEntry)); //write FAT16[1] entry
                    }
                }
		    
            __PRINT2(_L("#- CFatMountCB::SetVolumeCleanL() entry:  %x->%x"), tmp, fatEntry);    
            }
            else
            {
            __PRINT(_L("#- changing FAT16[1] is disabled in config!"));    
            }
        }
    else
        {//-- must never get here
        ASSERT(0);
        }
    
		//-- Notify the underlying media that the mount is consistent  (ignoring any error for now as there's nothing we can do with it)
		(void)LocalDrive()->Finalise(aClean);


    }

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

/**
Determine whether "VolumeClean" (ClnShutBitmask) flag is set.

@return ETrue if the volume is marked as clean and EFalse otherwise.
@leave  if is called for FAT12 or if read error occured.
*/
TBool CFatMountCB::VolumeCleanL() 
    {

    //-- read access to the FAT is through TDriveInterface, because CFatTable doesn't allow access to FAT[1]
    TDriveInterface& drive =DriveInterface();

    if(Is32BitFat())
        {//-- Fat32
        TFat32Entry fatEntry;
        const TInt  KFatEntrySize=sizeof(fatEntry); //-- FAT entry size in bytes
        TPtr8       ptrFatEntry((TUint8*)&fatEntry, KFatEntrySize);
        
        User::LeaveIfError(drive.ReadNonCritical(StartOfFatInBytes()+KFatEntrySize, KFatEntrySize, ptrFatEntry)); //read FAT32[1] entry
        return (fatEntry & KFat32CleanShutDownMask);
        }
    else 
    if(Is16BitFat())
        {//-- Fat16
        TFat16Entry fatEntry;
        const TInt  KFatEntrySize=sizeof(fatEntry); //-- FAT entry size in bytes
        TPtr8       ptrFatEntry((TUint8*)&fatEntry, KFatEntrySize);
        
        User::LeaveIfError(drive.ReadNonCritical(StartOfFatInBytes()+KFatEntrySize, KFatEntrySize, ptrFatEntry)); //read FAT16[1] entry
        return (fatEntry & KFat16CleanShutDownMask);
        }
    else
        {//-- Fat12 doesn't support this feature, shan't get here, actually
        ASSERT(0);
        User::Leave(KErrNotSupported);
        return ETrue; //-- to satisfy the compiler
        }
    }


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

/**
Mount a Fat volume. 

@param aForceMount Flag to indicate whether mount should be forced to succeed if an error occurs
@leave KErrNoMemory,KErrNotReady,KErrCorrupt,KErrUnknown.
*/
void CFatMountCB::MountL(TBool aForceMount)
	{

    const TInt driveNo = Drive().DriveNumber();
    
    __PRINT2(_L("CFatMountCB::MountL() drv:%d, forceMount=%d\n"),driveNo,aForceMount);

    ASSERT(State() == ENotMounted || State() == EDismounted);
    SetState(EMounting);
    SetReadOnly(EFalse);
   
	User::LeaveIfError(CreateDrive(driveNo));


    //-- read FAT configuration parameters from estart.txt
    iFatConfig.ReadConfig(driveNo);

    //-- initialise interface to the low-level drive access
    if(!iDriverInterface.Init(this))
        User::LeaveIfError(KErrNoMemory);    

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

    iSize=capsBuf().iSize;
    iRamDrive = EFalse;

    if(capsBuf().iMediaAtt & KMediaAttVariableSize)
    {//-- this is a RAM drive
        UserSvr::UnlockRamDrive();
        iRamDrive = ETrue;
    }

	if(aForceMount)
	{//-- the state is "forcedly mounted", special case. This is an inconsistent state.
        SetState(EInit_Forced);  
    	return;
    }

    //-- read boot sector. If main is damaged, try to use backup one instead if this is not a RAM drive.
    TFatBootSector bootSector;
    User::LeaveIfError(ReadBootSector(bootSector, iRamDrive));


    //-- print out boot sector debug information
    bootSector.PrintDebugInfo();

    //-- determine FAT type by data from boot sector. This is done by counting number of clusters, not by BPB_RootEntCnt
    SetFatType(bootSector.FatType());
    ASSERT(iFatType != EInvalid); //-- this shall be checked in ReadBootSector()
    

    if(bootSector.RootDirEntries() == 0 && !Is32BitFat())
    {//-- FAT types mismatch. BPB_RootEntCnt is 0, which can be only for FAT32, but the number of clusters is less 
     //-- than required for FAT32. Probably this is incorrectly FAT32 formatted media. Put the drive into ReadOnly mode, assuming
     //-- that is FAT32.
        __PRINT(_L("FAT type mismatch! Setting drive to ReadOnly mode for FAT32. \n"));
        SetFatType(EFat32); //-- force FAT type to be FAT32
        SetReadOnly(ETrue);
    }

    //-- store volume UID, it can be checked on Remount
    iUniqueID = bootSector.UniqueID();

    //-- populate volume parameters with the values from boot sector. They had been validated in TFatBootSector::IsValid()
    iVolParam.Populate(bootSector); 
	
    //-- initialize the volume
    InitializeL(capsBuf());
    ASSERT(State()==EInit_R);

    GetVolumeLabelFromDiskL(bootSector);

	__PRINT2(_L("CFatMountCB::MountL() Completed, drv: %d, state:%d"), DriveNumber(), State());
	}



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

/**
Initialize the FAT cache and disk access

@param  aLocDrvCaps local drive capabilities
@param  aIgnoreFSInfo if ETrue, FSInfo sector shall be ignored. Used on volume remount to force FAT free clusters counting.

@leave KErrNoMemory,KErrNotReady,KErrCorrupt,KErrUnknown.
*/
void CFatMountCB::InitializeL(const TLocalDriveCaps& aLocDrvCaps, TBool aIgnoreFSInfo/*=EFalse*/) 
	{
    __PRINT1(_L("CFatMountCB::InitializeL() drv:%d"), DriveNumber());

    ASSERT(State() == EMounting); //-- we must get here only from MountL()
   
    //========== Find out number of clusters on the volume
	if(iRamDrive && SectorsPerCluster()!=1)
		{// Align iFirstFreeByte to cluster boundary if internal ram drive
		const TInt sectorsPerClusterLog2=ClusterSizeLog2()-SectorSizeLog2();
		const TInt rootDirEndSector=RootDirEnd()>>SectorSizeLog2();
		const TInt alignedSector=((rootDirEndSector+SectorsPerCluster()-1)>>sectorsPerClusterLog2)<<sectorsPerClusterLog2;
		iFirstFreeByte=alignedSector<<SectorSizeLog2();
		}
	else
		{
		if(Is32BitFat())
			iFirstFreeByte=(NumberOfFats() * FatSizeInBytes()) + (FirstFatSector() << SectorSizeLog2());
		else
			iFirstFreeByte=RootDirEnd();
		}


	    {//-- check if volume geometry looks valid
        const TInt usableSectors=TotalSectors()-(iFirstFreeByte>>SectorSizeLog2());
	    iUsableClusters=usableSectors>>(ClusterSizeLog2()-SectorSizeLog2());

        const TUint32 KMinClusters = 32; //-- absolute minimum number of clusters on the volume
        const TUint32 KMaxClusters=(TotalSectors()-FirstFatSector()-NumberOfFats()*(FatSizeInBytes()>>SectorSizeLog2())) >> (ClusterSizeLog2()-SectorSizeLog2());
        
        if(usableSectors <=0 || iUsableClusters < KMinClusters || iUsableClusters > KMaxClusters)
            {
            __PRINT(_L("CFatMountCB::InitializeL() Wrong number of usable cluster/sectors on the volume!"));
            User::Leave(KErrCorrupt);
            }
        }

	//========== initialise RawDisk interface
	//-- CFatMountCB parameters might have changed, e.g. after formatting. Reconstruct directory cache with new parameters
	
    delete iRawDisk;
	iRawDisk=CRawDisk::NewL(*this, aLocDrvCaps);
    iRawDisk->InitializeL();


    //========== Try to read FSInfo and deduct number of free clusters and other information from there
    TBool   bUseDataFromFsInfo = !aIgnoreFSInfo && Is32BitFat(); //-- if ETrue, we are going to use data from FSInfo sector (applicable for FAT32 only)
    
    //-- main boot sector shall be valid, otherwise we can't trust data from FSInfo
    bUseDataFromFsInfo = bUseDataFromFsInfo && iMainBootSecValid;

    //-- 1. check if using FSInfo is disabled in config
    if(bUseDataFromFsInfo && !FatConfig().FAT32_UseFSInfoOnMount())
    {
        __PRINT(_L("#- FSInfo using is disabled in config!"));
        bUseDataFromFsInfo = EFalse;
    }

#ifdef  _DEBUG
    //-- 2. check if FSInfo is disabled by test interface (special debug property). This property is defined and set by the test application.
    TInt nMntDebugFlags;
    if(bUseDataFromFsInfo && RProperty::Get(KSID_Test1, DriveNumber(), nMntDebugFlags) == KErrNone)
        {//-- test property for this drive is defined
            if(nMntDebugFlags & KMntDisable_FsInfo)
            {
            __PRINT(_L("#- FSInfo using is disabled by debug interface."));
            bUseDataFromFsInfo = EFalse;
            }
        }
#endif

    //-- 3. try to read FSInfoSector and its copy if the volume had been properly shut down before (is now clean) 
    CFatTable::TMountParams fatMntParams;
    bUseDataFromFsInfo = bUseDataFromFsInfo && VolumeCleanL();
    if(bUseDataFromFsInfo)
        {
        bUseDataFromFsInfo = ProcessFSInfoSectors(fatMntParams);
        if(!bUseDataFromFsInfo)
            { 
            __PRINT1(_L("#- CFatMountCB::ProcessFSInfoSectors() failed. drv:%d"), DriveNumber());
            }
        }

    //========== create and initialise FAT table 
	
    delete iFatTable;
    iFatTable=CFatTable::NewL(*this, aLocDrvCaps);

    //-- mount the FAT table. Depending on mount parameters and configuration this method 
    //-- can do various things, like counting free clusters synchronously if data from FSInfo isn't valid, 
    //-- or setting up a FAT backround thread and return immediately etc.
    iFatTable->MountL(fatMntParams);
   
    SetState(EInit_R);  //-- the state is "Initialized, but not writen"

    //-- make a callback, telling FileServer about free space discovered.
    const TInt64 freeSpace = ((TInt64)FAT().NumberOfFreeClusters()) << ClusterSizeLog2();
    SetDiskSpaceChange(freeSpace);
    //========== create and setup leaf direcotry cache if cache limit is set bigger than one 

	const TUint32 cacheLimit = iFatConfig.LeafDirCacheSize();
	if (cacheLimit > 1)
		{
		// destroy the old leaf dir cache to avoid memory leak.
		delete iLeafDirCache;
		iLeafDirCache = CLeafDirCache::NewL(cacheLimit);
		}
	else
		{
		iLeafDirCache = NULL;
		}
    
    __PRINT3(_L("#- CFatMountCB::InitializeL() done. drv:%d, Free clusters:%d, 1st Free cluster:%d"),DriveNumber(), FAT().NumberOfFreeClusters(), FAT().FreeClusterHint());

    
	}

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

/**
    Write FSInfo sectors to the media

    @param aMediaPos    Media position to write FSInfo sector to
    @param aFSInfo      FSInfo structure to write
    @return System wide error code
*/
TInt CFatMountCB::WriteFSInfoSector(TInt64 aMediaPos, const TFSInfo& aFSInfo)  const
    {
    __PRINT2(_L("#- CFatMountCB::WriteFSInfoSector() drv:%d, pos:0x%x"),Drive().DriveNumber(), (TUint32)aMediaPos);   

    ASSERT(aMediaPos >= 0 && aMediaPos < FirstFatSector()<<SectorSizeLog2());
    ASSERT(Is32BitFat());

    TBuf8<KSizeOfFSInfo> fsInfoSecBuf;

    //-- put data to the sector buffer
    aFSInfo.Externalize(fsInfoSecBuf);

	TInt r=LocalDrive()->Write(aMediaPos, fsInfoSecBuf);
	if (r!=KErrNone)
        {
        __PRINT2(_L("CFatMountCB::WriteFSInfoSector() failed! drv:%d, code:%d"),Drive().DriveNumber(),r);
        }

    return r;
    }

//-------------------------------------------------------------------------------------------------------------------
/**
    Read the FSInfo structure from media 

    @param aMediaPos    Media position to read FSInfo sector from
    @param aFSInfo      data read from FSInfo structure
    @return System wide error code
*/
TInt CFatMountCB::ReadFSInfoSector(TInt64 aMediaPos, TFSInfo& aFSInfo)  const
    {
    __PRINT2(_L("#- CFatMountCB::ReadFSInfoSector() drv:%d, pos:0x%x"),Drive().DriveNumber(), (TUint32)aMediaPos);   

    ASSERT(aMediaPos >= 0 && aMediaPos < FirstFatSector()<<SectorSizeLog2());
    ASSERT(Is32BitFat());

    TBuf8<KSizeOfFSInfo> fsInfoSecBuf;

    TInt r=LocalDrive()->Read(aMediaPos, KSizeOfFSInfo, fsInfoSecBuf); 
	if (r!=KErrNone)
	{
		__PRINT2(_L("CFatMountCB::ReadFSInfoSector() failed! drv:%d, code:%d"),Drive().DriveNumber(),r);
	    return r;
	}

    //-- take FSInfo data from the buffer
    aFSInfo.Internalize(fsInfoSecBuf);
    
    return KErrNone;
    }


/**
    Initialize data to represent the root directory

    @param anEntry Entry to initialise
*/
void CFatMountCB::InitializeRootEntry(TFatDirEntry& anEntry) const
	{
	anEntry.SetName(_L8("ROOT"));
	anEntry.SetAttributes(KEntryAttDir);
	anEntry.SetStartCluster(RootClusterNum()); //--iRootClusterNum is 0 for FAT12/16
	}



/**
    Implementation of CMountCB::FileSystemSubType(). Retrieves the sub type of Fat file system
    and returns the name as a descriptor.

    @param aName Name of the sub type of Fat file system
    @return KErrNone if successful; KErrArgument if aName is not long enough; KErrNotReady if
		    the mount is not ready.

    @see CMountCB::FileSystemSubType()
*/
TInt CFatMountCB::SubType(TDes& aName) const
	{
	if(aName.MaxLength() < 5)
		return KErrArgument;
	
	switch (FatType())
		{
		case EFat12:
			{
			aName = KFSSubType_FAT12;
			return KErrNone;
			}
		case EFat16:
			{
			aName = KFSSubType_FAT16;
			return KErrNone;
			}
		case EFat32:
			{
			aName = KFSSubType_FAT32;
			return KErrNone;
			}
		default:
		// case EInvalidFatType
			return KErrNotReady;
		}
	}


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

/**
    Try to extract useful information from the FSInfo sectors.
    The information from FSInfo sectors will be trusted only if there are main and backup sectors,
    they are both valid and the same.

    @param  aFatInitParams  on success will contain the number of free clusters on the volume and 1st free cluster number from the FSInfo 
    @return ETrue on success.
*/
TBool CFatMountCB::ProcessFSInfoSectors(CFatTable::TMountParams& aFatInitParams) const
    {

    aFatInitParams.iFreeClusters = 0;
    aFatInitParams.iFirstFreeCluster = 0;
    aFatInitParams.iFsInfoValid = EFalse;

    const TUint32 currFsInfoSec   = iVolParam.FSInfoSectorNum();    //-- values from the boot sector
    const TUint32 currFsInfoBkSec = iVolParam.BkFSInfoSectorNum();                                                                              

    if(!Is32BitFat() || currFsInfoSec < KFSInfoSectorNum || currFsInfoSec >= FirstFatSector())
        {
        ASSERT(0);  //-- main FSInfo sector must have sensible location
        return EFalse;
        }

    if(currFsInfoBkSec < KFSInfoSectorNum || currFsInfoBkSec >= FirstFatSector() || currFsInfoBkSec <= currFsInfoSec)
        return EFalse; //-- something is wrong with backup copy location

    TFSInfo fsInfo;
	TInt    nRes;

    //-- 1. read and validate main FS Info sector
    nRes = ReadFSInfoSector(currFsInfoSec << SectorSizeLog2(), fsInfo);
    if(nRes != KErrNone)
        return EFalse;

    fsInfo.PrintDebugInfo();

    if(!fsInfo.IsValid())
        return EFalse;

    const TUint32 freeCount=fsInfo.FreeClusterCount(); // last known free cluster count
	const TUint32 nextFree =fsInfo.NextFreeCluster();  // hint to file system as to where to start looking for free clusters

    //-- 2. read and check backup FS Info sector, it must be the same as the main one
    nRes = ReadFSInfoSector(currFsInfoBkSec << SectorSizeLog2(), fsInfo);
    
    if(nRes != KErrNone || !fsInfo.IsValid() || freeCount != fsInfo.FreeClusterCount() || nextFree != fsInfo.NextFreeCluster())
    {
        __PRINT(_L("#- CFatMountCB::ProcessFSInfoSectors(): copies of FSInfo are different!"));
        return EFalse;
    }

    if(freeCount < 1 || freeCount > UsableClusters())
        return EFalse; //-- looks like invalid value
    
    if(nextFree < KFatFirstSearchCluster || nextFree >= UsableClusters()+KFatFirstSearchCluster)
        return EFalse; //-- looks like invalid value

    //-- success
    aFatInitParams.iFreeClusters = freeCount;
    aFatInitParams.iFirstFreeCluster = nextFree;
    aFatInitParams.iFsInfoValid = ETrue;
    
    return ETrue;
    }

//-------------------------------------------------------------------------------------------------------------------
/**
    Internal helper method. Writes FSInfo sector and its backup copy to the volume if necessary. 
    
    @param  aInvalidateFSInfo if ETrue, FSInfo data (free clusters count) will be invalidated.
                              otherwise, data from the CFatTable object will be used
                                    
    @leave  if disk opertion fails
*/
void CFatMountCB::DoUpdateFSInfoSectorsL(TBool aInvalidateFSInfo)
    {
    __PRINT2(_L("#- CFatMountCB::DoUpdateFSInfoSectorsL(%d) drv:%d"),aInvalidateFSInfo, Drive().DriveNumber());
    
    ASSERT(Is32BitFat());
    
    if(!aInvalidateFSInfo)
        {
        ASSERT(FAT().ConsistentState());
        }
        
    //-- 1. check that FSInfoSector numbers are valid
    TBool bCanWriteFSInfo_Main = EFalse;//-- if ETrue, it's OK to write main FSInfo sector
    TBool bCanWriteFSInfo_Bk = EFalse;  //-- if ETrue, it's OK to write backup FSInfo sector

    const TUint32 currFsInfoSec   = iVolParam.FSInfoSectorNum();    //-- values from the boot sector
    const TUint32 currFsInfoBkSec = iVolParam.BkFSInfoSectorNum();
        
    if(currFsInfoSec > 0 && currFsInfoSec < FirstFatSector())
        {//-- seems to be OK
        bCanWriteFSInfo_Main = ETrue;
        }
    else
        {
        __PRINT1(_L("#- DoUpdateFSInfoSectorsL() iFSInfoSectorNum is wrong!: sec:%d"), currFsInfoSec);
        }

    if(currFsInfoBkSec > 0 && currFsInfoBkSec < FirstFatSector() && currFsInfoBkSec > currFsInfoSec)
        {//-- seems to be OK
        bCanWriteFSInfo_Bk = bCanWriteFSInfo_Main;
        }
    else
        {
        __PRINT1(_L("#- DoUpdateFSInfoSectorsL() iBkFSInfoSectorNum is wrong!: sec:%d"),currFsInfoBkSec);
        }

    if(!bCanWriteFSInfo_Main && !bCanWriteFSInfo_Bk)
        return; //-- nothing to do

    
    const TUint32 KFsInfoSecPos_Main = currFsInfoSec   << SectorSizeLog2();  //-- main FSInfo sector media position
    const TUint32 KFsInfoSecPos_Bk   = currFsInfoBkSec << SectorSizeLog2();  //-- backup FSInfo sector media position
    
    TFSInfo fsInfoSector;

    TBool bNeedWriteFSInfo_Main = EFalse;
    TBool bNeedWriteFSInfo_Bk = EFalse;

    
    const TUint32 KInvalidVal = 0xFFFFFFFF; //-- invalid value for FSInfo fields, see FAT specs
    
    //-- we need here _exact_ number of free clusters, so make FAT().NumberOfFreeClusters() operation synchronous
    const TUint32 KFreeClusters    = aInvalidateFSInfo ? KInvalidVal : FAT().NumberOfFreeClusters(ETrue); 
    const TUint32 KNextFreeCluster = aInvalidateFSInfo ? KInvalidVal : FAT().FreeClusterHint(); 
    
    //-- check if the main FSInfo sector differs from the FAT information
    if(bCanWriteFSInfo_Main)
        {
        User::LeaveIfError(ReadFSInfoSector(KFsInfoSecPos_Main, fsInfoSector));
        bNeedWriteFSInfo_Main = !fsInfoSector.IsValid() ||
                                fsInfoSector.FreeClusterCount() != KFreeClusters || 
                                fsInfoSector.NextFreeCluster()  != KNextFreeCluster; 
        }

    //-- check if the backup FSInfo sector differs from the FAT information
    if(bCanWriteFSInfo_Bk)
        {
        User::LeaveIfError(ReadFSInfoSector(KFsInfoSecPos_Bk, fsInfoSector));
        bNeedWriteFSInfo_Bk =   !fsInfoSector.IsValid() ||
                                fsInfoSector.FreeClusterCount() != KFreeClusters ||
                                fsInfoSector.NextFreeCluster()  != KNextFreeCluster; 
        }

    //-- setup data in FSInfo sector to write
    fsInfoSector.Initialise();
    fsInfoSector.SetFreeClusterCount(KFreeClusters);
    fsInfoSector.SetNextFreeCluster(KNextFreeCluster);

    if(!bNeedWriteFSInfo_Main && !bNeedWriteFSInfo_Bk)
        return; //-- nothing to do

    SetVolumeCleanL(EFalse); //-- mark volume dirty, just in case something will happen on FSInfo sectors write

    if(bNeedWriteFSInfo_Main)
        User::LeaveIfError(WriteFSInfoSector(KFsInfoSecPos_Main, fsInfoSector));

    if(bNeedWriteFSInfo_Bk)
        User::LeaveIfError(WriteFSInfoSector(KFsInfoSecPos_Bk, fsInfoSector));

    }

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

/**
    CFatMountCB control method.
    @param  aLevel  specifies the operation to perfrom on the mount
    @param  aOption specific option for the given operation
    @param  aParam  pointer to generic parameter, its meaning depends on aLevel and aOption

    @return standard error code.
*/
TInt CFatMountCB::MountControl(TInt aLevel, TInt aOption, TAny* aParam)
    {
    __PRINT3(_L("CFatMountCB::MountControl() drv:%d, level:%d, opt:%d"),Drive().DriveNumber(), aLevel, aOption);
    
    TInt nRes = KErrNotSupported; 
    
    if(aLevel == ECheckFsMountable)
        {
        return MntCtl_DoCheckFileSystemMountable();
        }
    
    //-- todo: move these functions code into separate methods ??

    //-- mount state query: check if is in finalised state 
    if(aLevel == EMountStateQuery && aOption == ESQ_IsMountFinalised)
        {
        TBool bFinalised;
        nRes = IsFinalised(bFinalised);
        if(nRes == KErrNone)
            {
            *((TBool*)aParam) = bFinalised;
            }
        
        return nRes;
        }

    //-- mount-specific volume parameters queries that might not be handled by CFatMountCB::VolumeL
    if(aLevel == EMountVolParamQuery)
        {
        ASSERT(ConsistentState()); //-- volume state shall be consistent, otherwise its parameters do not make sense
        switch(aOption)
            {
            //-- Request a certain amount of free space on the volume.
            case ESQ_RequestFreeSpace:
                {
                TUint64* pVal = (TUint64*)aParam; //-- in: number of free bytes on the volume required, out: resulted amount of free space.
                const TUint32 KClustersRequired = (TUint32)((*pVal + ClusterSize() - 1) >> ClusterSizeLog2());
                __PRINT2(_L("MountControl() ReqFreeSpace:%LU, clusters:%d"), *pVal, KClustersRequired);    

                if(KClustersRequired)
                    {//-- actually, this doesn't guarantee that it will finally be KClustersRequired available.
                    (void)FAT().RequestFreeClusters(KClustersRequired);  
                    }
         
                const TUint32 freeClusters = FAT().NumberOfFreeClusters(EFalse); //-- _current_ amount of free clusters  
                *pVal = (TInt64)freeClusters << ClusterSizeLog2();

                return KErrNone;
                }

            //-- A request to obtain the _current_ amount of free space on the volume asynchronously, without blocking.
            case ESQ_GetCurrentFreeSpace:
                {
                TUint64* pVal = (TUint64*)aParam; //-- out: resulted amount of free space.

                const TUint32 freeClusters = FAT().NumberOfFreeClusters(EFalse); //-- _current_ amount of free clusters  
                *pVal = (TInt64)freeClusters << ClusterSizeLog2();
                __PRINT1(_L("MountControl() Asynch. request; curent amount of free clusters: %d"), freeClusters);

                return KErrNone;
                }
        
            //-- A request to obtain size of the mounted volume without blocking (CMountCB::VolumeL() can block).
            case ESQ_MountedVolumeSize:
                {
                if(iRamDrive)
                    return KErrNotSupported; //-- it requires knowledge of free space on the volume
    
                TUint64* pVal = (TUint64*)aParam; 
                *pVal = iSize; //-- physical drive size

                //-- take into account space occupied by FAT table, etc.
                *pVal -= ClusterBasePosition(); 
                *pVal=(*pVal >> ClusterSizeLog2()) << ClusterSizeLog2();  //-- round down to cluster size

                __PRINT1(_L("MountControl() MountedVolumeSize:%LU"), *pVal);
                return KErrNone;
                }

            default:
            __PRINT1(_L("MountControl() unsupported opt:%d"), aOption);
            ASSERT(0);
            break;
            };

        }

        //-- File System - specific queries 
        if(aLevel == EMountFsParamQuery && aOption == ESQ_GetMaxSupportedFileSize)
            {//-- this is a query to provide the max. supported file size; aParam is a pointer to TUint64 to return the value
            *(TUint64*)aParam = KMaxSupportedFatFileSize;    
            return KErrNone;
            }


    
    
    return KErrNotSupported; 
    }

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

/**
Reports whether the specified interface is supported - if it is,
the supplied interface object is modified to it

@param aInterfaceId     The interface of interest
@param aInterface       The interface object
@return                 KErrNone if the interface is supported, otherwise KErrNotFound 

@see CMountCB::GetInterface()
*/
TInt CFatMountCB::GetInterface(TInt aInterfaceId, TAny*& aInterface,TAny* aInput)
    {
    switch(aInterfaceId)
        {
        case (CMountCB::EFileAccessor):
            ((CMountCB::MFileAccessor*&) aInterface) = this;
            return KErrNone;
        
        case (CMountCB::EGetFileSystemSubType):
            aInterface = (MFileSystemSubType*) (this);
            return KErrNone;

        case (CMountCB::EGetClusterSize):
            aInterface = (MFileSystemClusterSize*) (this);
            return KErrNone;

        case CMountCB::ELocalBufferSupport:
            // RAM drives doesn't support local buffers (this results in file caching being disabled)
            if (iRamDrive) 
                return KErrNotSupported;
            else
                return LocalDrive()->LocalBufferSupport();
        
		case EGetProxyDrive:
			((CProxyDrive*&)aInterface) = LocalDrive();
			return KErrNone;
        
        case CMountCB::EFileExtendedInterface:
       		// For supporting large file ReadFileSection
       		((CMountCB::MFileExtendedInterface*&) aInterface) = this;
           	return KErrNone;
        
        default:
            return(CMountCB::GetInterface(aInterfaceId, aInterface, aInput));
        }
    }

//-----------------------------------------------------------------------------------------
void CFatMountCB::ReadSection64L(const TDesC& aName, TInt64 aPos, TAny* aTrg, TInt aLength, const RMessagePtr2& aMessage)
// From CMountCB::MFileExtendedInterface
	{
    __PRINT(_L("CFatMountCB::ReadSection64L"));
	
    CheckStateConsistentL();
    TEntryPos dosEntryPos(RootIndicator(),0);
    TFatDirEntry dosEntry;
    TFileName fileName; 

    TInt namePos=aName.LocateReverse(KPathDelimiter)+1; // There is always a path delimiter
    TLeafDirData leafDir(0);
    dosEntryPos.iCluster=FindLeafDirL(aName.Left(namePos), leafDir);
    dosEntryPos.iPos=0;
    TEntryPos firstEntryPos;
    TFatDirEntry firstEntry;
    DoFindL(aName.Mid(namePos),KEntryAttMaskSupported,
    		firstEntryPos,firstEntry,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

	TInt64 fileSize = MAKE_TINT64(0,dosEntry.Size());
	if (aPos>=fileSize)
        User::Leave(KErrEof);
	
    if (aPos+aLength>fileSize)
        aLength=(TInt)(fileSize-aPos);
    
    TInt cluster=StartCluster(dosEntry);	
	TInt64 pos = aPos;
	
    TInt endCluster;
    TInt clusterSize=1<<ClusterSizeLog2();      //  Size of file clusters
	TInt readTotal = 0;
	
	// Total number of clusters in file
    TInt maxClusters=(TInt)((fileSize+clusterSize-1)>>ClusterSizeLog2());

	// Read data
    FOREVER
        {
		//  Get the maximum number of clusters that can be read contiguously
        TInt64 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 = (TInt)Min((TInt64)(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;
		}
    }
