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