// 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\sfat\sl_file.cpp
// 
//


//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//!!
//!! 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 <e32math.h>

const TInt KSeekIndexSize=128; // Cache 128 clusters
const TInt KSeekIndexSizeLog2=7;
const TInt KFirstClusterNum=2;

CFatFileCB::CFatFileCB()
    {

    __PRINT1(_L("CFatFileCB created 0x%x"),this);
    }

CFatFileCB::~CFatFileCB()
    {
    __PRINT1(_L("CFatFileCB deleted 0x%x"),this);

    //-- a nasty trick to find out if the CFatFileCB is in consistent state on the moment of destruction.
    //-- Because of OOM conditions CFatFileCB might not be fully constructed and to be deleted, while FlushAll()
    //-- implies valid iMount.
    const CMountCB* pMount  = &Mount();
    if(pMount)
        {//-- do some finalisation work if CMountCB is valid
        if (iAtt&KEntryAttModified)
            TRAP_IGNORE(FlushAllL());
        }

    delete[] iSeekIndex;
    }


void CFatFileCB::CreateSeekIndex()
//
// Create a seek index
//
    {

    iSeekIndex = new TUint32[KSeekIndexSize];
    if (iSeekIndex == NULL)
        return;

    Mem::FillZ(iSeekIndex, sizeof(TUint32) * KSeekIndexSize);

    iSeekIndexSize=CalcSeekIndexSize(Size());
    }

TInt CFatFileCB::SeekToPosition(TInt aNewRelCluster,TInt aClusterOffset)
//
// Use the seek index to set iCurrentPos.iCluster as close as possible to aNewRelCluster
// Return aNewRelCluster-aCurrentPos.iCluster
//
    {
    TInt clusterOffset=aClusterOffset;
    TInt seekPos=(aNewRelCluster>>iSeekIndexSize)-1;
    __ASSERT_DEBUG(seekPos<KSeekIndexSize,Fault(EFatFileSeekIndexTooSmall));

    while(seekPos>=0 && iSeekIndex[seekPos]==0 && clusterOffset!=0)
        {
        seekPos--;
        clusterOffset--;
        }
    if (clusterOffset==0) // Counted back to the current cluster
        return(aClusterOffset);
    if (seekPos<0)
        {
        iCurrentPos.iCluster=iStartCluster;
        return(aNewRelCluster);
        }

    iCurrentPos.iCluster=iSeekIndex[seekPos];
    return(aNewRelCluster-((seekPos+1)<<iSeekIndexSize));
    }

void CFatFileCB::SetSeekIndexValueL(TInt aRelCluster,TInt aStoredCluster)
//
// Sets a value in the seekindex
//
    {

    TInt seekPos=(aRelCluster>>iSeekIndexSize)-1;
    __ASSERT_DEBUG(seekPos<KSeekIndexSize,Fault(EFatFileSeekIndexTooSmall));
    __ASSERT_DEBUG(seekPos>=0,Fault(EFatFileSeekIndexTooSmall2));
    iSeekIndex[seekPos] = aStoredCluster;
    }

TBool CFatFileCB::IsSeekBackwards(TUint aPos)
//
// Return true if aPos<currentPos
//
    {
    
    TUint cluster=iCurrentPos.iCluster<<ClusterSizeLog2();
    TInt offset=ClusterRelativePos(iCurrentPos.iPos);
    TUint currentPos=cluster+offset;
    return(aPos<currentPos);
    }

