userlibandfileserver/fileserver/sfat32/sl_mnt32.cpp
changeset 0 a41df078684a
child 33 0173bcd7697c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/userlibandfileserver/fileserver/sfat32/sl_mnt32.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,1119 @@
+// 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 TFatDriveInterface 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 TFatDriveInterface, because CFatTable doesn't allow access to FAT[1]
+    TFatDriveInterface& 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
+    iFatType = 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"));
+        iFatType = 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;
+    }
+
+/**
+Checks for end of file for all Fat types
+
+@param aCluster Cluster to check
+@return Result of test
+*/
+TBool CFatMountCB::IsEndOfClusterCh(TInt aCluster) const
+	{
+	if(Is32BitFat())
+		return(aCluster>=(TInt)0x0FFFFFF8 && aCluster<=(TInt)0x0FFFFFFF);
+	else if(Is16BitFat())
+		return(aCluster>=0xFFF8 && aCluster<=0xFFFF);
+	else
+		return(aCluster>=0xFF8 && aCluster<=0xFFF);
+	}
+
+/**
+Set a cluster to the end of cluster chain marker
+
+@param aCluster cluster to set to end of chain marker
+*/
+void CFatMountCB::SetEndOfClusterCh(TInt &aCluster) const
+	{
+	if(Is32BitFat())
+		aCluster=EOF_32Bit;
+	else if(Is16BitFat())
+		aCluster=EOF_16Bit;
+	else
+		aCluster=EOF_12Bit;
+	}
+
+/**
+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 (iFatType)
+		{
+		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;
+		}
+    }