// 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_fat16.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"
const TUint KDefFatResvdSec = 1; ///< default number of FAT12/16 reserved sectors
/**
Initialize the format parameters for a normal fixed sized disk
Setting set to adhere to Rules of Count of clusters for FAT type
@param aDiskSizeInSectors Size of volume in sectors
@return system-wide error code
*/
TInt CFatFormatCB::InitFormatDataForFixedSizeDiskNormal(TInt aDiskSizeInSectors, const TLocalDriveCapsV6& aCaps)
{
if( Drive().IsRemovable() )
iNumberOfFats = KNumberOfFatsExternal;
else
iNumberOfFats = KNumberOfFatsInternal;
iReservedSectors=KDefFatResvdSec;
if (aDiskSizeInSectors<4084*1) // < 2MB
{
iRootDirEntries=128;
iSectorsPerCluster=1;
iFileSystemName=KFileSystemName12;
iSectorsPerFat=MaxFat12Sectors();
}
else if (aDiskSizeInSectors<4084*2) // < 4MB (8168 sectors)
{
iRootDirEntries=256;
iSectorsPerCluster=2;
iFileSystemName=KFileSystemName12;
iSectorsPerFat=MaxFat12Sectors();
}
else if (aDiskSizeInSectors<4084*4) // < 8MB (16336 sectors)
{
iRootDirEntries=512;
iSectorsPerCluster=4;
iFileSystemName=KFileSystemName12;
iSectorsPerFat=MaxFat12Sectors();
}
else if (aDiskSizeInSectors<4084*8) // < 16MB (32672 sectors)
{
iRootDirEntries=512;
iSectorsPerCluster=8;
iFileSystemName=KFileSystemName12;
iSectorsPerFat=MaxFat12Sectors();
}
else // >= 16Mb - FAT16
{
iFileSystemName=KFileSystemName16;
TInt minSectorsPerCluster=(aDiskSizeInSectors+KMaxFAT16Entries-1)/KMaxFAT16Entries;
iRootDirEntries=512;
iSectorsPerCluster=1;
while (minSectorsPerCluster>iSectorsPerCluster)
iSectorsPerCluster<<=1;
iSectorsPerFat=MaxFat16Sectors();
}
// Ensure cluster size is a multiple of the block size
TInt blockSizeInSectors = aCaps.iBlockSize >> iSectorSizeLog2;
__PRINT1(_L("blockSizeInSectors: %d"),blockSizeInSectors);
ASSERT(blockSizeInSectors == 0 || IsPowerOf2(blockSizeInSectors));
if (blockSizeInSectors != 0 && IsPowerOf2(blockSizeInSectors))
{
__PRINT1(_L("iSectorsPerCluster (old): %d"),iSectorsPerCluster);
AdjustClusterSize(blockSizeInSectors);
__PRINT1(_L("iSectorsPerCluster (new): %d"),iSectorsPerCluster);
}
// Align first data sector on an erase block boundary if
// (1) the iEraseBlockSize is specified
// (2) the start of the partition is already aligned to an erase block boundary,
// i.e. iHiddenSectors is zero or a multiple of iEraseBlockSize
__PRINT1(_L("iHiddenSectors: %d"),iHiddenSectors);
TInt eraseblockSizeInSectors = aCaps.iEraseBlockSize >> iSectorSizeLog2;
__PRINT1(_L("eraseblockSizeInSectors: %d"),eraseblockSizeInSectors);
ASSERT(eraseblockSizeInSectors == 0 || IsPowerOf2(eraseblockSizeInSectors));
ASSERT(eraseblockSizeInSectors == 0 || eraseblockSizeInSectors >= blockSizeInSectors);
if ((eraseblockSizeInSectors != 0) &&
(iHiddenSectors % eraseblockSizeInSectors == 0) &&
(IsPowerOf2(eraseblockSizeInSectors)) &&
(eraseblockSizeInSectors >= blockSizeInSectors))
{
TInt r = AdjustFirstDataSectorAlignment(eraseblockSizeInSectors);
ASSERT(r == KErrNone);
(void) r;
}
__PRINT1(_L("iReservedSectors: %d"),iReservedSectors);
__PRINT1(_L("FirstDataSector: %d"), FirstDataSector());
return KErrNone;
}
TInt CFatFormatCB::FirstDataSector() const
{
TInt rootDirSectors = (iRootDirEntries * KSizeOfFatDirEntry + (iBytesPerSector-1)) / iBytesPerSector;
return iHiddenSectors + iReservedSectors + iNumberOfFats*iSectorsPerFat + rootDirSectors;
}
void CFatFormatCB::AdjustClusterSize(TInt aRecommendedSectorsPerCluster)
{
const TInt KMaxSecPerCluster = 64; // 32K
while (aRecommendedSectorsPerCluster > iSectorsPerCluster && iSectorsPerCluster <= (KMaxSecPerCluster/2))
iSectorsPerCluster<<= 1;
}
// AdjustFirstDataSectorAlignment()
// Attempts to align the first data sector on an erase block boundary by modifying the
// number of reserved sectors.
TInt CFatFormatCB::AdjustFirstDataSectorAlignment(TInt aEraseBlockSizeInSectors)
{
const TBool bFat16 = Is16BitFat();
// Save these 2 values in the event of a convergence failure; this should
// hopefully never happen, but we will cater for this in release mode to be safe,
TInt reservedSectorsSaved = iReservedSectors;
TInt sectorsPerFatSaved = iSectorsPerFat;
TInt reservedSectorsOld = 0;
// zero for FAT32
TInt rootDirSectors = (iRootDirEntries * KSizeOfFatDirEntry + (iBytesPerSector-1)) / iBytesPerSector;
TInt fatSectors = 0;
TInt KMaxIterations = 10;
TInt n;
for (n=0; n<KMaxIterations && reservedSectorsOld != iReservedSectors; n++)
{
reservedSectorsOld = iReservedSectors;
iSectorsPerFat = bFat16 ? MaxFat16Sectors() : MaxFat12Sectors();
fatSectors = iSectorsPerFat * iNumberOfFats;
// calculate number of blocks
TInt nBlocks = (iReservedSectors + fatSectors + rootDirSectors + aEraseBlockSizeInSectors-1) / aEraseBlockSizeInSectors;
iReservedSectors = (nBlocks * aEraseBlockSizeInSectors) - rootDirSectors - fatSectors;
}
ASSERT(iReservedSectors >= (TInt) KDefFatResvdSec);
if ((FirstDataSector() & (aEraseBlockSizeInSectors-1)) == 0)
{
return KErrNone;
}
else
{
iReservedSectors = reservedSectorsSaved;
iSectorsPerFat = sectorsPerFatSaved;
return KErrGeneral;
}
}
/**
Initialize the user specific format parameters for fixed sized disk.
@param aDiskSizeInSectors disk size in sectors
@return system-wide error code
*/
TInt CFatFormatCB::InitFormatDataForFixedSizeDiskUser(TInt aDiskSizeInSectors)
{
//-- KErrArgument will be returned if iSpecialInfo().iFATBits isn't one of EFB32, EFB16, EFB32
if(iSpecialInfo().iFlags & TLDFormatInfo::EOneFatTable)
iNumberOfFats = 1;
else if(iSpecialInfo().iFlags & TLDFormatInfo::ETwoFatTables)
iNumberOfFats = 2;
else if(Drive().IsRemovable())
iNumberOfFats = KNumberOfFatsExternal;
else
iNumberOfFats = KNumberOfFatsInternal;
if(iSpecialInfo().iReservedSectors == 0)
iReservedSectors = KDefFatResvdSec; //-- user hasn't specified reserved sectors count, use default (FAT12/16)
else
iReservedSectors = iSpecialInfo().iReservedSectors;
const TInt KMaxSecPerCluster = 64;
const TInt KDefaultSecPerCluster= 8; //-- default value, if the iSpecialInfo().iSectorsPerCluster isn't specified
iSectorsPerCluster = iSpecialInfo().iSectorsPerCluster;
if(iSectorsPerCluster <= 0)
{//-- default value, user hasn't specified TLDFormatInfo::iSectorsPerCluster
iSectorsPerCluster = KDefaultSecPerCluster; //-- will be adjusted later
}
else
{
iSectorsPerCluster = Min(1<<Log2(iSectorsPerCluster), KMaxSecPerCluster);
}
//-----------------------------------------
if (aDiskSizeInSectors < 4096) // < 2MB
{
iSectorsPerCluster = 1;
iRootDirEntries = 128;
}
else if (aDiskSizeInSectors < 8192) // < 4MB
{
iSectorsPerCluster = Min(iSectorsPerCluster, 2);
iRootDirEntries = 256;
}
else if (aDiskSizeInSectors < 32768) // < 16MB
{
iSectorsPerCluster = Min(iSectorsPerCluster, 4);
iRootDirEntries = 512;
}
else if (aDiskSizeInSectors < 131072) // < 64MB
{
iSectorsPerCluster = Min(iSectorsPerCluster, 8);
iRootDirEntries = 512;
}
else // >= 64Mb
iRootDirEntries = 512;
//-----------------------------------------
TLDFormatInfo::TFATBits fatBits = iSpecialInfo().iFATBits;
if (fatBits == TLDFormatInfo::EFBDontCare)
{
const TFatType fatType = SuggestFatType();
switch(fatType)
{
case EFat12:
fatBits = TLDFormatInfo::EFB12;
break;
case EFat16:
fatBits = TLDFormatInfo::EFB16;
break;
case EFat32:
fatBits = TLDFormatInfo::EFB32;
break;
case EInvalid:
ASSERT(0);
}
}
TFatType reqFatType(EInvalid); //-- requested FAT type
switch (fatBits)
{
case TLDFormatInfo::EFB12:
iFileSystemName=KFileSystemName12;
iSectorsPerFat=MaxFat12Sectors();
reqFatType = EFat12;
break;
case TLDFormatInfo::EFB16:
iFileSystemName=KFileSystemName16;
iSectorsPerFat=MaxFat16Sectors();
reqFatType = EFat16;
break;
case TLDFormatInfo::EFB32:
__PRINT(_L("CFatFormatCB::InitFormatDataForFixedSizeDiskUser() FAT32 Not supported!"));
return KErrNotSupported;
default:
__PRINT(_L("CFatFormatCB::InitFormatDataForFixedSizeDiskUser() Incorrect FAT type specifier!"));
return KErrArgument;
};
//-- check if we can format the volume with requested FAT type
const TFatType fatType = SuggestFatType();
if(fatType != reqFatType)
{//-- volume metrics don't correspond to the requested FAT type
__PRINT(_L("CFatFormatCB::InitFormatDataForFixedSizeDiskUser() FAT type mismatch!"));
return KErrArgument;
}
return KErrNone;
}
/**
Initialize the format parameters for a custom fixed sized disk
@param aFormatInfo The custom format parameters
@return system-wide error code
*/
TInt CFatFormatCB::InitFormatDataForFixedSizeDiskCustom(const TLDFormatInfo& aFormatInfo)
{
if(aFormatInfo.iFlags & TLDFormatInfo::EOneFatTable)
iNumberOfFats = 1;
else if(aFormatInfo.iFlags & TLDFormatInfo::ETwoFatTables)
iNumberOfFats = 2;
else if(Drive().IsRemovable())
iNumberOfFats = KNumberOfFatsExternal;
else
iNumberOfFats = KNumberOfFatsInternal;
iRootDirEntries=512;
iSectorsPerCluster = aFormatInfo.iSectorsPerCluster;
iSectorsPerTrack = aFormatInfo.iSectorsPerTrack;
iNumberOfHeads = aFormatInfo.iNumberOfSides;
iReservedSectors = aFormatInfo.iReservedSectors ? aFormatInfo.iReservedSectors : KDefFatResvdSec;
switch (aFormatInfo.iFATBits)
{
case TLDFormatInfo::EFB12:
iFileSystemName = KFileSystemName12;
iSectorsPerFat = MaxFat12Sectors();
break;
case TLDFormatInfo::EFB16:
iFileSystemName = KFileSystemName16;
iSectorsPerFat = MaxFat16Sectors();
break;
default:
{
TInt64 clusters64 = (aFormatInfo.iCapacity / KDefaultSectorSize) / iSectorsPerCluster;
TInt clusters = I64LOW(clusters64);
if (clusters < 4085)
{
iFileSystemName = KFileSystemName12;
iSectorsPerFat = MaxFat12Sectors();
}
else
{
iFileSystemName = KFileSystemName16;
iSectorsPerFat = MaxFat16Sectors();
}
}
}
return KErrNone;
}
void CFatFormatCB::RecordOldInfoL()
{
__PRINT(_L("CFatFormatCB::RecordOldInfoL"));
// Check if mount or disk is corrupt
// This should be stored in member variable because FatMount is remounted
// every time RFormat::Next() gets called thus FatMount().Initialised()
// will be inconsistent with previous state.
TLocalDriveCapsV3Buf caps;
User::LeaveIfError(LocalDrive()->Caps(caps));
iVariableSize=((caps().iMediaAtt)&KMediaAttVariableSize) ? (TBool)ETrue : (TBool)EFalse;
iDiskCorrupt = !FatMount().ConsistentState();
iBadClusters.Reset();
iBadSectors.Reset();
if (!iVariableSize && !iDiskCorrupt && (iMode & EQuickFormat))
{
iOldFirstFreeSector = FatMount().iFirstFreeByte >> FatMount().SectorSizeLog2();
iOldSectorsPerCluster = FatMount().SectorsPerCluster();
FatMount().FAT().InvalidateCacheL(); //-- invalidate whole FAT cache
const TInt maxClusterNum = FatMount().iUsableClusters + KFatFirstSearchCluster;
// Collect bad cluster information from current FAT table
const TUint32 mark = FatMount().Is16BitFat() ? KBad_16Bit : KBad_12Bit;
for (TInt i=KFatFirstSearchCluster; i<maxClusterNum; i++)
if (FatMount().FAT().ReadL(i) == mark)
iBadClusters.AppendL(i);
}
}
/**
Create the boot sector on media for the volume.
@leave System wide error codes
*/
void CFatFormatCB::CreateBootSectorL()
{
__PRINT1(_L("CFatFormatCB::CreateBootSector() drive:%d"),DriveNumber());
TFatBootSector bootSector;
bootSector.SetVendorID(KDefaultVendorID);
bootSector.SetBytesPerSector(iBytesPerSector);
bootSector.SetSectorsPerCluster(iSectorsPerCluster);
bootSector.SetReservedSectors(iReservedSectors);
bootSector.SetNumberOfFats(iNumberOfFats);
bootSector.SetRootDirEntries(iRootDirEntries);
if (iMaxDiskSectors<(TInt)KMaxTUint16)
bootSector.SetTotalSectors(iMaxDiskSectors);
else
{
bootSector.SetTotalSectors(0);
bootSector.SetHugeSectors(iMaxDiskSectors);
}
TInt numberOfClusters=iMaxDiskSectors/iSectorsPerCluster;
if (numberOfClusters>(TInt)KMaxTUint16)
User::Leave(KErrTooBig);
bootSector.SetFatSectors(iSectorsPerFat);
bootSector.SetReservedByte(0);
TTime timeID;
timeID.HomeTime(); // System time in future?
bootSector.SetUniqueID(I64LOW(timeID.Int64())); // Generate UniqueID from time
bootSector.SetVolumeLabel(_L8(""));
bootSector.SetFileSysType(iFileSystemName);
// Floppy specific info:
bootSector.SetJumpInstruction();
bootSector.SetMediaDescriptor(KBootSectorMediaDescriptor);
bootSector.SetNumberOfHeads(iNumberOfHeads);
bootSector.SetHiddenSectors(iHiddenSectors);
bootSector.SetSectorsPerTrack(iSectorsPerTrack);
bootSector.SetPhysicalDriveNumber(128);
bootSector.SetExtendedBootSignature(0x29);
User::LeaveIfError(FatMount().DoWriteBootSector(KBootSectorNum*bootSector.BytesPerSector(), bootSector));
}
//-------------------------------------------------------------------------------------------------------------------
/**
Format a disk section, called iteratively to erase whole of media, on last iteration
creates an empty volume. If called with quick formatonly erases the Fat leaving the
rest of the volume intact.
@leave System wide error code
*/
void CFatFormatCB::DoFormatStepL()
{
if (iFormatInfo.iFormatIsCurrent==EFalse)
{
if (iMode & EForceErase)
{
TInt r = FatMount().ErasePassword();
User::LeaveIfError(r);
// CFatMountCB::ErasePassword() calls TBusLocalDrive::ForceRemount(),
// so need to stop a remount from occurring in next call to :
// TFsFormatNext::DoRequestL((), TDrive::CheckMount().
FatMount().Drive().SetChanged(EFalse);
}
RecordOldInfoL();
InitializeFormatDataL();
FatMount().DoDismount();
if (iVariableSize)
FatMount().ReduceSizeL(0,I64LOW(FatMount().iSize));
}
//
// Blank disk if not EQuickFormat
//
if (!iVariableSize && !(iMode & EQuickFormat) && iCurrentStep)
{
if (iFormatInfo.iFormatIsCurrent == EFalse)
{//-- firstly invalidate sectors 0-6 inclusive
DoZeroFillMediaL(0, 7*iBytesPerSector);
}
TInt ret=FatMount().LocalDrive()->Format(iFormatInfo);
if (ret!=KErrNone && ret!=KErrEof) // Handle format error
ret = HandleCorrupt(ret);
if (ret!=KErrNone && ret!=KErrEof) // KErrEof could be set by LocalDrive()->Format()
User::Leave(ret);
if (ret==KErrNone)
{
iCurrentStep=100-(100*iFormatInfo.i512ByteSectorsFormatted)/iMaxDiskSectors;
if (iCurrentStep<=0)
iCurrentStep=1;
return;
}
}
// ReMount since MBR may have been rewritten and partition may have moved / changed size
TInt ret = LocalDrive()->ForceRemount(0);
if (ret != KErrNone && ret != KErrNotSupported)
User::Leave(ret);
// MBR may have changed, so need to re-read iHiddenSectors etc.before BPB is written
InitializeFormatDataL();
// Translate bad sector number to cluster number which contains that sector
// This only happens in full format, in quick format they are already cluster numbers
if (!iVariableSize && !(iMode & EQuickFormat))
User::LeaveIfError(BadSectorToCluster());
//
// Do the rest of the disk in one lump
//
iCurrentStep=0;
//-- zero-fill media from position 0 to the FAT end, i.e main & backup boot sector, FSInfo and its copy and all FATs
const TUint32 posFatEnd = ((iSectorsPerFat*iNumberOfFats) + iReservedSectors) * iBytesPerSector; //-- last FAT end position
if (iVariableSize)
FatMount().EnlargeL(posFatEnd);
DoZeroFillMediaL(0, posFatEnd);
//-- Zero fill root directory
const TInt rootDirSector = iReservedSectors + (iNumberOfFats * iSectorsPerFat);
const TInt rootDirSize = iRootDirEntries * KSizeOfFatDirEntry; //-- size in bytes
const TUint32 posRootDirStart = rootDirSector * iBytesPerSector;
const TUint32 posRootDirEnd = posRootDirStart + rootDirSize;
const TInt numOfRootSectors=(rootDirSize%iBytesPerSector) ? (rootDirSize/iBytesPerSector+1) : (rootDirSize/iBytesPerSector);
if (iVariableSize)
FatMount().EnlargeL(iBytesPerSector*numOfRootSectors);
DoZeroFillMediaL(posRootDirStart, posRootDirEnd);
// Enlarge ram drive to take into account rounding of
// data start to cluster boundary
if(iVariableSize && iSectorsPerCluster!=1)
{
const TInt firstFreeSector=rootDirSector+numOfRootSectors;
const TInt firstFreeCluster=firstFreeSector%iSectorsPerCluster ? firstFreeSector/iSectorsPerCluster+1 : firstFreeSector/iSectorsPerCluster;
const TInt alignedSector=firstFreeCluster*iSectorsPerCluster;
if(alignedSector!=firstFreeSector)
FatMount().EnlargeL((alignedSector-firstFreeSector)*iBytesPerSector);
}
//-- FAT[0] must contain media descriptor in the low byte, FAT[1] for fat16/32 may contain some flags
TBuf8<4> startFat(4);
startFat.Fill(0xFF);
if(iVariableSize||Is16BitFat()) //-- FAT16 or RAM drive which is always FAT16
{
startFat.SetLength(4);
}
else //-- FAT12
{
startFat.SetLength(3);
}
startFat[0]=KBootSectorMediaDescriptor;
//-- write FAT[0] and FAT[1] entries to all copies of FAT
for(TInt i=0;i<iNumberOfFats;i++)
{
User::LeaveIfError(LocalDrive()->Write(iBytesPerSector*(iReservedSectors+(iSectorsPerFat*i)),startFat));
}
//-- create boot sectors
CreateBootSectorL();
//-- here we have bad clusters numbers saved by the quick format
//-- Interpret old bad cluster number to new cluster number and mark new bad clusters
if (!iVariableSize && iBadClusters.Count()>0)
{
//-- Here we need fully mounted volume, so mount it normally.
FatMount().MountL(EFalse);
iBadClusters.Sort();
TranslateL();
TInt mark = FatMount().Is16BitFat() ? KBad_16Bit : KBad_12Bit;
TInt i;
for (i=0; i<iBadClusters.Count(); ++i)
FatMount().FAT().WriteL(iBadClusters[i], mark);
FatMount().FAT().FlushL();
#if defined(_DEBUG)
TInt r=FatMount().CheckDisk();
__PRINT1(_L("CFatFormatCB::DoFormatStepL() CheckDisk res: %d"),r);
#endif
}
else
{
//-- We do not need to perform full mount in this case, the TDrive object will be marked as changed in ~CFormatCB and the
//-- mount will be closed. Therefore on the first access to it it will be mounted normally.
FatMount().MountL(ETrue); //-- force mount
}
__PRINT1(_L("CFatFormatCB::DoFormatStepL() Format complete drv:%d"), DriveNumber());
}
TInt CFatFormatCB::BadSectorToCluster()
{
const TInt sizeofFatAndRootDir = iSectorsPerFat*iNumberOfFats + ((iRootDirEntries*KSizeOfFatDirEntry+(1<<iSectorSizeLog2)-1)>>iSectorSizeLog2);
TInt firstFreeSector = iReservedSectors + sizeofFatAndRootDir;
TInt i, r;
for (i=0; i<iBadSectors.Count(); ++i)
{
TInt badSector = iBadSectors[i];
// Check in rare case that corrupt in critical area
// which includes bootsector, FAT table, (and root dir if not FAT32)
if (badSector < firstFreeSector)
{
if (badSector == 0) // Boot sector corrupt
return KErrCorrupt;
if (badSector < iReservedSectors) // Harmless in reserved area
continue;
// Extend reserved area to cover bad sector
iReservedSectors = badSector + 1;
firstFreeSector = iReservedSectors + sizeofFatAndRootDir;
continue;
}
// Figure out bad cluster number and record it
TInt cluster = (badSector-firstFreeSector)/iSectorsPerCluster + KFatFirstSearchCluster;
if (iBadClusters.Find(cluster) == KErrNotFound)
{
if ((r=iBadClusters.Append(cluster)) != KErrNone)
return r;
}
}
return KErrNone;
}