userlibandfileserver/fileserver/sfat/sl_vfat.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Thu, 19 Aug 2010 11:14:22 +0300
branchRCL_3
changeset 42 a179b74831c9
parent 2 4122176ea935
permissions -rw-r--r--
Revision: 201033 Kit: 201033

// 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_vfat.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 <e32svr.h>
#include <e32math.h>


IMPORT_C const TFatUtilityFunctions* GetFatUtilityFunctions();

const TInt KMaxLengthWithoutTilde = 8;
const TUint8 KLeadingE5Replacement = 0x05;

// use second half of ISO Latin 1 character set for extended chars
const TUint KExtendedCharStart=0x80;
const TUint KExtendedCharEnd=0xff;

LOCAL_C TBool IsLegalChar(TChar aCharacter,TBool aAllowWildChars,TBool aUseExtendedChars=EFalse,TBool aInScanDrive=EFalse)
//
// Returns ETrue if aCharacter is legal inside a dos filename
//
    {
    if ((aCharacter==KMatchOne) || (aCharacter==KMatchAny))
        return(aAllowWildChars);
    if ((TUint)aCharacter < 0x20)
        return EFalse;
    // Don't check illegal ascii char because some non-English char value may
    // fall in this area
    if (aInScanDrive)
        return ETrue;
    return LocaleUtils::IsLegalShortNameCharacter(aCharacter,aUseExtendedChars);
    }

LOCAL_C void ReplaceFirstCharacterIfClashesWithE5L(TDes8& aShortName)
    {
    if (0 < aShortName.Length() && aShortName[0] == KEntryErasedMarker)
        {
        aShortName[0] = KLeadingE5Replacement;
        }
    }

LOCAL_C void ReplaceIllegalCharactersL(TDes& aLongName, TUint aCharacterToReplaceWith)
    {
    TBool alreadyFoundExtensionDelimiter=EFalse;

    TInt LongNameLen = aLongName.Length();
    TInt extDelimiterIndex = aLongName.LocateReverse(KExtDelimiter);

    for (TInt i=LongNameLen-1; i>=0; --i) // iterate backwards as aLongName may change length during the loop, and also because we want to leave the *right-most* occurrence of KExtDelimiter unchanged
        {
        TUint character=aLongName[i];
        if (character==(TUint)KExtDelimiter)
            {
            if (alreadyFoundExtensionDelimiter)
                {
                aLongName[i]=(TText)aCharacterToReplaceWith; // A.B.C becomes A_B.C
                }
            alreadyFoundExtensionDelimiter=ETrue;
            }
        else
            {
            // the code below doesn't need any #if defined(_UNICODE) stuff as a narrow-build aLongName would not contain values above 0xff (which is well below the surrogates area in Unicode 0xd800-0xdfff)
            TBool isSurrogatePair=EFalse;

            // LAST character in file name or file ext CAN NOT be HIGH surrogate
            if (i==LongNameLen-1 || i==extDelimiterIndex-1)
                {
                if (IsHighSurrogate((TText16)character))
                    {
                    // Corrupt surrogate
                    User::Leave(KErrBadName);
                    }
                }
            // FIRST character in file name or file ext CAN NOT be LOW surrogate
            if (i==0 || i==extDelimiterIndex+1)
                {
                if (IsLowSurrogate((TText16)character))
                    {
                    // Corrupt surrogate
                    User::Leave(KErrBadName);
                    }
                }
            // if LOW Surrogate
            if (IsLowSurrogate((TText16)character))
                {
                // check for HIGH surrogate
                if (!IsHighSurrogate(aLongName[--i]))
                    {
                    // Corrupt surrogate
                    User::Leave(KErrBadName);
                    }
                // surrogate pair found
                character&=~0xdc00;
                character|=((aLongName[i]&~0xd800)<<10);
                character+=0x00010000; // this must be added - it cannot be bitwise-"or"-ed
                isSurrogatePair=ETrue;
                }

            // if High Surrogate
            if (!isSurrogatePair && IsHighSurrogate((TText16)character))
                {
                // Corrupt surrogate
                User::Leave(KErrBadName);
                }

            if (!IsLegalChar(character, EFalse))
                {
                if (isSurrogatePair)
                    {
                    aLongName.Delete(i+1, 1);
                    }
                aLongName[i]=(TText)aCharacterToReplaceWith;
                }
            }
        }
    }