void CFatFileCB::CheckPosL(TUint aPos)
//
// Check that the file is positioned correctly.
// If aPos<currentPos attempt to guess the new position.
//
    {
    __PRINT1(_L("CFatFileCB::CheckPosL(%d)"), aPos);
    if (aPos==iCurrentPos.iPos)
        return;
    __ASSERT_DEBUG(aPos <= (TUint)Size(), Fault(EFatFilePosBeyondEnd));

    if (iFileSizeModified && IsSeekBackwards(aPos))
        FlushDataL(); 
    
    TUint newRelCluster=aPos>>ClusterSizeLog2();
    if ( aPos && (aPos==(newRelCluster<<ClusterSizeLog2())) )
        newRelCluster--;
    TUint oldRelCluster=iCurrentPos.iPos>>ClusterSizeLog2();
    if ( iCurrentPos.iPos && (iCurrentPos.iPos==(oldRelCluster<<ClusterSizeLog2())) )
        oldRelCluster--;    
    TInt clusterOffset=newRelCluster-oldRelCluster;
    TInt oldCluster=iCurrentPos.iCluster;
    iCurrentPos.iPos=aPos;
    if (clusterOffset==0)
        return;
    TInt seekOffset=clusterOffset;
    if (iSeekIndex!=NULL)
        { // Can alter iCurrentPos.iCluster
        seekOffset=SeekToPosition(newRelCluster,seekOffset);
        if (seekOffset==0)
            return;
        }
    if (clusterOffset==-1 && seekOffset!=1)
        { // Check previous cluster
        TInt cluster=oldCluster-1;
        if (FAT().GetNextClusterL(cluster) && cluster==oldCluster)
            {
            iCurrentPos.iCluster=oldCluster-1;
            return;
            }
        }
    if (seekOffset<0)
        {
        seekOffset=newRelCluster;
        iCurrentPos.iCluster=iStartCluster;
        }
    while (seekOffset--)
        {
        if (!FAT().GetNextClusterL(iCurrentPos.iCluster))
            {
            __PRINT(_L("CFatFileCB::CheckPosL() corrupt#1"));
            User::Leave(KErrCorrupt);
            }
        TInt cluster=newRelCluster-seekOffset;
        if (iSeekIndex!=NULL && cluster && (cluster>>iSeekIndexSize)<<iSeekIndexSize==cluster)
            SetSeekIndexValueL(cluster,iCurrentPos.iCluster);
        }
    }

void CFatFileCB::SetL(const TFatDirEntry& aFatDirEntry,TShare aShare,const TEntryPos& aPos)
//
// Initialize FileCB from entry data
//
    {

    __PRINT(_L("CFatFileCB::SetL"));
    SetSize(aFatDirEntry.Size()); 
    iCurrentPos.iCluster= FatMount().StartCluster(aFatDirEntry);
    iStartCluster=iCurrentPos.iCluster;
    iCurrentPos.iPos=0;
    iAtt=aFatDirEntry.Attributes();
    iModified= aFatDirEntry.Time(FatMount().TimeOffset());
    iShare=aShare;
    iFileDirPos=aPos;

    SetMaxSupportedSize(KMaxSupportedFatFileSize);
    }

//-----------------------------------------------------------------------------
// from CFileCB::MExtendedFileInterface
void CFatFileCB::ReadL(TInt64 aPos,TInt& aLength, TDes8* aDes, const RMessagePtr2& aMessage, TInt aOffset)
    {
    __PRINT2(_L("CFatFileCB::ReadL aFilePos=%LU aLength=%d"),aPos,aLength);
    
    if((TUint64)aPos > KMaxSupportedFatFileSize-1)
        User::Leave(KErrNotSupported);  //-- max. position in the file is 0xFFFFFFFE

    FatMount().CheckStateConsistentL();
    
    CheckPosL(I64LOW(aPos));
    
    const TUint startPos = iCurrentPos.iPos;
    const TUint curSize  = (TUint)Size();
    const TUint length   = (TUint)aLength;
    
    if((startPos + length > curSize) || (startPos > startPos + length) )
        aLength=curSize-startPos;
    
    FatMount().ReadFromClusterListL(iCurrentPos,aLength,aDes,aMessage,aOffset);
    aLength=iCurrentPos.iPos-startPos;
    }


void CFatFileCB::ReadL(TInt aFilePos,TInt& aLength,const TAny* aTrg,const RMessagePtr2& aMessage)
    {
    ReadL(TInt64(aFilePos),aLength,(TDes8*) aTrg,aMessage, 0);
    }

