userlibandfileserver/fileserver/sfat/sl_mnt16.cpp
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:29:07 +0100
changeset 30 8aab599e3476
parent 2 4122176ea935
permissions -rw-r--r--
Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h) Have multiple extension sections in the bld.inf, one for each version of the compiler. The RVCT version building the tools will build the runtime libraries for its version, but make sure we extract all the other versions from zip archives. Also add the archive for RVCT4.

// 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_mnt16.cpp
// CFatMountCB code, specific to the EFAT.FSY
// 
//

/**
 @file 
*/

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


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


//-------------------------------------------------------------------------------------------------------------------
/**
    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);
    ASSERT(r==KErrNone);
    
    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.
    @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 (N/A for FAT12/16)
    @return     standard error code.
*/
TInt CFatMountCB::ReadBootSector(TFatBootSector& aBootSector, TBool /*aDoNotReadBkBootSec=EFalse*/)
    {
    //-- read main boot sector from the sector 0
    TInt nRes = DoReadBootSector(KBootSectorNum << KDefSectorSzLog2, aBootSector); 
    if(nRes == KErrNone)
        {
        if(aBootSector.IsValid())
            {//-- main boot sector is valid, everything is OK
            return KErrNone;
            }
        else
            {
            __PRINT(_L("Boot Sector is invalid! dump:\n"));
            aBootSector.PrintDebugInfo();
            return KErrCorrupt;
            }
        }

    //-- can't read boot sector
    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);

    User::LeaveIfError(LocalDrive()->Write(KFat16VolumeLabelPos,aVolumeLabel)); 
    }



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

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[1]

    if(Is16BitFat())
        {//-- Fat16
        __PRINT2(_L("#- CFatMountCB::SetVolumeCleanL, drive:%d, param:%d, FAT16, efat.fsy"),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
                    }
                }
            
             //-- 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);

            __PRINT2(_L("#- CFatMountCB::SetVolumeCleanL() entry:  %x->%x"), tmp, fatEntry);    
        
            }
        else //if(FatConfig().FAT16_UseCleanShutDownBit())
            {
            __PRINT(_L("#- changing FAT16[1] is disabled in config!"));    
            }
       
        }// if(Is16BitFat())
    else
        {//-- must never get here
        ASSERT(0);
        }
    
    }

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

/**
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()
    {
    TFatDriveInterface& drive = DriveInterface();
    
    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(Drive().DriveNumber()));

    //-- 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 and validate boot sector (sector 0)
    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()

    //-- values from the boot sector are checked in TFatBootSector::IsValid()
    //-- 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
@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
        {
        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();
    

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

    //========== 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;
        }

    //==========  find out free clusters number on the volume
    FAT().CountFreeClustersL();
    
    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);

    __PRINT3(_L("#- CFatMountCB::InitializeL() done. drv:%d, Free clusters:%d, 1st Free cluster:%d"),DriveNumber(), FAT().NumberOfFreeClusters(), FAT().FreeClusterHint());
    }

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



/**
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(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(Is16BitFat())
        aCluster=0xFFF8;
    else
        aCluster=0xFF8;
    }

/**
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(0);     
    }


/**
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;
            }
        default:
        // case EInvalidFatType
            return KErrNotReady;
        }
    }

//-------------------------------------------------------------------------------------------------------------------
/**
    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)
    {
    TInt nRes = KErrNotSupported; 

    if(aLevel == ECheckFsMountable)
        {
        return MntCtl_DoCheckFileSystemMountable();
        }
    
    //-- 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;
        }

    //-- 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 nRes;
    }

//-------------------------------------------------------------------------------------------------------------------
/**
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;
        
        default:
            return(CMountCB::GetInterface(aInterfaceId, aInterface, aInput));
        }
    }