TShortName DoGenerateShortNameL(const TDesC& aLongName,TInt& aNum,TBool aUseTildeSelectively)
//
// Create a legal shortname from aLongName
//
    {

    TFileName longName(aLongName);
    longName.UpperCase();
    ReplaceIllegalCharactersL(longName, '_');
    TPtrC longNameWithoutExtension(longName);
    TPtrC longNameExtension(KNullDesC);
    const TInt positionOfExtension=longName.LocateReverse(KExtDelimiter);
    if (positionOfExtension==0)
        {
        // No filename specified, so use the extension as the basis of the shortname.
        // Make sure we always append a tilde+number in this case to avoid generating the same 
        // short filename as one of the protected folders ("\SYS", "\RESOURCE","\PRIVATE")
        longNameWithoutExtension.Set(longName.Mid(positionOfExtension+1));
        aUseTildeSelectively = EFalse;
        if (aNum < 0)
            aNum = 1;
        }
    else if (positionOfExtension!=KErrNotFound)
        {
        longNameWithoutExtension.Set(longName.Left(positionOfExtension));
        longNameExtension.Set(longName.Mid(positionOfExtension+1));
        }
    
    // Converts the original file name main part into 8-bit character string
    TShortName tempShortName(0);

    LocaleUtils::ConvertFromUnicodeL(tempShortName, longNameWithoutExtension);
    const TInt originalNameLength = tempShortName.Length();

    // Converts the original file name extension part into 8-bit character string
    TShortName tempShortNameExt(0);
    
    LocaleUtils::ConvertFromUnicodeL(tempShortNameExt, longNameExtension);
    const TInt extensionNameLength = tempShortNameExt.Length();
    // // const TInt extensionNameLength = tempShortNameExt.Length();

    // Checks the length of both original file name main part and original file name extension part
    if(aUseTildeSelectively)
        {
        // don't append ~<aNum>
        if(originalNameLength<=KMaxLengthWithoutTilde && extensionNameLength<=KMaxFatFileNameExt)
            aNum=-1;
        }

    // Applies tilde and number if necessary
    TBuf8<5> tildeAndNumber;
    if (aNum>=0)
        {
        tildeAndNumber.Append('~');
        tildeAndNumber.AppendNumUC(aNum,EHex);
        }
    const TInt lengthOfTildeAndNumber=tildeAndNumber.Length();

    // Creates actual shortname from longname of the original file
    TShortName shortName(11);
#if defined(_DEBUG)
    shortName.Fill(0x01); // fill shortName with garbage to ensure that every byte is written to by this function
#endif

    // Fills the main part of the shortname of the original file
    const TInt numberOfBytesFreeBeforeTilde=KMaxFatFileNameWithoutExt-lengthOfTildeAndNumber;

    TPtr8 portionOfShortNameBeforeTilde((TUint8*)shortName.Ptr(), 0, numberOfBytesFreeBeforeTilde);
    TInt lengthOfPortionOfShortNameBeforeTilde = 
                (originalNameLength < numberOfBytesFreeBeforeTilde) ? originalNameLength : numberOfBytesFreeBeforeTilde;

    portionOfShortNameBeforeTilde.Copy((TUint8*)tempShortName.Ptr(), lengthOfPortionOfShortNameBeforeTilde);
    if( lengthOfPortionOfShortNameBeforeTilde != originalNameLength)
        {
        for( int i = 0; i<lengthOfPortionOfShortNameBeforeTilde; i++)
            {
            if(portionOfShortNameBeforeTilde[i] >= 0x80) //leading byte found
                {
                if( i == lengthOfPortionOfShortNameBeforeTilde - 1) //leading byte found on the edge
                    {
                    lengthOfPortionOfShortNameBeforeTilde -= 1;
                    break;
                    }
                else
                    {
                    i++;
                    }
                }
            }
        }
    Mem::Copy(((TUint8*)shortName.Ptr())+lengthOfPortionOfShortNameBeforeTilde, tildeAndNumber.Ptr(), lengthOfTildeAndNumber);
    TInt i;
    for (i=lengthOfPortionOfShortNameBeforeTilde+lengthOfTildeAndNumber; i<KMaxFatFileNameWithoutExt; ++i)
        {
        shortName[i]=' ';
        }

    // Fills the extension part of the shortname of the original file
    TInt lengthOfExt = 
                (extensionNameLength < KMaxFatFileNameExt) ? extensionNameLength : KMaxFatFileNameExt;
    
    if( lengthOfExt != extensionNameLength)
        {
        for( int i = 0; i<lengthOfExt; i++)
            {
            if(tempShortNameExt[i] >= 0x80)
                {
                if( i == lengthOfExt - 1)
                    {
                    lengthOfExt -= 1;
                    break;
                    }
                else
                    {
                    i++;
                    }
                }
            }
        }           
    Mem::Copy(((TUint8*)shortName.Ptr()) + KMaxFatFileNameWithoutExt, tempShortNameExt.Ptr(), lengthOfExt);
    for (i = KMaxFatFileNameWithoutExt + lengthOfExt; i<KMaxFatFileNameWithoutExt+KMaxFatFileNameExt; ++i)
        {
        shortName[i]=' ';
        }
    
    ReplaceFirstCharacterIfClashesWithE5L(shortName);       
    return shortName;
    }