//-----------------------------------------------------------------------------
// from CFileCB::MExtendedFileInterface
void CFatFileCB::WriteL(TInt64 aPos,TInt& aLength,const TDesC8* aSrc,const RMessagePtr2& aMessage, TInt aOffset)
    {
    __PRINT2(_L("CFatFileCB::WriteL aFilePos=%LU aLength=%d"),aPos,aLength);
    // FAT supports 32 bits only for file size
    TUint64 endPos = aPos + aLength;
    if(endPos > KMaxSupportedFatFileSize)
        User::Leave(KErrNotSupported);
    
    FatMount().CheckStateConsistentL();
    FatMount().CheckWritableL();
    const TUint pos = I64LOW(aPos);
    CheckPosL(pos);
    
    const TUint startCluster = (TUint)iStartCluster;
    const TUint length       = (TUint)aLength;
    
    endPos = iCurrentPos.iPos + length; 
    if ((endPos           > (TUint)Size()) ||
        (iCurrentPos.iPos > endPos)         ) // Overflow condition 
        DoSetSizeL(iCurrentPos.iPos+length,EFalse);
    
    TUint startPos=iCurrentPos.iPos;
    TInt badcluster=0;
    TInt goodcluster=0;
    
    TRAPD(ret, FatMount().WriteToClusterListL(iCurrentPos,aLength,aSrc,aMessage,aOffset,badcluster, goodcluster));
    
    if (ret == KErrCorrupt || ret == KErrDied)
        {
        if(startCluster == 0)
            { //Empty File, revert all the clusters allocated.
            TInt cluster = iStartCluster;
            iStartCluster = 0;
            SetSize(0);
            FlushAllL();

            iCurrentPos.iCluster = 0;
            iCurrentPos.iPos = 0;

            FAT().FreeClusterListL(cluster);
            FAT().FlushL();
            }
        else
            { //Calculate the clusters required based on file size, revert extra clusters if allocated.
            const TUint curSize = (TUint)Size();
            TUint ClustersNeeded = curSize >> ClusterSizeLog2();
            if(curSize > (ClustersNeeded << ClusterSizeLog2()))
                {
                ClustersNeeded++;
                }

            TInt cluster = iStartCluster;
            while(--ClustersNeeded)
                {
                FAT().GetNextClusterL(cluster);
                }
                
            iCurrentPos.iCluster = cluster;

            if (FAT().GetNextClusterL(cluster))
                {
                FAT().FreeClusterListL(cluster);
                }

            FAT().WriteFatEntryEofL(iCurrentPos.iCluster);
            FAT().FlushL();
            }
        }

    User::LeaveIfError(ret);

    if(badcluster != 0)
        {
        if(iStartCluster == badcluster)
            {
            iStartCluster = goodcluster;
            FlushStartClusterL();
            }
        else
            {
            TInt aCluster = iStartCluster;
            do
                {
                if((TUint)badcluster == FAT().ReadL(aCluster))
                    {
                    FAT().WriteL(aCluster, goodcluster);
                    FAT().FlushL();
                    break;
                    }
                }
            while(FAT().GetNextClusterL(aCluster));
            }
        }
    aLength=iCurrentPos.iPos-startPos;

    if(FatMount().IsRuggedFSys() && pos+(TUint)aLength>(TUint)Size())
        {
        WriteFileSizeL(pos+aLength);
        }

    }


void CFatFileCB::WriteL(TInt aFilePos,TInt& aLength,const TAny* aSrc,const RMessagePtr2& aMessage)
    {
    WriteL(TInt64(aFilePos),aLength,(TDesC8*) aSrc,aMessage, 0);
    }



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

void CFatFileCB::ResizeIndex(TInt aNewMult,TUint aNewSize)
//
// Resize the seek index to accomodate a larger or smaller filesize
// Assumes KSeekIndexSize is a power of 2.
//
    {

    TInt maxNewIndex=aNewSize>>(ClusterSizeLog2()+aNewMult);


    TInt    index=0;
    TInt    indexEnd=KSeekIndexSize;
    TInt    newValEnd=maxNewIndex;

    if (iSeekIndexSize<aNewMult)
        {
        TInt newVal=index;
        TInt step=1<<(aNewMult-iSeekIndexSize);
        index+=step-1;
        while(index<indexEnd && newVal<newValEnd)
            {
            iSeekIndex[newVal] =  iSeekIndex[index];
            newVal++;
            index+=step;
            }
        while(newVal<indexEnd)
            iSeekIndex[newVal++] =  0;
        }
    else
        {
        TInt diffSize = iSeekIndexSize-aNewMult;
        TInt oldVal=(KSeekIndexSize>>diffSize) - 1;
        TInt newVal=indexEnd-1;
        TInt skip=(1<<diffSize)-1;

        if ((iSeekIndexSize - aNewMult) > KSeekIndexSizeLog2)
            {
            ClearIndex(0); //-- Invalidate every entry.
            }
        else
            {
            while(newVal>=index)
                {

                iSeekIndex[newVal--] =  iSeekIndex[oldVal--];


                for(TInt i=skip;i>0;i--)
                    {   
                    iSeekIndex[newVal--] = 0;

                    }
                }
            }
        }
    iSeekIndexSize=aNewMult;
    }


/**
    Zero freed clusters in the index

    @param  aNewSize new size of the file that the index corresponds to.
            if = 0  all existing index will be zero filled
*/ 
void CFatFileCB::ClearIndex(TUint aNewSize)
    {

    if (!iSeekIndex)
        return;

    if(aNewSize==0)
        {
        //-- zero fill all the array
        Mem::FillZ(iSeekIndex, KSeekIndexSize*sizeof(TUint32));
        return;
        }

    // Files that fill up a cluster exactly do not have a trailing empty
    // cluster. So the entry for that position must also be invalidated
    aNewSize--;
    TInt firstInvalidIndex=aNewSize>>(iSeekIndexSize+ClusterSizeLog2());
        
    TInt indexLen=KSeekIndexSize-firstInvalidIndex;

    Mem::FillZ(iSeekIndex+firstInvalidIndex, indexLen * sizeof(TUint32));
    }

TInt CFatFileCB::CalcSeekIndexSize(TUint aSize)
//
// Find the nearest power of 2 > aSize
//
    {
    TInt count = 0;
    const TUint indexSize=KSeekIndexSize<<ClusterSizeLog2();//KSeekIndexSize=128
    if (aSize<=indexSize)
      return(count);
    
    while((aSize>>=1)>0)
        {
        count++;
        }
    return (count - (KSeekIndexSizeLog2 + ClusterSizeLog2()) + 1);
    }

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

void CFatFileCB::SetSizeL(TInt64 aSize)
    {
    __PRINT(_L("CFatFileCB::SetSizeL"));
    
    // FAT supports 32 bits only for file size
    if (I64HIGH(aSize))
        User::Leave(KErrNotSupported);

    if(FatMount().IsRuggedFSys())
        DoSetSizeL(I64LOW(aSize),ETrue);
    else
        DoSetSizeL(I64LOW(aSize),EFalse);
    }


void CFatFileCB::SetSizeL(TInt aSize)
//
// Envelope function around DoSetSizeL to enable aSize to
// be written to disk for rugged fat file system
//
    {
    SetSizeL(TInt64(aSize));
    }

void CFatFileCB::DoSetSizeL(TUint aSize,TBool aIsSizeWrite)
//
// Extend or truncate the file.
// Expects the modified attribute and iSize are set afterwards.
// Does not alter iCurrentPos, the current file position.
// Writes size of file to disk if aIsSizeWrite set
//
    {
    __PRINT2(_L("CFatFileCB::DoSetSizeL sz:%d, fileWrite=%d"),aSize ,aIsSizeWrite);

    FatMount().CheckStateConsistentL();
    FatMount().CheckWritableL();

    
    // Can not change the file size if it is clamped
    if(Mount().IsFileClamped(MAKE_TINT64(0,iStartCluster)) > 0)
        User::Leave(KErrInUse);
    
    iFileSizeModified=ETrue;

    TInt newIndexMult=CalcSeekIndexSize(aSize);
    if (iSeekIndex!=NULL && newIndexMult!=iSeekIndexSize)
        ResizeIndex(newIndexMult,aSize);
    if (aSize == 0)
        {
        if (Size() != 0)
            {
            ClearIndex(0); //-- clear seek index array
            TInt cluster=iStartCluster;
            iStartCluster = 0;
            SetSize(0);
            FlushAllL();
            CheckPosL(0);
            FAT().FreeClusterListL(cluster);
            FAT().FlushL();
            }
        return;
        }
    if (aSize<(TUint)Size())
        {
        if(aIsSizeWrite)        // write file size if decreasing
                WriteFileSizeL(aSize);
        CheckPosL(aSize);
        TInt cluster=iCurrentPos.iCluster;
        if (FAT().GetNextClusterL(cluster))
            {
            FAT().WriteFatEntryEofL(iCurrentPos.iCluster);
            FAT().FreeClusterListL(cluster);
            }
        ClearIndex(aSize);
        FAT().FlushL();
        return;
        }
    
    TUint newSize=aSize>>ClusterSizeLog2(); //  Number of clusters we now need
    if (aSize > (newSize<<ClusterSizeLog2()))
        newSize++;  //  File size is not an exact multiple of cluster size
                    //  Increment the number of clusters required to accomodate tail
    
    if (iStartCluster==0)
        {
        //-- FAT().FreeClusterHint() will give us a hint of the last free cluster
        ClearIndex(0); //-- clear seek index array
        TInt tempStartCluster=FAT().AllocateClusterListL(newSize, FAT().FreeClusterHint());
        FAT().FlushL();
        iCurrentPos.iCluster=tempStartCluster;
        iStartCluster=tempStartCluster;
        SetSize(aSize);
        FlushAllL();
        }
    else
        {
        const TUint curSize = (TUint)Size(); 
        TUint oldSize=curSize>>ClusterSizeLog2();   //  Number of clusters we had previously
        if (curSize>(oldSize<<ClusterSizeLog2()))
            oldSize++;
    
        TInt newClusters=newSize-oldSize;   //  Number of clusters we need to prepare
        if (newClusters)
            {
            TEntryPos currentPos=iCurrentPos;
            CheckPosL(Size());
            FAT().ExtendClusterListL(newClusters,iCurrentPos.iCluster);
            iCurrentPos=currentPos;
            }
        FAT().FlushL();
        if(aIsSizeWrite)            // write file size if increasing
            WriteFileSizeL(aSize);
        }
    }