/**
Check whether a Dos name is legal or not.

@param aName                The entry name to be analysed (may be represented as TDes16& or TDes8&)
@param anAllowWildCards     Flag to indicate whether to allow wildcards in name or not
@param aUseExtendedChars    Flag to indicate if extended characters are allowed
@param aInScanDrive         Flag to indicate whether called when scanning drive
@param aAllowLowerCase      ETrue to allow lower case in the analysed DOS name

@return ETrue if the name is a legal DOS one.
*/

static TBool DoCheckLegalDosName(const TDesC& aName, TBool anAllowWildCards, TBool aUseExtendedChars, TBool aInScanDrive, TBool aAllowLowerCase, TBool aIsForFileCreation)
    {
    const TInt count=aName.Length();
    if (count==0)
        return EFalse;

    TInt valid=0;
    TInt i=0;
    
    //-- check the entry name
    while (i<count)
        {
        TChar c=aName[i++];
        if (c==KExtDelimiter)
            {
            // DOS entry names must contain at least one valid character before the extension
            if (i == 1)
                return EFalse;
            break;
            }
        
          if(!aAllowLowerCase && c.IsLower())
            return EFalse; //-- low case is not allowed

        if (!IsLegalChar(c,anAllowWildCards,aUseExtendedChars,aInScanDrive))
            {
            return EFalse;
            }
        
        if (aIsForFileCreation)
            {
            if ((aUseExtendedChars && (TUint) c > KExtendedCharEnd) || 
                    (!aUseExtendedChars && (TUint) c > KExtendedCharStart))
                {
                return EFalse;
                }
            }

        if (c!=KMatchAny)
            if (++valid>KMaxFatFileNameWithoutExt)
                return EFalse;
        }
    
    //-- check entry extension
    valid=0;
    while (i<count)
        {
        TChar c=aName[i++];
        if (c==KExtDelimiter)
            return EFalse;
        
        if(!aAllowLowerCase && c.IsLower())
            return EFalse; //-- low case is not allowed

        if (!IsLegalChar(c,anAllowWildCards,aUseExtendedChars,aInScanDrive))
            return EFalse;
        
        if (aIsForFileCreation)
            {
            if ((aUseExtendedChars && (TUint) c > KExtendedCharEnd) || 
                    (!aUseExtendedChars && (TUint) c > KExtendedCharStart))
                {
                return EFalse;
                }
            }

        if (c!=KMatchAny)
            if (++valid>KMaxFatFileNameExt)
                return EFalse;
        }
    
    // Unicode file name checking for file opening.
    if (!aIsForFileCreation && GetFatUtilityFunctions())
        {
        TBuf8<KMaxFileName*2> convertedName8;
        TRAPD(err, LocaleUtils::ConvertFromUnicodeL(convertedName8, aName, TFatUtilityFunctions::EOverflowActionLeave));
        if (err != KErrNone)
            return EFalse;
        
        const TInt len8 = convertedName8.Length();
        TInt j = 0; 
        TInt nonWildChar = 0;
        while (j < len8)
            {
            const TUint8 c8 = convertedName8[j++];
            if (c8 == KExtDelimiter)
                break;
            if (c8 == '*' && !anAllowWildCards)
                return EFalse;
            if (c8 == '*' && anAllowWildCards)
                continue;
            
            if (++nonWildChar > KMaxFatFileNameWithoutExt)
                return EFalse;
            }
        
        // check extension part
        nonWildChar = 0;
        while (j < len8)
            {
            const TUint8 c8 = convertedName8[j++];
            if (c8 == KExtDelimiter)
                return EFalse;
            if (c8 == '*' && !anAllowWildCards)
                return EFalse;
            if (c8 == '*' && anAllowWildCards)
                continue;
            
            if (++nonWildChar > KMaxFatFileNameExt)
                return EFalse;
            }
        }

    return ETrue;
    }