//-----------------------------------------------------------------------------
/**
    Set the entry's attributes and modified time.
*/
void CFatFileCB::SetEntryL(const TTime& aTime,TUint aSetAttMask,TUint aClearAttMask)
    {
    __PRINT(_L("CFatFileCB::SetEntryL"));
    
    FatMount().CheckStateConsistentL();
    FatMount().CheckWritableL();

    TUint setAttMask=aSetAttMask&KEntryAttMaskSupported;
    if (setAttMask|aClearAttMask)
        {
        iAtt|=setAttMask;
        iAtt&=(~aClearAttMask);
        }
    if (aSetAttMask&KEntryAttModified)
        iModified=aTime;
    iAtt|=KEntryAttModified;
    }

/**
    This is a RuggedFAT - specific method. Writes file size to the corresponding field of this
    file direcrory entry.
*/
void CFatFileCB::WriteFileSizeL(TUint aSize)
    {
    __PRINT(_L("CFatFileCB::WriteFileSizeL"));
    TEntryPos entryPos=iFileDirPos;
    entryPos.iPos+=_FOFF(SFatDirEntry,iSize);
    TPtrC8 size((TUint8*)&aSize,sizeof(TUint));
    
    //-- use directory cache when dealing with directories
    FatMount().DirWriteL(entryPos,size);
    iFileSizeModified=EFalse;
    }

//-----------------------------------------------------------------------------
/** 
    Flush file size, attributes, time etc. to the media.
    It doesn't matter if whole directory entry is being written of only part of it. Anyway, a single DOS
    dir. entry always fits into 1 sector.
*/
void CFatFileCB::FlushDataL()
    {
    __PRINT(_L("CFatFileCB::FlushDataL"));
    FlushAllL();
    }

//-----------------------------------------------------------------------------
/** 
    Flush the fide directory entry data: files size, attributes, time etc. 
*/
void CFatFileCB::FlushAllL()
    {
    __PRINT(_L("CFatFileCB::FlushAllL()"));

    if (Mount().IsCurrentMount()==EFalse)
        User::Leave(KErrDisMounted);

    FatMount().CheckStateConsistentL();
    FatMount().CheckWritableL();

    TFatDirEntry entry;
    FatMount().ReadDirEntryL(iFileDirPos,entry);
    __ASSERT_ALWAYS(entry.IsEndOfDirectory()==EFalse,User::Leave(KErrCorrupt));
    entry.SetAttributes(iAtt&KEntryAttMaskSupported);
    entry.SetSize(Size());
    entry.SetTime(iModified, FatMount().TimeOffset());
    entry.SetStartCluster(iStartCluster);

    TBool setNotify = FatMount().GetNotifyUser();
    if(setNotify)
        {
        FatMount().SetNotifyOff();  // do not launch a notifier
        }

    TRAPD(ret, FatMount().WriteDirEntryL(iFileDirPos,entry));
    
    if(setNotify)
        {
        FatMount().SetNotifyOn();
        }

    User::LeaveIfError(ret);
    iAtt&=(~KEntryAttModified);
    iFileSizeModified=EFalse;
    }

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