/**
    Check whether a Dos name is legal or not. Unicode version
    parameters and return value absolutely the same as in DoCheckLegalDosName()
*/
TBool IsLegalDosName(const TDesC16& aName, TBool anAllowWildCards, TBool aUseExtendedChars, TBool aInScanDrive, TBool aAllowLowerCase, TBool aIsForFileCreation)
    {

    __PRINT(_L("IsLegalDosName 16"));

    return DoCheckLegalDosName(aName, anAllowWildCards, aUseExtendedChars, aInScanDrive, aAllowLowerCase, aIsForFileCreation);  
    }

TBool CFatMountCB::FindShortNameL(const TShortName& aName,TEntryPos& anEntryPos)
//
// Returns ETrue and the entryPos of aName if found or EFalse
//
    {
    
    __PRINT(_L("VFAT::CFatMountCB::FindShortNameL"));   
    TFatDirEntry fatEntry;
    TInt count=0;
    FOREVER
        {
        count++;
        ReadDirEntryL(anEntryPos,fatEntry);
        MoveToDosEntryL(anEntryPos,fatEntry);
        if (fatEntry.IsEndOfDirectory())
            break;
        if (IsRootDir(anEntryPos)&&(anEntryPos.iPos+StartOfRootDirInBytes()==(RootDirEnd()-KSizeOfFatDirEntry)))    
            if (fatEntry.IsErased())
                break;//Allows maximum number of entries in root directory
        if (fatEntry.Name()==aName)
            return ETrue;
        MoveToNextEntryL(anEntryPos);
        if (IsRootDir(anEntryPos)&&(StartOfRootDirInBytes()+anEntryPos.iPos==RootDirEnd()))
            break;//Allows maximum number of entries in root directory
        }
    return EFalse;
    }
    
TBool CFatMountCB::IsUniqueNameL(const TShortName& aName,TInt aDirCluster)
//
// Returns ETrue if aName is unique, EFalse if a matching name is found.
//
    {

    __PRINT(_L("VFAT::CFatMountCB::IsUniqueNameL"));    
    TEntryPos entryPos(aDirCluster,0);
    if (FindShortNameL(aName,entryPos))
        return(EFalse);
    return(ETrue);
    }

void CFatMountCB::ReplaceClashingNameL(const TShortName& aNewName,const TEntryPos& anEntryPos)
//
// A legal dos name has been typed that clashes with a computer generated shortname
// Change the shortname to something else.
//
    {

    __PRINT(_L("VFAT::CFatMountCB::ReplaceClashingNameL")); 
    TFatDirEntry entry;
    ReadDirEntryL(anEntryPos,entry);
    __ASSERT_ALWAYS(entry.IsEndOfDirectory()==EFalse,User::Leave(KErrCorrupt));
    entry.SetName(aNewName);
    WriteDirEntryL(anEntryPos,entry);
//  We now need to fix up VFAT entries with a new checksum reflecting new shortname
//  Calculate new checksum
    TUint8 checksum=CalculateShortNameCheckSum(aNewName);
//  Now go back and adjust all VFAT entries corresponding to this shortname
    TEntryPos entryPos=anEntryPos;
    FOREVER
        {
        entryPos.iPos-=KSizeOfFatDirEntry;  
        ReadDirEntryL(entryPos,entry);
        entry.iData[0x0D]=checksum;
        if (entry.iData[0]&0x40)
            break;
        }
    }