/**
    Rename already opened file.
    @param  aNewName new file name; all trailing dots from the name will be removed
*/
void CFatFileCB::RenameL(const TDesC& aNewName)
    {
    __PRINT2(_L("CFatFileCB::RenameL[0x%x], name:%S"),this, &aNewName);

    FatMount().CheckStateConsistentL();
    FatMount().CheckWritableL();

    const TPtrC fileName = RemoveTrailingDots(aNewName); //-- remove trailing dots from the name


    FatMount().DoRenameOrReplaceL(*iFileName, fileName, CFatMountCB::EModeRename,iFileDirPos);
    
    AllocBufferL(iFileName, fileName);
    
    if(!FatMount().IsRuggedFSys())
        FAT().FlushL();
    }


//***********************************************************
//* BlockMap interface
//***********************************************************
    
TInt CFatFileCB::BlockMap(SBlockMapInfo& aInfo, TInt64& aStartPos, TInt64 aEndPos)
//
// Retrieves the block map of a given section of the file, in the FAT file system.
//  
    {
    __PRINT2(_L("CFatFileCB::BlockMap aStartPos=%ld aEndPos=%ld"), aStartPos, aEndPos);
    
    if ( I64HIGH(aStartPos) || I64HIGH(aEndPos) )
        return KErrNotSupported;

    TUint startPos = I64LOW(aStartPos);
    TUint endPos = I64LOW(aEndPos);

    // aEndPos will always be >=0 at this point
    const TUint length = endPos - startPos;
    
    // Store the position of cluster zero in aInfo
    CFatMountCB& fatMount = FatMount();

    TInt drvNo=-1;
    TBusLocalDrive* locDrv;
    if((fatMount.LocalDrive()->GetLocalDrive(locDrv)==KErrNone) && ((drvNo=GetLocalDriveNumber(locDrv))>=0) && (drvNo<KMaxLocalDrives))
        aInfo.iLocalDriveNumber=drvNo;
    else
        return KErrNotSupported;

    // Fetch the address of cluster 0
    aInfo.iStartBlockAddress = fatMount.FAT().DataPositionInBytes(KFirstClusterNum);

    TRAPD(r, CheckPosL(startPos));
    if (r != KErrNone)
        return r;

    aInfo.iBlockStartOffset = fatMount.ClusterRelativePos(iCurrentPos.iPos);
    aInfo.iBlockGranularity = 1 << FatMount().ClusterSizeLog2();
    const TUint myStartPos = iCurrentPos.iPos;
    if ( myStartPos + length > (TUint)Size())
        return KErrArgument;

    TRAP(r, FatMount().BlockMapReadFromClusterListL(iCurrentPos, length, aInfo));
    if (r != KErrNone)
        return r;

    aStartPos = iCurrentPos.iPos;
    if ((I64LOW(aStartPos) == (TUint)Size()) || ( I64LOW(aStartPos) == (myStartPos + length)))
        return KErrCompletion;
    else
        return KErrNone;
    }



TInt CFatFileCB::GetInterface(TInt aInterfaceId,TAny*& aInterface,TAny* aInput)
    {
    switch(aInterfaceId)
        {
        case EExtendedFileInterface:
            ((CFileCB::MExtendedFileInterface*&) aInterface) = this;
            return KErrNone;

        case EBlockMapInterface:
            aInterface = (CFileCB::MBlockMapInterface*) this;
            return KErrNone;

        case EGetLocalDrive:
            return FatMount().LocalDrive()->GetLocalDrive((TBusLocalDrive*&) aInterface);

        default:
            return CFileCB::GetInterface(aInterfaceId,aInterface,aInput);
        }
    }




/**
    Overwrites file's start cluster (iStartCluster) in its directory entry.
*/
void CFatFileCB::FlushStartClusterL()
    {
    __PRINT(_L("CFatFileCB::FlushStartClusterL"));

    CFatMountCB& mount = FatMount();
    TFatDirEntry dirEntry;
    
    mount.ReadDirEntryL(iFileDirPos, dirEntry);      //-- read this file's dir. entry
    dirEntry.SetStartCluster(iStartCluster);         //-- set new start cluster
    mount.WriteDirEntryL(iFileDirPos, dirEntry);//-- write the entry back
    }