TBool CFatMountCB::GenerateShortNameL(TInt aDirCluster,const TDesC& aName,TShortName& aGeneratedName, TBool aForceRandomize)
//
// Generate a legal dos filename as an alias for aName.
// Returns ETrue if aName is a legal dos name.
//
    {

    __PRINT(_L("VFAT::CFatMountCB::GenerateShortNameL"));
    // Given the long file-name "ABCDEFGHI.TXT", EPOC used to generate short 
    // file-names in the following pecking order:
    //     "ABCDEFGH.TXT",
    //     "ABCDEF~0.TXT",
    //     "ABCDEF~1.TXT",
    //     "ABCDEF~2.TXT",
    //     etc.
    // Now, however, EPOC behaves in a more Windows-like manner and 
    // generates short file-names in this pecking order:
    //     "ABCDEF~1.TXT",
    //     "ABCDEF~2.TXT",
    //     "ABCDEF~3.TXT",
    //     "ABCDEF~4.TXT",
    // After failing to find an unused short name 4 times in a row, 
    // a random number is used to speed up the process. So subsequent
    // short-file names become
    //     "ABC~nnnn.TXT"   where nnnn is a random number
    //    
    TBool useTildeSelectively = ETrue;
    TInt endNum = KMaxDuplicateShortName;   //  0xFFFF
    const TInt KMaxNonRandomShortFileNames = 4;

    TInt i = 1;

    TBool randomize = aForceRandomize;
    if (randomize)
        {
        i = (TInt) (Math::Random() & KMaxDuplicateShortName);
        endNum = (i - 1) & KMaxDuplicateShortName;
        }

    while(i != endNum)
        {
        aGeneratedName=DoGenerateShortNameL(aName,i,useTildeSelectively);

        if (IsUniqueNameL(aGeneratedName,aDirCluster))
            break;

        if (i == KMaxNonRandomShortFileNames && !randomize)
            {
            randomize = ETrue;
            i = (TInt) (Math::Random() & KMaxDuplicateShortName);
            endNum = (i - 1) & KMaxDuplicateShortName;
            }
        else if (i == -1)
            {
            useTildeSelectively=EFalse;
            i = 1;
            }
        else
            i = (i + 1) & KMaxDuplicateShortName;
        }

    if (i == endNum)
        User::Leave(KErrAlreadyExists);

    if((i == -1) && IsLegalDosName(aName,EFalse,EFalse,EFalse,EFalse,ETrue))
        {
        // Original file name is a legal 8.3 name
        return(ETrue);
        }
    else
        {
        return(EFalse);
        }


    }

void TFatDirEntry::InitializeAsVFat(TUint8 aCheckSum)
//
// Initialize a FAT entry as a VFAT filename
//
    {

    Mem::Fill(this,sizeof(SFatDirEntry),0xFF);
    iData[0x0B]=0x0F;
    iData[0x0C]=0x00; iData[0x0D]=aCheckSum;
    iData[0x1A]=0x00; iData[0x1B]=0x00;
    }

void TFatDirEntry::SetVFatEntry(const TDesC& aName,TInt aLen)
//
// Write up to KMaxVFatEntryName unicode chars from aName to the entry
//
    {

    TInt rem=aName.Length()-aLen;
    TPtrC section(aName.Ptr()+aLen,Min(rem,KMaxVFatEntryName));
    TBuf16<KMaxVFatEntryName> buf16;
    buf16.Copy(section);
    if (rem<KMaxVFatEntryName)
        {
        rem++;
        buf16.ZeroTerminate();
        buf16.SetLength(rem); // Zero termination doesn't increase the buf length
        }
    TUint8 orderNo=(TUint8)(aLen/KMaxVFatEntryName+1);
    TInt s=Min(rem,5);
    Mem::Copy(&iData[0x01],buf16.Ptr(),s*2);//Copy up to 10 bytes of buf16 into iData
    TInt offset=s;
    rem-=s;
    s=Min(rem,6);
    Mem::Copy(&iData[0x0E],buf16.Ptr()+offset,s*2);
    offset+=s;
    rem-=s;
    s=Min(rem,2);
    Mem::Copy(&iData[0x1C],buf16.Ptr()+offset,s*2);
    rem-=s;
    if (rem==0)
        orderNo|=0x40;
    iData[0]=orderNo;
    }

void TFatDirEntry::ReadVFatEntry(TDes16& aBuf) const
//
// Read KMaxVFatEntryName unicode chars from the entry
//
    {

    aBuf.SetLength(KMaxVFatEntryName);
    Mem::Copy(&aBuf[0],&iData[0x01],5*2);
    Mem::Copy(&aBuf[5],&iData[0x0E],6*2);
    Mem::Copy(&aBuf[11],&iData[0x1C],2*2);
    }

void CFatMountCB::WriteDirEntryL(TEntryPos& aPos,const TFatDirEntry& aFatDirEntry,const TDesC& aLongName)
//
// Write a VFAT directory entry to disk at position aPos - leave aPos refering to the dos entry
// Assumes sufficient space has been created for it by AddDirEntry.
//
    {

    __PRINT(_L("VFAT::CFatMountCB::WriteDirEntryL"));   
    __ASSERT_DEBUG(aLongName.Length(),Fault(EVFatNoLongName));
    TEntryPos startPos(aPos.iCluster,aPos.iPos);
    TUint8  localBuf[KDefaultSectorSize];
    TUint8 cksum=CalculateShortNameCheckSum(aFatDirEntry.Name());
    TInt numEntries=NumberOfVFatEntries(aLongName.Length())-1; // Excluding dos entry
    // see if all entries written to one sector
    // single sector writes not supported if sector size>default size 
    TInt dosOffset=numEntries<<KSizeOfFatDirEntryLog2;
    TInt absolutePos=(aPos.iCluster<<ClusterSizeLog2())+ClusterRelativePos(aPos.iPos);
    TBool isSameSector=(((absolutePos^(absolutePos+dosOffset))>>SectorSizeLog2())==0 && ((TUint)(1<<SectorSizeLog2())<=KDefaultSectorSize));
    TFatDirEntry vFatEntry;
    vFatEntry.InitializeAsVFat(cksum);
    TInt offset=0;
    while (numEntries--)
        {
        vFatEntry.SetVFatEntry(aLongName,KMaxVFatEntryName*numEntries);//   KMaxVFatEntryName=13
        if(isSameSector)
            {
            Mem::Copy(&localBuf[offset],&vFatEntry,KSizeOfFatDirEntry);
            offset+=KSizeOfFatDirEntry;
            MoveToNextEntryL(aPos);
            }
        else
            {
            WriteDirEntryL(aPos,vFatEntry);
            MoveToNextEntryL(aPos);
            }
        }
    if(isSameSector)
        {
        Mem::Copy(&localBuf[offset],&aFatDirEntry,KSizeOfFatDirEntry);
        
        //-- use special interface to access FAT directory file
        DirWriteL(startPos,TPtrC8(&localBuf[0],dosOffset+KSizeOfFatDirEntry));
        }
    else
        WriteDirEntryL(aPos,aFatDirEntry);
    }

void CFatMountCB::EraseDirEntryL(TEntryPos aPos,const TFatDirEntry& aFirstEntry)
//
// Mark all entries in a VFat directory entry as erased
//
    {
    __PRINT(_L("VFAT::CFatMountCB::EraseDirEntryL"));
    TInt numEntries=0;
    if (aFirstEntry.IsVFatEntry())
        numEntries=aFirstEntry.NumFollowing();
    if(IsRuggedFSys()&&numEntries)
        {
        TInt count=numEntries;
        TEntryPos pos=aPos;
        while(count--)
            MoveToNextEntryL(pos);
        EraseDirEntryL(pos);
        numEntries--;
        }
    FOREVER
        {
        EraseDirEntryL(aPos);
        if (!numEntries--)
            break;
        MoveToNextEntryL(aPos);
        }
    }


void  LocaleUtils::ConvertFromUnicodeL(TDes8& aForeign, const TDesC16& aUnicode, TFatUtilityFunctions::TOverflowAction aOverflowAction)
//
// Convert the volume label using the algorithm specified in the current locale-DLL.
//
    {
    if(aOverflowAction == TFatUtilityFunctions::EOverflowActionLeave)
        {
        GetCodePage().ConvertFromUnicodeL(aForeign, aUnicode, TCodePageUtils::EOverflowActionLeave);
        }
    else
        {
        GetCodePage().ConvertFromUnicodeL(aForeign, aUnicode, TCodePageUtils::EOverflowActionTruncate);
        }
    }

void  LocaleUtils::ConvertToUnicodeL(TDes16& aUnicode, const TDesC8& aForeign, TFatUtilityFunctions::TOverflowAction aOverflowAction)
//
// Convert the volume label using the algorithm specified in the current locale-DLL.
//
    {
    if(aOverflowAction == TFatUtilityFunctions::EOverflowActionLeave)
        {
        GetCodePage().ConvertToUnicodeL(aUnicode, aForeign, TCodePageUtils::EOverflowActionLeave);
        }
    else
        {
        GetCodePage().ConvertToUnicodeL(aUnicode, aForeign, TCodePageUtils::EOverflowActionTruncate);
        }
    }

TBool LocaleUtils::IsLegalShortNameCharacter(TUint aCharacter,TBool aUseExtendedChars)
//
// Convert the volume label using the algorithm specified in the current locale-DLL.
//
    {
    return GetCodePage().IsLegalShortNameCharacter(aCharacter, aUseExtendedChars);
    }