os/kernelhwsrv/userlibandfileserver/fileserver/sfat32/sl_mnt.cpp
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
     1 // Copyright (c) 1996-2009 Nokia Corporation and/or its subsidiary(-ies).
     2 // All rights reserved.
     3 // This component and the accompanying materials are made available
     4 // under the terms of the License "Eclipse Public License v1.0"
     5 // which accompanies this distribution, and is available
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
     7 //
     8 // Initial Contributors:
     9 // Nokia Corporation - initial contribution.
    10 //
    11 // Contributors:
    12 //
    13 // Description:
    14 // Common CFatMountCB code for both EFAT.FSY and EFAT32.fsy
    15 //
    16 //
    17 
    18 /**
    19  @file
    20  @internalTechnology
    21 */
    22 
    23 #include "sl_std.h"
    24 #include "sl_cache.h"
    25 #include "sl_leafdir_cache.h"
    26 #include "sl_dir_cache.h"
    27 #include "sl_scandrv.h"
    28 #include <hal.h>
    29 
    30 TShortName DoGenerateShortNameL(const TDesC& aLongName,TInt& aNum,TBool aUseTildeSelectively);
    31 
    32 
    33 //-----------------------------------------------------------------------------------------
    34 
    35 TFatVolParam::TFatVolParam()
    36     {
    37     Mem::FillZ(this, sizeof(TFatVolParam));
    38     }
    39 
    40 /**
    41     populate the object with the values from the boot sector.
    42     @param aBootSector a reference to the valid boots sector
    43 */
    44 void TFatVolParam::Populate(const TFatBootSector& aBootSector)
    45     {
    46     ASSERT(aBootSector.IsValid());
    47 
    48     iSectorsPerCluster = aBootSector.SectorsPerCluster();
    49     iSectorSizeLog2    = Log2(aBootSector.BytesPerSector());
    50     iClusterSizeLog2   = iSectorSizeLog2+Log2(iSectorsPerCluster);
    51     iFirstFatSector    = aBootSector.FirstFatSector();
    52     iNumberOfFats      = aBootSector.NumberOfFats();
    53     iFatSizeInBytes    = aBootSector.TotalFatSectors()*aBootSector.BytesPerSector();
    54     iTotalSectors      = aBootSector.VolumeTotalSectorNumber();
    55     iRootClusterNum    = aBootSector.RootClusterNum(); //-- will be 0 for FAT12/16
    56 
    57     iRootDirectorySector = aBootSector.RootDirStartSector();
    58     iRootDirEnd = (iRootDirectorySector + aBootSector.RootDirSectors()) << SectorSizeLog2(); //-- doesn't matter for FAT32
    59 
    60     //-- get main and backup FSInfo sectors position, these fields will be 0 for FAT12/16
    61     iFSInfoSectorNum   = aBootSector.FSInfoSectorNum();
    62     iBkFSInfoSectorNum = (TUint16)(aBootSector.BkBootRecSector()+iFSInfoSectorNum); //-- Bk FSInfo sector must follow the Bk boot sector
    63     }
    64 
    65 TBool TFatVolParam::operator==(const TFatVolParam& aRhs) const
    66     {
    67     ASSERT(&aRhs != this);
    68     if(&aRhs == this)
    69         return ETrue; //-- comparing with itself
    70 
    71     return (Mem::Compare((TUint8*)this, sizeof(TFatVolParam), (TUint8*)&aRhs, sizeof(TFatVolParam)) == 0);
    72     }
    73 
    74 
    75 //-----------------------------------------------------------------------------------------
    76 
    77 
    78 CFatMountCB::CFatMountCB()
    79     {
    80     __PRINT2(_L("CFatMountCB::CFatMountCB() 0x%x, %S"), this, &KThisFsyName);
    81 
    82     SetFatType(EInvalid);
    83     iState   = ENotMounted;
    84     
    85     DBG_STATEMENT(iCBRecFlag = 0); //-- debug flag only
    86     }
    87 
    88 CFatMountCB::~CFatMountCB()
    89     {
    90     __PRINT1(_L("#-CFatMountCB::~CFatMountCB() 0x%x"), this);
    91 
    92     DoDismount();
    93 
    94     delete iNotifier;
    95     delete iFatTable;
    96     delete iRawDisk;
    97     delete iLeafDirCache;
    98 
    99     }
   100 
   101 //-----------------------------------------------------------------------------------------
   102 
   103 CFatMountCB* CFatMountCB::NewL()
   104     {
   105     CFatMountCB* pSelf = new(ELeave) CFatMountCB;
   106 
   107     CleanupStack::PushL(pSelf);
   108     pSelf->ConstructL();
   109     CleanupStack::Pop(pSelf);
   110 
   111     return pSelf;
   112     }
   113 
   114 // second-stage constructor
   115 void CFatMountCB::ConstructL()
   116 {
   117     //-- create Notifier
   118     iNotifier = CAsyncNotifier::New();
   119     if( !iNotifier )
   120         {
   121         Close();
   122         User::Leave(KErrNoMemory);
   123         }
   124 
   125     iNotifier->SetMount(this);
   126     }
   127 
   128 //-------------------------------------------------------------------------------------------------------------------
   129 
   130 /**
   131 Implementation of CMountCB::FileSystemClusterSize(). Returns cluster size of this mount.
   132 @return Cluster size value if successful; otherwise KErrNotReady if the mount is not ready.
   133 @see CMountCB::FileSystemClusterSize()
   134 */
   135 TInt CFatMountCB::ClusterSize() const
   136     {
   137     if (ClusterSizeLog2())
   138         return (1 << ClusterSizeLog2());
   139 
   140     return KErrNotReady;
   141     }
   142 
   143 //-------------------------------------------------------------------------------------------------------------------
   144 
   145 /**
   146     @leave KErrAccessDenied if the mount is read-only
   147 */
   148 void CFatMountCB::CheckWritableL() const
   149     {
   150     if(ReadOnly())
   151         {
   152         __PRINT(_L("CFatMountCB is RO!"));
   153         User::Leave(KErrAccessDenied);
   154         }
   155     }
   156 
   157 /**
   158     @leave KErrCorrupt if the mount is in inconsistent state i.e high-level file and directory  operations can not be performed
   159 */
   160 void CFatMountCB::CheckStateConsistentL() const
   161     {
   162     if(!ConsistentState())
   163         {
   164         __PRINT(_L("CFatMountCB state is inconsistent !"));
   165         User::Leave(KErrCorrupt);
   166         }
   167     }
   168 
   169 
   170 //-------------------------------------------------------------------------------------------------------------------
   171 /**
   172     Helper Method. Check if the parameters of the volume being remounted are the same as current ones.
   173     @return ETrue if volume parameters remained same.
   174 */
   175 TBool CFatMountCB::CheckVolumeTheSame()
   176 {
   177     //-- initialise local drive
   178     TInt nRes =InitLocalDrive();
   179     if(nRes != KErrNone)
   180         return EFalse;
   181 
   182     //-- read the boot sector or its backup copy if the main is damaged. It will aslo validate it.
   183     TFatBootSector bootSector;
   184     nRes = ReadBootSector(bootSector, iRamDrive);
   185     if(nRes != KErrNone)
   186         return EFalse;
   187 
   188     //-- 1. check volume Uid
   189     if(iUniqueID != bootSector.UniqueID())
   190         return EFalse;
   191 
   192     //-- check volume parameters, they must remain the same
   193     TFatVolParam volParam;
   194     volParam.Populate(bootSector);
   195 
   196     if(!(volParam == iVolParam))
   197         return EFalse;
   198 
   199 
   200     return ETrue;
   201 
   202 }
   203 //-------------------------------------------------------------------------------------------------------------------
   204 
   205 /**
   206     Helper Method. Check if the parameters of the volume being remounted are the same as current ones.
   207     If they are, re-initialises the mount.
   208 */
   209 void CFatMountCB::DoReMountL()
   210     {
   211 
   212     if(!CheckVolumeTheSame())
   213         User::Leave(KErrGeneral);
   214 
   215 	//-- get drive capabilities
   216     TLocalDriveCapsV2Buf capsBuf;
   217 	User::LeaveIfError(LocalDrive()->Caps(capsBuf));
   218 
   219     //-- the volume is the same as it was on original MountL()
   220     //-- we need to re-initialize for the case when the media was removed, FAT or directory structure changed on other device and the media returned back.
   221     DoDismount();
   222 
   223     SetState(EMounting);
   224 
   225     InitializeL(capsBuf(), ETrue); //-- forcedly disable FSInfo usage. This will lead to FAT free clusters re-counting.
   226 
   227     }
   228 
   229 //-------------------------------------------------------------------------------------------------------------------
   230 
   231 /**
   232     Try remount this Fat volume. Checks if the volume parameters remained the same as on original MountL() call, and
   233     if they are, re-initialises the mount. This includes resetting all caches.
   234     ! Do not call this method from TDriveInterface methods, like citical and non-critical notifiers ! This can lead to the
   235     recursive loops and undefined behaviour.
   236 
   237     @return KErrNone if the remount was OK
   238             system-wide error code otherwise
   239 */
   240 TInt CFatMountCB::ReMount()
   241     {
   242     __PRINT2(_L("CFatMountCB::ReMount(), drv:%d, curr state:%d"), DriveNumber(), State());
   243 
   244     const TFatMntState currState = State();
   245 
   246     //-- analyse the mount state and find out if we can remount at all.
   247     switch(currState)
   248         {
   249         case ENotMounted:
   250         __PRINT(_L("CFatMountCB::ReMount() Invalid mount state!"));
   251 
   252         ASSERT(0);
   253         return KErrGeneral;
   254 
   255         //-- correct state, proceed to remount
   256         default:
   257         break;
   258     }
   259 
   260     //-- there are 2 options here:
   261     //-- 1. normally initialised mount had been forcedly dismounted (it can optionally have objects opened on it)
   262     //--    in this case the DoReMountL() will succeed and everything will be fine, the objects will be accessible afterwards
   263     //-- 2. the mount hasn't been initialised at all (it does not have for example, FAT table created etc.)
   264     //--    in this case we may need to fake the success. This can only happen on forced mount by CFormatCB
   265     TInt nRes;
   266     TRAP(nRes, DoReMountL());
   267 
   268     if(nRes != KErrNone)
   269         {
   270         //-- note that the mount may be here left in inconsistent state (EMounting)
   271         //-- if DoReMountL() fails. This is OK, because we can not make any valid read/write operations in such a state and
   272         //-- the drive must be dismounted and mounted again. File Server's TDrive shall do this.
   273         __PRINT1(_L("CFatMountCB::ReMount() failed! code:%d"), nRes);
   274 
   275         //-- If we are in the EInit_Forced state, it means that we are trying to remount the volume that has been formatted.
   276         //-- scenario: On formatting, if we can't read a bootsector, new _empty_ object of the CFatMountCB is created and
   277         //-- it is used for performing a format. If the format has finished, but RFormat isn't closed yet and we try to access the volume,
   278         //-- we will get here, because all members of the constructed mount will be zeroes.
   279         if(currState == EInit_Forced)
   280             {
   281             __PRINT(_L("CFatMountCB::ReMount() simulating normal remount!"));
   282             SetState(currState);
   283             return KErrNone;
   284             }
   285 
   286         return nRes;
   287         }
   288 
   289     __PRINT1(_L("CFatMountCB::ReMount() Completed drv:%d"), DriveNumber());
   290     SetState(EInit_R);
   291     return nRes;
   292     }
   293 
   294 //-------------------------------------------------------------------------------------------------------------------
   295 
   296 /**
   297     Reset the last leaf dir or invalidate leaf dir cache if leaf dir cache is
   298     instantiated.
   299 */
   300 
   301 void CFatMountCB::InvalidateLeafDirCache()
   302 	{
   303     if (iLeafDirCache)
   304     	{
   305         iLeafDirCache->Reset();
   306     	}
   307     else
   308     	{
   309         User::Free(iLastLeafDir);
   310         iLastLeafDir=NULL;
   311     	}
   312 	}
   313 
   314 //-------------------------------------------------------------------------------------------------------------------
   315 
   316 /**
   317     Delete mount's caches
   318     Moves CFatMountCB into ENotMounted state immediately.
   319 */
   320 void CFatMountCB::DoDismount()
   321     {
   322     __PRINT1(_L("CFatMountCB::DoDismount() drv:%d"), DriveNumber());
   323 
   324     //-- try to flush and destroy FAT cache
   325     if (iFatTable)
   326         {
   327         if(!ConsistentState() || ReadOnly())
   328             {//-- the mount state is inconsistent, so the data can't be flushed. Ignore dirty cache either.
   329             iFatTable->Dismount(ETrue);
   330             }
   331         else
   332             {//-- Try to flush the FAT - if this fails there's not much we can do
   333             TRAPD(r, iFatTable->FlushL());
   334             iFatTable->Dismount(r != KErrNone); //-- ignore dirty data if we failed to flush the cache
   335             }
   336         }
   337 
   338     //-- destroy leafdir name cache, this cache will be re-created while mounting or re-mounting
   339     //-- see CFatMountCB::InitializeL()
   340     delete iLeafDirCache;
   341     iLeafDirCache = NULL;
   342 
   343     //-- destroy directory cache, this cache will be re-created while mounting or re-mounting
   344     //-- see CFatMountCB::InitializeL()
   345     delete iRawDisk;
   346     iRawDisk = NULL;
   347 
   348     //-- Set mount state to "Dismounted". Which means that there might be no caches, but the mount is alive,
   349     //-- i.e. iFatTable & iRawDisk are valid
   350     SetState(EDismounted);
   351     }
   352 
   353 //-----------------------------------------------------------------------------------------
   354 
   355 /** old implementation */
   356 void CFatMountCB::FinaliseMountL()
   357     {
   358     FinaliseMountL(RFs::EFinal_RW);
   359     }
   360 
   361 //-----------------------------------------------------------------------------------------
   362 /**
   363     Dismount the CFatMountCB and the drive.
   364     called from TDrive::Dismount().
   365 */
   366 void CFatMountCB::Dismounted()
   367     {
   368     __PRINT1(_L("CFatMountCB::Dismounted() drv:%d"), DriveNumber());
   369 
   370     //-- n.b. it is no safe to do a kind of filnalisatin work here that implies accessing the media.
   371     //-- this method may be called after the media change occured from the TDrive::Dismount(). It means
   372     //-- that if we try to write some data here, they could be written into a different medium, if it had been
   373     //-- physically changed.
   374 
   375     const TFatMntState prevState = State();
   376 
   377     DoDismount(); //-- it will change mount state to EDismounted
   378     DismountedLocalDrive();
   379 
   380     //-- check if the previous state was EInit_Forced, which means that this method was called
   381     //-- on the mount that might not be alive (no valid iFatTable & iRawDisk).
   382     //-- This can happen only during format operation on non-mounted previously volume.
   383     //-- this EInit_Forced state must be processed separately, see ::Remount()
   384     if(prevState == EInit_Forced)
   385         SetState(EInit_Forced);
   386 
   387     }
   388 
   389 
   390 //-------------------------------------------------------------------------------------------------------------------
   391 
   392 /**
   393     Find out if the mount is finalised.
   394     @param  aFinalised on exit will be ETrue if the maunt is finalised, EFalse otherwise.
   395     @return standard error codes.
   396 */
   397 TInt CFatMountCB::IsFinalised(TBool& aFinalised)
   398     {
   399     switch(State())
   400         {
   401         case EFinalised: //-- already explicitly finalised
   402             aFinalised = ETrue;
   403         return KErrNone;
   404 
   405         case EInit_W: //-- the volume had been written
   406             aFinalised = EFalse;
   407         return KErrNone;
   408 
   409         default: //-- it depends on the state
   410         break;
   411         }
   412 
   413     //-- find out if the volume is _physically_ finalised.
   414     //-- It can be in the state EInit_R, but finalised before mounting
   415     if(!VolCleanFlagSupported())
   416         return KErrNotSupported;
   417 
   418     TInt nRes = KErrNone;
   419     TRAP(nRes, aFinalised = VolumeCleanL());
   420 
   421     return nRes;
   422     }
   423 
   424 //-------------------------------------------------------------------------------------------------------------------
   425 
   426 /**
   427     @return ETrue if the mount is in consistent state i.e. normally mounted.
   428     See TFatMntState enum for more detail.
   429 */
   430 TBool CFatMountCB::ConsistentState() const
   431     {
   432     return (iState==EInit_R) || (iState==EInit_W) || (iState == EFinalised);
   433     }
   434 
   435 //-------------------------------------------------------------------------------------------------------------------
   436 
   437 /**
   438     Open CFatMountCB for write. I.e. perform some actions on the first write attempt.
   439     This is a callback from TDriveInterface.
   440     @return System wide error code.
   441 */
   442 TInt CFatMountCB::OpenMountForWrite()
   443     {
   444     if(State() == EInit_W)
   445         return KErrNone; //-- nothing to do, the mount is already opened for write
   446 
   447     __PRINT1(_L("#- CFatMountCB::OpenMountForWrite() drv:%d\n"),DriveNumber());
   448 
   449     ASSERT(State() == EInit_R || State() == EFinalised);
   450 
   451     //-- Check possible recursion. This method must not be called recursively. SetVolumeCleanL() works through direct disc access and
   452     //-- can not call TDriveInterface methods that call this method etc.
   453     ASSERT(iCBRecFlag == 0);
   454     DBG_STATEMENT(iCBRecFlag = 1); //-- set recursion check flag
   455 
   456     //-- do here some "opening" work, like marking volme as dirty
   457     //-- be careful here, as soon as this is a callback from TDriveInterface, writing via TDriveInterface may cause some unwanted recursion.
   458 
   459     //-- mark the volume as dirty
   460     TInt nRes=KErrNone;
   461     TRAP(nRes, SetVolumeCleanL(EFalse));
   462     if(nRes == KErrNone)
   463         {
   464         SetState(EInit_W);
   465         }
   466 
   467     DBG_STATEMENT(iCBRecFlag = 0); //-- reset recursion check flag
   468 
   469     return nRes;
   470 
   471     }
   472 
   473 //-------------------------------------------------------------------------------------------------------------------
   474 
   475 /**
   476     Unfinalise the mount, reset "VolumeCleanShutDown" flag and change the state if necessarily.
   477 */
   478 void CFatMountCB::UnFinaliseMountL()
   479     {
   480     switch(State())
   481         {
   482         case EFinalised:
   483         case EInit_R:
   484             SetVolumeCleanL(EFalse); //-- the mount, mark volume "dirty"
   485             SetState(EInit_R);
   486         return;
   487 
   488         case EInit_W:
   489         return; //-- nothing to do
   490 
   491         default:
   492         //-- other mount states are inconsistent; can't perform this operation
   493         User::Leave(KErrAbort);
   494         break;
   495 
   496         }
   497 
   498     }
   499 
   500 //-------------------------------------------------------------------------------------------------------------------
   501 
   502 /**
   503     Finalise the mount.
   504 
   505     @param  aOperation  describes finalisation operation ,see RFs::TFinaliseDrvMode
   506     @param  aParam1     not used, for future expansion
   507     @param  aParam2     not used, for future expansion
   508 
   509     @leave  System wide error code. particular cases:
   510             KErrArgument invalid arguments
   511             KErrInUse    if the volume has opened objects (files, directories etc)
   512             KErrCorrupt  if the volume is corrupt
   513 
   514 */
   515 void CFatMountCB::FinaliseMountL(TInt aOperation, TAny* /*aParam1*/, TAny* /*aParam2*/)
   516     {
   517     __PRINT2(_L("#- CFatMountCB::FinaliseMountL() op:%d, drv:%d"), aOperation, DriveNumber());
   518 
   519     switch(aOperation)
   520         {
   521         case RFs::EFinal_RW:
   522         case RFs::EFinal_RO:
   523         break;
   524 
   525         case RFs::EForceUnfinalise:
   526             UnFinaliseMountL();
   527         return;
   528 
   529         default:
   530             __PRINT1(_L("#- CFatMountCB::FinaliseMountL() unexpected operation!:%d"), aOperation);
   531             ASSERT(0);
   532             User::Leave(KErrArgument);
   533         return;
   534         }
   535 
   536     //-- mount finalisation work
   537 
   538     ASSERT(aOperation == RFs::EFinal_RW || aOperation == RFs::EFinal_RO);
   539 
   540     if(State() == EFinalised)
   541         {//-- the mount is already finalised. All we can do is to set it to RO mode
   542         if(ReadOnly() && aOperation == RFs::EFinal_RW)
   543             {
   544             User::Leave(KErrAccessDenied); //-- can't override RO flag
   545             }
   546 
   547 		(void)LocalDrive()->Finalise(ETrue);
   548 
   549         if(aOperation == RFs::EFinal_RO)
   550             {
   551             SetReadOnly(ETrue);
   552             return;
   553             }
   554 
   555         return;
   556         }
   557 
   558     if(LockStatus() != 0)
   559         {//-- can't finalise the volume if it has opened objects and not in the consistent state.
   560          //-- Theoretically, we can finalise the mount if we have files opened only for read, but at present,
   561          //-- it's impossible to detect such situation.
   562         User::Leave(KErrInUse);
   563         }
   564 
   565     if(State() != EInit_R && State() != EInit_W)
   566         {//-- can't finalise the mount because it can be in an inconsistent state; e.g. corrupt.
   567         __PRINT1(_L("#- CFatMountCB::FinaliseMountL() Invalid mount State: %d"),State());
   568         User::Leave(KErrCorrupt);
   569         }
   570 
   571     //-- flush FAT cache
   572     FAT().FlushL();
   573 
   574     //-- for FAT32 we may need to update data in FSInfo sectors
   575     if(Is32BitFat())
   576         {
   577         if(FAT().ConsistentState())
   578             {//-- FAT table state is consistent and the number of free clusters is known.
   579              //-- Do it disregarding the mount state, it may help in the situation when 2 copies of the FSInfo are different for some reason.
   580             DoUpdateFSInfoSectorsL(EFalse);
   581             }
   582             else
   583             {//-- FAT table state is inconsistent, the most probable case here: background scan for free clusters is still working.
   584              //-- in this case we can't put corect values into the FSInfo.
   585             if(State() == EInit_W)
   586                 {//-- bad situation: free clusters may be being counted and someone has already written something on the volume at the same time.
   587                  //-- we do not know the exact number of free clustes and can't wait until scan finishes. Invalidate FSInfo.
   588                 __PRINT(_L("#- CFatMountCB::FinaliseMountL() invalidating FSInfo"));
   589                 DoUpdateFSInfoSectorsL(ETrue);
   590                 }
   591              else
   592                 {//-- no changes on the volume, just do not update FSInfo
   593                 __PRINT(_L("#- CFatMountCB::FinaliseMountL() FAT state inconsistent; FSInfo isn't updated"));
   594                 }
   595 
   596             }//if(FAT().ConsistentState())
   597 
   598         }//if(Is32BitFat())
   599 
   600 
   601 
   602     //-- mark the volume as clean
   603     SetVolumeCleanL(ETrue);
   604 
   605     //-- finally, put the volume into RO mode if required
   606     if(aOperation == RFs::EFinal_RO)
   607         SetReadOnly(ETrue);
   608 
   609     SetState(EFinalised);
   610     }
   611 
   612 
   613 //-------------------------------------------------------------------------------------------------------------------
   614 
   615 /**
   616 @return ETrue if "VolumeClean" flag is supported i.e. this is not FAT12
   617 */
   618 TBool CFatMountCB::VolCleanFlagSupported() const
   619     {
   620         const TFatType fatType=FatType();
   621 
   622         ASSERT(fatType == EFat12 || fatType == EFat16 || fatType == EFat32);
   623         return (fatType != EFat12);
   624     }
   625 
   626 //-----------------------------------------------------------------------------------------
   627 
   628 
   629 /**
   630     Obtain the volume information.
   631     All information except iSize and iFree has been added by TDrive::Volume().
   632 
   633     @param  aVolume on return will contain iSize & iFree fields filled with actual data.
   634 */
   635 void CFatMountCB::VolumeL(TVolumeInfo& aVolume) const
   636     {
   637 
   638     //-- if true, this operation will be synchronous, i.e the client will be suspended until FAT32 scanning thread finishes, if running.
   639     //-- the information if this operation is synchronous or not can be passed by client in TVolumeInfo::iFileCacheFlags field.
   640     //-- if the client sets aVolume.iVolSizeAsync flag there, RFs::Volume() will be asynchronous, i.e the _current_ number of free clusters
   641     //-- will be returned.
   642     const TBool bSyncOp = !aVolume.iVolSizeAsync;
   643     aVolume.iVolSizeAsync = EFalse; //-- reset this flag in order it not to be reused on the client side
   644 
   645 	__PRINT2(_L("CFatMountCB::VolumeL() drv:%d, synch:%d"), DriveNumber(), bSyncOp);
   646     const TDriveInfo& drvInfo=aVolume.iDrive;
   647 
   648 #if defined(__EPOC32__)
   649     // if RAM drive, cap size according to HAL.
   650     if (drvInfo.iType==EMediaRam)
   651         {
   652         TLocalDriveCapsV2Buf caps;
   653         LocalDrive()->Caps(caps);
   654 
   655         const TInt max_drive_size=TInt(caps().iEraseBlockSize);
   656         const TInt cur_drive_size=I64INT(caps().iSize);
   657 
   658         aVolume.iSize=max_drive_size;
   659         aVolume.iFree=max_drive_size-cur_drive_size;
   660 
   661         aVolume.iSize=aVolume.iFree+iSize;
   662 
   663         TInt maxSize;
   664         if (HAL::Get(HAL::EMaxRAMDriveSize, maxSize) == KErrNone)
   665             {
   666             // iSize will never grow beyond maxRam because of a check in medint.
   667             // d <= f; (s{f} + f) - m <= f; s{f} <= m
   668             __ASSERT_DEBUG(iSize <= maxSize, Fault(EFatRAMDriveSizeInvalid));
   669             if (aVolume.iSize > maxSize)
   670                 {
   671                 TInt64 d = aVolume.iSize - maxSize;
   672                 __ASSERT_DEBUG(d <= aVolume.iFree, Fault(EFatRAMDriveFreeInvalid));
   673                 aVolume.iSize -= d;
   674                 aVolume.iFree -= d;
   675                 }
   676             }
   677 
   678         aVolume.iSize-=ClusterBasePosition(); // Allow for bytes used by FAT etc
   679         aVolume.iSize=(aVolume.iSize>>ClusterSizeLog2())<<ClusterSizeLog2();  //-- round down to cluster size
   680 
   681         return;
   682         }//if (drvInfo.iType==EMediaRam)
   683 
   684 #endif
   685 
   686 
   687     const TUint32 freeClusters = FAT().NumberOfFreeClusters(bSyncOp);
   688 
   689     __PRINT1(_L("CFatMountCB::VolumeL() free clusters:%d"), freeClusters);
   690 
   691     aVolume.iFree = (TInt64)freeClusters << ClusterSizeLog2();
   692 
   693     if (drvInfo.iType==EMediaRam)
   694         aVolume.iSize=aVolume.iFree+iSize;
   695 
   696     aVolume.iSize-=ClusterBasePosition(); // Allow for bytes used by FAT etc
   697     aVolume.iSize=(aVolume.iSize >> ClusterSizeLog2()) << ClusterSizeLog2();  //-- round down to cluster size
   698 
   699     }
   700 
   701 
   702 //-----------------------------------------------------------------------------------------
   703 
   704 //
   705 //  Set the volume label (write aVolume label into BPB & Volume Label File)
   706 //  aName string may be zero length but is assumed to contain no illegal characters or NULLs.
   707 //
   708 void CFatMountCB::SetVolumeL(TDes& aName)
   709     {
   710 
   711     __PRINT(_L("CFatMountCB::SetVolumeL"));
   712 
   713     CheckStateConsistentL();
   714     CheckWritableL();
   715 
   716     __ASSERT_ALWAYS(aName.Length()<=KVolumeLabelSize,User::Leave(KErrBadName));
   717 
   718     TBuf8<KVolumeLabelSize> buf8(KVolumeLabelSize);
   719     buf8.Zero();
   720 	LocaleUtils::ConvertFromUnicodeL(buf8, aName, TFatUtilityFunctions::EOverflowActionLeave);
   721 	aName.Zero();
   722 	LocaleUtils::ConvertToUnicodeL(aName, buf8); // adjust aName (which may contain more underscores after this line than before)
   723 
   724     const TInt lengthOfBuf8=buf8.Length();
   725     // Pad to end with spaces if not empty.
   726     if (lengthOfBuf8>0 && lengthOfBuf8<KVolumeLabelSize)
   727         {
   728         buf8.SetLength(KVolumeLabelSize);
   729         Mem::Fill(&buf8[lengthOfBuf8],KVolumeLabelSize-lengthOfBuf8,' ');
   730         }
   731 
   732     // Write a volume label file
   733     WriteVolumeLabelFileL( buf8 );
   734 
   735     // Write the boot sector volume label
   736     // Always pad to full length with spaces
   737     if (lengthOfBuf8==0)
   738         {
   739         buf8.Fill(' ',KVolumeLabelSize);
   740         }
   741 
   742     WriteVolumeLabelL(buf8);
   743     }
   744 
   745 //-----------------------------------------------------------------------------------------
   746 
   747 /**
   748     Make a directory.
   749     @param aName full path to the directory to create. Name validity is checked by file server.
   750     all trailing dots from the name will be removed
   751 */
   752 void CFatMountCB::MkDirL(const TDesC& aName)
   753     {
   754     __PRINT2(_L("CFatMountCB::MkDirL, drv:%d, %S"), DriveNumber(), &aName);
   755 
   756     CheckStateConsistentL();
   757     CheckWritableL();
   758 
   759     TPtrC dirName = RemoveTrailingDots(aName); //-- remove trailing dots from the name
   760 
   761     TInt namePos=dirName.LocateReverse(KPathDelimiter)+1; // There is always a path delimiter
   762     TPtrC name=dirName.Mid(namePos);
   763     TLeafDirData leafDir;
   764     const TEntryPos dirPos(FindLeafDirL(dirName.Left(namePos), leafDir),0);
   765     TEntryPos dumPos=dirPos;
   766     TFatDirEntry dumEntry;
   767 
   768     TBool isOriginalNameLegal = IsLegalDosName(name,EFalse,EFalse,EFalse,EFalse,ETrue);
   769     iFileCreationHelper.InitialiseL(name);
   770     TFileName fileName;
   771     TEntryPos startPos;
   772     TFatDirEntry startEntry;
   773     
   774     TRAPD(ret,DoFindL(name,KEntryAttMaskSupported,
   775 			    		startPos,startEntry,dumPos,dumEntry,
   776 			    		fileName,KErrNotFound,
   777 			    		&iFileCreationHelper,
   778 			    		leafDir));
   779 
   780     if (ret!=KErrNotFound && ret!=KErrNone)
   781         User::Leave(ret);
   782     if (ret!=KErrNotFound)
   783         {
   784         if (dumEntry.Attributes()&KEntryAttDir)
   785             User::Leave(KErrAlreadyExists);
   786         else
   787             User::Leave(KErrAccessDenied);
   788         }
   789     TShortName shortName;
   790 
   791     if (iFileCreationHelper.GetValidatedShortName(shortName) == KErrNotFound)
   792     	{
   793     	GenerateShortNameL(dirPos.iCluster,name,shortName,ETrue);
   794     	}
   795 
   796     TInt numEntries=1;
   797     if (isOriginalNameLegal==EFalse)
   798         numEntries=NumberOfVFatEntries(name.Length());
   799     dumPos=dirPos;
   800 
   801     if (iFileCreationHelper.IsNewEntryPosFound())
   802     	{
   803     	dumPos = iFileCreationHelper.EntryAddingPos();
   804     	}
   805 
   806     AddDirEntryL(dumPos,numEntries);    //  Directory entry in leaf directory
   807     TInt startCluster;
   808     FOREVER
   809         {
   810         //-- FAT().FreeClusterHint() will give us a hint of the last free cluster
   811         startCluster=FAT().AllocateSingleClusterL(dumPos.iCluster ? dumPos.iCluster : FAT().FreeClusterHint());
   812 
   813         FAT().FlushL();
   814         TRAPD(r, InitializeFirstDirClusterL(startCluster,dirPos.iCluster));
   815         if(r == KErrNone)
   816             break;
   817         if(r != KErrCorrupt)
   818             User::Leave(r);
   819         FAT().MarkAsBadClusterL(startCluster);
   820         }
   821     TFatDirEntry fatDirEntry;
   822     fatDirEntry.SetName(shortName);
   823     fatDirEntry.SetAttributes(KEntryAttDir);
   824     TTime now;
   825 	now.UniversalTime();
   826     fatDirEntry.SetTime(now, TimeOffset());
   827     fatDirEntry.SetCreateTime(now, TimeOffset());
   828     fatDirEntry.SetStartCluster(startCluster);
   829     fatDirEntry.SetSize(0);
   830     if (isOriginalNameLegal)
   831         WriteDirEntryL(dumPos,fatDirEntry);
   832     else
   833         WriteDirEntryL(dumPos,fatDirEntry,name);
   834 
   835     iFileCreationHelper.Close();
   836     }
   837 
   838 //-----------------------------------------------------------------------------------------
   839 
   840 /**
   841     Setup 1st cluster of the new directory
   842 
   843     @param  aStartCluster   this entry start cluster number
   844     @param  aParentCluster  parent entry start cluster number
   845 */
   846 void CFatMountCB::InitializeFirstDirClusterL(TInt aStartCluster,TInt aParentCluster)
   847     {
   848     const TUint32 KClusterSz= 1<<ClusterSizeLog2();
   849     const TUint32 KMaxBufSz = KClusterSz;           //-- max. nuffer size is a cluster
   850     const TUint32 KMinBufSz = 1<<SectorSizeLog2();  //-- min. buffer size is 1 sector (for OOM case)
   851 
   852     //-- allocate a buffer for directory file 1st cluster initialisation
   853     RBuf8 buf;
   854     CleanupClosePushL(buf);
   855 
   856     if(buf.CreateMax(KMaxBufSz) != KErrNone)
   857         buf.CreateMaxL(KMinBufSz); //-- OOM, try to create smaller buffer
   858 
   859     buf.FillZ();
   860 
   861     //-- copy "." directory entry to the buffer
   862 
   863     //-- "." directory entry
   864     TFatDirEntry entry;
   865     TTime now;
   866 	now.UniversalTime();
   867     entry.SetTime(now, TimeOffset() );
   868     entry.SetAttributes(KEntryAttDir);
   869     entry.SetCurrentDirectory();
   870     entry.SetStartCluster(aStartCluster);
   871     Mem::Copy(&buf[0],&entry,KSizeOfFatDirEntry);
   872 
   873     //-- append ".." directory entry
   874     entry.SetParentDirectory();
   875     entry.SetStartCluster(aParentCluster==RootIndicator() ? 0 : aParentCluster);
   876     Mem::Copy(&buf[0]+KSizeOfFatDirEntry,&entry,KSizeOfFatDirEntry);
   877 
   878     TEntryPos entryPos(aStartCluster,0);
   879 
   880     //-- write buffer to the beginning of the directory file.
   881     DirWriteL(entryPos, buf); //-- use special interface to access FAT directory file
   882 
   883     //-- fill in the rest of the cluster if we used a small buffer
   884     if((TUint32)buf.Size() < KClusterSz) //-- use special interface to access FAT directory file
   885     {
   886         buf.FillZ();
   887         const TInt restCnt = SectorsPerCluster() - 1;
   888         ASSERT(restCnt >=1);
   889 
   890         for(TInt i=0; i<restCnt; ++i)
   891         {
   892             entryPos.iPos += KMinBufSz;
   893             DirWriteL(entryPos, buf); //-- use directory cache when dealing with directories
   894         }
   895 
   896     }
   897 
   898     CleanupStack::PopAndDestroy(&buf);
   899 
   900     }
   901 
   902 //-----------------------------------------------------------------------------------------
   903 
   904 /**
   905     Remove a directory.
   906     @param aName directory name
   907     all trailing dots from the name will be removed
   908 */
   909 void CFatMountCB::RmDirL(const TDesC& aName)
   910     {
   911     __PRINT2(_L("CFatMountCB::RmDirL, drv:%d, %S"), DriveNumber(), &aName);
   912 
   913     CheckStateConsistentL();
   914     CheckWritableL();
   915 
   916     TPtrC dirName = RemoveTrailingDots(aName); //-- remove trailing dots from the name
   917 
   918     TFatDirEntry dirEntry;
   919     TEntryPos dirEntryPos(RootIndicator(),0); // Already checked entry is a directory
   920     FindEntryStartL(dirName,KEntryAttMatchMask|KEntryAttMatchExclusive,dirEntry,dirEntryPos);
   921     TEntryPos dosEntryPos=dirEntryPos;
   922     TFatDirEntry dosEntry=dirEntry;
   923     MoveToDosEntryL(dosEntryPos,dosEntry);
   924     if (!IsDirectoryEmptyL(StartCluster(dosEntry)))
   925         User::Leave(KErrInUse);
   926     // Remove the directory from cache before erasing
   927     if(iLeafDirCache && iLeafDirCache->CacheCount() > 0)
   928     	{
   929     	iLeafDirCache->RemoveDirL(StartCluster(dosEntry));
   930     	}
   931 
   932     EraseDirEntryL(dirEntryPos,dirEntry);
   933     FAT().FreeClusterListL(StartCluster(dosEntry));
   934     FAT().FlushL();
   935     }
   936 
   937 //-----------------------------------------------------------------------------------------
   938 
   939 /**
   940     Delete a file
   941     @param aName file name
   942     all trailing dots from the name will be removed
   943 */
   944 void CFatMountCB::DeleteL(const TDesC& aName)
   945     {
   946     __PRINT2(_L("CFatMountCB::DeleteL, drv:%d, %S"), DriveNumber(), &aName);
   947 
   948     CheckStateConsistentL();
   949     CheckWritableL();
   950 
   951     TPtrC fullName = RemoveTrailingDots(aName); //-- remove trailing dots from the name
   952 
   953     TFatDirEntry firstEntry;
   954     TEntryPos firstEntryPos(RootIndicator(),0);
   955     FindEntryStartL(fullName,KEntryAttMaskSupported,firstEntry,firstEntryPos);
   956     TEntryPos dosEntryPos=firstEntryPos;
   957     TFatDirEntry dosEntry=firstEntry;
   958     MoveToDosEntryL(dosEntryPos,dosEntry);
   959     if ((dosEntry.Attributes()&KEntryAttReadOnly) || (dosEntry.Attributes()&KEntryAttDir))
   960         User::Leave(KErrAccessDenied);
   961     // Can not delete a file if it is clamped
   962     CMountCB* basePtr=(CMountCB*)this;
   963     TInt startCluster=StartCluster(dosEntry);
   964     if(basePtr->IsFileClamped(MAKE_TINT64(0,startCluster)) > 0)
   965         User::Leave(KErrInUse);
   966     EraseDirEntryL(firstEntryPos,firstEntry);
   967     FAT().FreeClusterListL(StartCluster(dosEntry));
   968     FAT().FlushL();
   969     }
   970 
   971 //-----------------------------------------------------------------------------------------
   972 
   973 /**
   974 
   975     Rename or replace a directory entry.
   976     Assumes all files are closed and replace is only passed files.
   977     Assumes rename target does not exist or is the source file.
   978 
   979     --------------- operating mode --------------------------------------------
   980 
   981     * rename mode
   982 
   983     aOldName exists  |  aNewName exists |   result
   984         N                    N              leave KErrNotFound
   985         N                    Y              leave KErrNotFound
   986         Y                    N              rename aOldName -> aNewName
   987         Y                    Y              leave KErrAlreadyExists if(aOldName!=aNewName); otherwise do nothing
   988 
   989     * replace mode
   990 
   991         N                    N              leave KErrNotFound
   992         N                    Y              leave KErrNotFound
   993         Y                    N              rename aOldName -> aNewName
   994         Y                    Y              contents and all file attributes of the "aNewName" are replaced with aOldName's. "aOldName" entries are deleted then.
   995 
   996 
   997     @param   aOldName           entry name to be renamed or replaced
   998     @param   aNewName           a new entry name
   999     @param   aMode              specifies renaming / replacing
  1000     @param   aNewDosEntryPos    on exit contains new entry Pos.
  1001 */
  1002 void CFatMountCB::DoRenameOrReplaceL(const TDesC& aOldName, const TDesC& aNewName, TRenMode aMode, TEntryPos& aNewName_DosEntryPos)
  1003 	{
  1004     __PRINT3(_L("CFatMountCB::DoRenameOrReplaceL() mode:%d old:%S, new:%S"), aMode, &aOldName, &aNewName);
  1005 
  1006     const TBool namesAreIdentical = FileNamesIdentical(aOldName, aNewName); //-- this is case-insensitive.
  1007     const TBool renameMode = (aMode == EModeRename);
  1008     const TBool replaceMode = !renameMode;
  1009     TInt  nRes;
  1010 
  1011     if(namesAreIdentical && replaceMode)
  1012         return; //-- nothing to do, replacing file with itself
  1013 
  1014     //---------------------------------------------------------------------------------------------------------------------------
  1015     //-- 1. find the entries of 'aOldName' file. It must always succeed, because FileServer firstly tries to locate 'aOldName'
  1016 
  1017     TFatDirEntry oldName_FirstEntry; //-- first entry of the "aOldName" entryset
  1018     TEntryPos    oldName_FirstEntryPos(RootIndicator(), 0); //-- dir. pos of the start "aOldName" VFAT entry set
  1019 
  1020     FindEntryStartL(aOldName, KEntryAttMaskSupported, oldName_FirstEntry, oldName_FirstEntryPos);
  1021 
  1022     TFatDirEntry oldName_DosEntry    = oldName_FirstEntry;   //-- "aOldName" entry set DOS entry
  1023     TEntryPos    oldName_DosEntryPos = oldName_FirstEntryPos;//-- dir. pos of the "aOldName" DOS entry
  1024 
  1025     MoveToDosEntryL(oldName_DosEntryPos, oldName_DosEntry);
  1026 
  1027     const TBool bOldNameIsVFAT = !(oldName_DosEntryPos == oldName_FirstEntryPos); //-- ETrue if "aOldName" is VFAT name, i.e. consists of mode than 1 entry
  1028 
  1029     //-- check if the file "aOldName" is clamped. In this case it can't be replaced.
  1030     if(replaceMode && (IsFileClamped(StartCluster(oldName_DosEntry)) > 0))
  1031         User::Leave(KErrInUse);
  1032 
  1033     //---------------------------------------------------------------------------------------------------------------------------
  1034     //-- 2. find the entry of 'aNewName' file. Further behavior depends on rename/replace mode and if this file exists or not
  1035 
  1036     //-- extract new file name from the full path
  1037     TPtrC ptrNewName;
  1038     TPtrC ptrNewNameParentDir;
  1039     const TInt delimPos = aNewName.LocateReverse(KPathDelimiter);
  1040     ptrNewName.Set(aNewName.Mid(delimPos+1));
  1041     ptrNewNameParentDir.Set(aNewName.Left(delimPos+1));
  1042 
  1043     //-- find the parent directory of the "aNewName" and create iterator for it
  1044     TLeafDirData leafDir;
  1045     const TEntryPos aNewName_ParentDirPos = TEntryPos(FindLeafDirL(ptrNewNameParentDir, leafDir), 0); //-- 'aNewName' parent directory iterator
  1046     aNewName_DosEntryPos = aNewName_ParentDirPos;
  1047 
  1048     TEntryPos    newName_VFatEntryPos; //-- dir. pos of the start "aNewName" VFAT entry set
  1049     TFatDirEntry newName_DosEntry;
  1050 
  1051     TFileName fileName;
  1052     iFileCreationHelper.InitialiseL(ptrNewName);
  1053     TFatDirEntry startEntry;
  1054 
  1055     TRAP(nRes, DoFindL(ptrNewName, KEntryAttMaskSupported,
  1056 	    		newName_VFatEntryPos, startEntry, aNewName_DosEntryPos, newName_DosEntry,
  1057 	    		fileName, KErrNotFound,
  1058 	    		&iFileCreationHelper,
  1059 	    		leafDir));
  1060 
  1061     if (nRes!=KErrNone && nRes!=KErrNotFound)
  1062         User::Leave(nRes);
  1063 
  1064     const TBool newFileExists = (nRes == KErrNone); //-- ETrue if 'aNewName' file exists.
  1065     const TBool bNewNameIsVFAT = !IsLegalDosName(ptrNewName, EFalse, EFalse, EFalse, EFalse, ETrue);
  1066 
  1067     if(renameMode && newFileExists)
  1068     	{
  1069         if(!namesAreIdentical)
  1070         {
  1071         if ((newName_DosEntry.Attributes()&KEntryAttDir) != (oldName_DosEntry.Attributes()&KEntryAttDir))
  1072         	{
  1073         	User::Leave(KErrAccessDenied); 	//-- leave with KErrAccessDenied if it is trying to rename a file
  1074         									//		to a dir or vice versa.
  1075         	}
  1076         User::Leave(KErrAlreadyExists); //-- can't rename file if the file with 'aNewName' already exists
  1077         }
  1078         else
  1079         	{
  1080             if(!bNewNameIsVFAT && !bOldNameIsVFAT)
  1081                 return; //-- renaming DOS name to itself
  1082         	}
  1083         //-- allow renaming entry to itself. "namesAreIdentical" is case-insensitive. use case: "FILE" -> "File"
  1084     	}
  1085 
  1086     //---------------------------------------------------------------------------------------------------------------------------
  1087 
  1088     if(replaceMode && newFileExists)
  1089     	{
  1090         //---------------------------------------------------------------------------------------------------------------------------
  1091         //-- replace contents of the 'aNewName' with 'aOldName' and remove 'aOldName' entries.
  1092 
  1093         //-- check if we are still trying to replace the file with itself, probably using short name alias
  1094         if(aNewName_DosEntryPos == oldName_DosEntryPos)
  1095             return; //-- nothing to do, it's the same file
  1096 
  1097         const TInt oldNameStartCluster = StartCluster(oldName_DosEntry);
  1098         const TInt newNameStartCluster = StartCluster(newName_DosEntry); //-- store starting cluster of the chain to be unlinked
  1099 
  1100         newName_DosEntry.SetStartCluster(oldNameStartCluster);
  1101         newName_DosEntry.SetSize(oldName_DosEntry.Size());
  1102         newName_DosEntry.SetTime(oldName_DosEntry.Time(TTimeIntervalSeconds(0)), TTimeIntervalSeconds(0));
  1103         newName_DosEntry.SetAttributes(oldName_DosEntry.Attributes());
  1104 
  1105             if(IsRuggedFSys())
  1106             	{
  1107             	//-- Note 1.
  1108                 //-- set a special Id in reserved section for old and new entries.
  1109                 //-- if write fails before the old entry gets erased, we will have 2 entries pointing to the same clusterchain.
  1110                 //-- ScanDrive is responsible for fixing this situation by erasing entry with ID KReservedIdOldEntry.
  1111                 //-- note that  SetRuggedFatEntryId() uses "LastAccessTime" DOS FAT entry field to store the ID.
  1112                 //-- in normal situation this field isn't used, though Windows checkdisk can chack its validiy.
  1113                 //-- KReservedIdNewEntry == 0x0000 that corresponds to year 1980.
  1114 
  1115 	            newName_DosEntry.SetRuggedFatEntryId(KReservedIdNewEntry);
  1116 	            oldName_DosEntry.SetRuggedFatEntryId(KReservedIdOldEntry);
  1117 	            WriteDirEntryL(oldName_DosEntryPos, oldName_DosEntry);
  1118                 }
  1119 
  1120         //-- write 'aNewName' DOS dir. entry data back
  1121         WriteDirEntryL(aNewName_DosEntryPos, newName_DosEntry);
  1122 
  1123         //-- erase "oldName" entryset.
  1124         EraseDirEntryL(oldName_FirstEntryPos, oldName_FirstEntry);
  1125 
  1126         //-- free 'aNewName' cluster list
  1127         FAT().FreeClusterListL(newNameStartCluster);
  1128 
  1129         if(IsRuggedFSys())
  1130             FAT().FlushL();
  1131 
  1132     	}
  1133     else //if(replaceMode && newFileExists)
  1134     	{
  1135         //---------------------------------------------------------------------------------------------------------------------------
  1136         //-- Renaming 'aOldName' to 'aNewName': add 'aNewName' entry set and remove 'aOldName' entryset
  1137 
  1138         TFatDirEntry newDosEntry = oldName_DosEntry;
  1139         //-- generate short name for the 'aNewName' entryset and make new DOS entry
  1140         if(bNewNameIsVFAT)
  1141         	{//-- need to generate a short name for VFAT entryset DOS entry
  1142             TShortName shortName;
  1143 
  1144 		    if (iFileCreationHelper.GetValidatedShortName(shortName) == KErrNotFound)
  1145 		    	{
  1146 		        GenerateShortNameL(aNewName_DosEntryPos.Cluster(), ptrNewName, shortName, ETrue);
  1147 		    	}
  1148 
  1149             newDosEntry.SetName(shortName);
  1150         	}
  1151         else
  1152         	{//-- just use 'aNewName' as DOS name.
  1153             TBuf8<KFatDirNameSize+1> tmp; //-- the name may be "XXXXXXXX.YYY"
  1154             tmp.Copy(ptrNewName);
  1155             newDosEntry.SetName(DosNameToStdFormat(tmp));
  1156         	}
  1157 
  1158         if(IsRuggedFSys())
  1159         	{//-- the the note(1) above
  1160             newDosEntry.SetRuggedFatEntryId(KReservedIdNewEntry);
  1161             oldName_DosEntry.SetRuggedFatEntryId(KReservedIdOldEntry);
  1162             WriteDirEntryL(oldName_DosEntryPos, oldName_DosEntry);
  1163         	}
  1164 
  1165         //-- add new entryset to the directory
  1166         aNewName_DosEntryPos.iPos = 0;
  1167         aNewName_DosEntryPos.iCluster = aNewName_ParentDirPos.Cluster();
  1168 
  1169 	    if (iFileCreationHelper.IsNewEntryPosFound())
  1170 	    	{
  1171 	    	aNewName_DosEntryPos = iFileCreationHelper.EntryAddingPos();
  1172 	    	}
  1173 
  1174         if(bNewNameIsVFAT)
  1175         	{
  1176             const TInt numEntries = NumberOfVFatEntries(ptrNewName.Length());
  1177             AddDirEntryL(aNewName_DosEntryPos, numEntries);
  1178             WriteDirEntryL(aNewName_DosEntryPos, newDosEntry, ptrNewName);
  1179             }
  1180         else
  1181             {//-- new name is one DOS entry only
  1182             AddDirEntryL(aNewName_DosEntryPos, 1);
  1183             WriteDirEntryL(aNewName_DosEntryPos, newDosEntry);
  1184             }
  1185 
  1186         //-- erase old entryset.
  1187         EraseDirEntryL(oldName_FirstEntryPos, oldName_FirstEntry);
  1188 
  1189         //-- if we have renamed (moved) a directory, need to update its pointer to parent directory ('..' entry)
  1190         if((newDosEntry.Attributes() & KEntryAttDir))
  1191         	{
  1192             TEntryPos parentPtrEntPos(StartCluster(newDosEntry), 1*KSizeOfFatDirEntry);
  1193 
  1194             TFatDirEntry chFatEnt;
  1195             ReadDirEntryL(parentPtrEntPos, chFatEnt);
  1196 
  1197             const TUint parentDirStartCluster_Old = StartCluster(chFatEnt);
  1198                   TUint parentDirStartCluster_New = aNewName_ParentDirPos.Cluster();
  1199 
  1200             if(parentDirStartCluster_New == RootClusterNum() && parentDirStartCluster_New != 0)
  1201             	{//-- we are in the root directory. for some reason, '..' entries of the directories in the root dir.
  1202             	//-- must have starting cluster 0
  1203                 parentDirStartCluster_New = 0;
  1204             	}
  1205 
  1206             if(parentDirStartCluster_Old != parentDirStartCluster_New)
  1207             	{
  1208                 chFatEnt.SetStartCluster(parentDirStartCluster_New);
  1209                 WriteDirEntryL(parentPtrEntPos, chFatEnt);
  1210             	}
  1211             // Invalidate leaf dir cache as it is hard to track the dir structure changes now
  1212             if (iLeafDirCache)
  1213             	{
  1214                 iLeafDirCache->Reset();
  1215             	}
  1216         	}
  1217     	}//else if(replaceMode && newFileExists)
  1218 
  1219     iFileCreationHelper.Close();
  1220 	}
  1221 
  1222 //-----------------------------------------------------------------------------------------
  1223 
  1224 /**
  1225     Rename 'aOldName' file/directory to 'aNewName'
  1226     all trailing dots from the names will be removed
  1227 
  1228     @param  aOldName        existing object name
  1229     @param  aNewName        new object name
  1230 */
  1231 void CFatMountCB::RenameL(const TDesC& aOldName, const TDesC& aNewName)
  1232     {
  1233     __PRINT3(_L("CFatMountCB::RenameL, drv:%d, %S %S"), DriveNumber(), &aOldName, &aNewName);
  1234 
  1235     CheckStateConsistentL();
  1236     CheckWritableL();
  1237 
  1238     TEntryPos newEntryPos;
  1239     DoRenameOrReplaceL(RemoveTrailingDots(aOldName), RemoveTrailingDots(aNewName) ,EModeRename, newEntryPos);
  1240 
  1241     if(!IsRuggedFSys())
  1242         FAT().FlushL();
  1243     }
  1244 
  1245 //-----------------------------------------------------------------------------------------
  1246 
  1247 /**
  1248     Replace contents of the 'aNewName' with the contents of 'aOldName'
  1249     all trailing dots from the names will be removed
  1250 
  1251     @param  aOldName        existing object name
  1252     @param  aNewName        new object name
  1253 */
  1254 void CFatMountCB::ReplaceL(const TDesC& aOldName,const TDesC& aNewName)
  1255     {
  1256 
  1257     __PRINT3(_L("CFatMountCB::ReplaceL, drv:%d, %S %S"), DriveNumber(), &aOldName, &aNewName);
  1258 
  1259     CheckStateConsistentL();
  1260     CheckWritableL();
  1261 
  1262     TEntryPos newEntryPos;
  1263     DoRenameOrReplaceL(RemoveTrailingDots(aOldName), RemoveTrailingDots(aNewName), EModeReplace, newEntryPos);
  1264     if(!IsRuggedFSys())
  1265         FAT().FlushL();
  1266     }
  1267 
  1268 //-----------------------------------------------------------------------------------------
  1269 
  1270 
  1271 /**
  1272     Try to find a directory entry by the given name and path.
  1273     This method _must_ leave if the entry is not found. See the caller.
  1274 
  1275     @param  aName   path to the directory object. all trailing dots from the name will be removed.
  1276     @param  anEntry on return will contain the entry data
  1277 
  1278     @leave  KErrPathNotFound if there is no path to the aName
  1279             KErrNotFound     if the entry corresponding to the aName is not found
  1280             system-wide erorr code of media read failure.
  1281 */
  1282 void CFatMountCB::EntryL(const TDesC& aName,TEntry& anEntry) const
  1283     {
  1284     __PRINT2(_L("CFatMountCB::EntryL, drv:%d, %S"), DriveNumber(), &aName);
  1285 
  1286     CheckStateConsistentL();
  1287 
  1288     TEntryPos entryPos(RootIndicator(),0);
  1289     TFatDirEntry entry;
  1290     TPtr fileName(anEntry.iName.Des());
  1291 
  1292     TPtrC fullName = RemoveTrailingDots(aName);
  1293     TInt namePos=fullName.LocateReverse(KPathDelimiter)+1; // There is always a path delimiter
  1294     TLeafDirData leafDir;
  1295     entryPos.iCluster=FindLeafDirL(fullName.Left(namePos), leafDir);
  1296     entryPos.iPos=0;
  1297     TEntryPos startPos;
  1298     TFatDirEntry startEntry;
  1299 
  1300     DoFindL(fullName.Mid(namePos),KEntryAttMaskSupported,
  1301     		startPos,startEntry,entryPos,entry,
  1302     		fileName,KErrNotFound,
  1303     		NULL,
  1304     		leafDir);
  1305 
  1306 
  1307     anEntry.iAtt=entry.Attributes();
  1308     anEntry.iSize=entry.Size();
  1309     anEntry.iModified=entry.Time(TimeOffset());
  1310 
  1311 	if (fileName.Length()==0)
  1312         {
  1313         TBuf8<0x20> dosName(DosNameFromStdFormat(entry.Name()));
  1314         LocaleUtils::ConvertToUnicodeL(fileName,dosName);
  1315         }
  1316     if ((TUint)anEntry.iSize>=sizeof(TCheckedUid))
  1317         ReadUidL(StartCluster(entry),anEntry);
  1318     }
  1319 
  1320 //-----------------------------------------------------------------------------------------
  1321 
  1322 /**
  1323     Set directory entry details.
  1324     @param  aName           entry name; all trailing dots from the name will be removed
  1325     @param  aTime           entry modification time (and last access as well)
  1326     @param  aSetAttMask     entry attributes OR mask
  1327     @param  aClearAttMask   entry attributes AND mask
  1328 
  1329 */
  1330 void CFatMountCB::SetEntryL(const TDesC& aName,const TTime& aTime,TUint aSetAttMask,TUint aClearAttMask)
  1331     {
  1332     __PRINT2(_L("CFatMountCB::SetEntryL, drv:%d, %S"), DriveNumber(), &aName);
  1333 
  1334     CheckStateConsistentL();
  1335     CheckWritableL();
  1336 
  1337     TEntryPos firstEntryPos(RootIndicator(),0);
  1338     TFatDirEntry firstEntry;
  1339     FindEntryStartL(RemoveTrailingDots(aName),KEntryAttMaskSupported,firstEntry,firstEntryPos);
  1340     MoveToDosEntryL(firstEntryPos,firstEntry);
  1341     TUint setAttMask=aSetAttMask&KEntryAttMaskSupported;
  1342     if (setAttMask|aClearAttMask)
  1343         {
  1344         TInt att=firstEntry.Attributes();
  1345         att|=setAttMask;
  1346         att&=(~aClearAttMask);
  1347         firstEntry.SetAttributes(att);
  1348         }
  1349     if (aSetAttMask&KEntryAttModified)
  1350 		{
  1351 		firstEntry.SetTime(aTime,TimeOffset());
  1352 		}
  1353     WriteDirEntryL(firstEntryPos,firstEntry);
  1354     }
  1355 
  1356 //-----------------------------------------------------------------------------------------
  1357 
  1358 void CFatMountCB::DoCheckFatForLoopsL(TInt aCluster, TInt& aPreviousCluster, TInt& aChangePreviousCluster, TInt& aCount) const
  1359 //
  1360 // Check one fat cluster for loops.
  1361 //
  1362     {
  1363 
  1364     if (aCluster==aPreviousCluster)
  1365         User::Leave(KErrCorrupt); // Found loop
  1366 
  1367     aCount++;
  1368     if (aCount==aChangePreviousCluster)
  1369         {
  1370         aCount=0;
  1371         aChangePreviousCluster<<=1;
  1372         aPreviousCluster=aCluster;
  1373         }
  1374     }
  1375 
  1376 //-----------------------------------------------------------------------------------------
  1377 
  1378 void CFatMountCB::CheckFatForLoopsL(const TFatDirEntry& anEntry) const
  1379 //
  1380 // Check for loops
  1381 //
  1382     {
  1383 
  1384     TInt cluster=StartCluster(anEntry);
  1385     if (cluster==0 && anEntry.Size()==0)
  1386         return;
  1387 
  1388     TInt previousCluster=cluster;
  1389     TInt changePreviousCluster=1;
  1390     TInt count=0;
  1391 
  1392 
  1393     for(;;)
  1394         {
  1395         if ((TUint)cluster < KFatFirstSearchCluster || (!IsEndOfClusterCh(cluster) && (TUint)cluster>MaxClusterNumber()))
  1396             User::Leave(KErrCorrupt);
  1397 
  1398          if(!FAT().GetNextClusterL(cluster))
  1399             break;
  1400 
  1401          DoCheckFatForLoopsL(cluster, previousCluster, changePreviousCluster, count);
  1402         }
  1403 
  1404     }
  1405 
  1406 //-----------------------------------------------------------------------------------------
  1407 
  1408 /**
  1409     Open/Create/Replace a file on the current mount.
  1410 
  1411     @param  aName   file name; all trailing dots from the name will be removed
  1412     @param  aMode   File open mode, See TFileMode
  1413     @param  anOpen  specifies action: open, create or replace the file
  1414     @param  aFile   pointer to the CFileCB object to populate
  1415 
  1416 */
  1417 void CFatMountCB::FileOpenL(const TDesC& aName,TUint aMode,TFileOpen anOpen,CFileCB* aFile)
  1418     {
  1419     __PRINT3(_L("CFatMountCB::FileOpenL, drv:%d, mode:%d, name:%S"), DriveNumber(), anOpen, &aName);
  1420 
  1421     CheckStateConsistentL();
  1422 
  1423     TPtrC fullName = RemoveTrailingDots(aName); //-- remove trailing dots from the name
  1424 
  1425     TFatDirEntry firstEntry;
  1426     TEntryPos firstEntryPos(RootIndicator(),0);
  1427     TInt nPos=fullName.LocateReverse(KPathDelimiter)+1; // There is always a path delimiter
  1428     TPtrC name(fullName.Mid(nPos));
  1429     TInt ret = KErrNone;
  1430 
  1431     iFileCreationHelper.Close();
  1432     if (anOpen == EFileCreate || anOpen == EFileReplace)
  1433     	{
  1434     	iFileCreationHelper.InitialiseL(name);
  1435         TRAP(ret,FindEntryStartL(fullName,KEntryAttMaskSupported,firstEntry,firstEntryPos,&iFileCreationHelper));
  1436     	}
  1437     else
  1438         {
  1439         TRAP(ret,FindEntryStartL(fullName,KEntryAttMaskSupported,firstEntry,firstEntryPos));
  1440         }
  1441 
  1442     if (ret!=KErrNone && ret!=KErrNotFound)
  1443         User::Leave(ret);
  1444 
  1445     if (ret==KErrNone)
  1446         {
  1447         MoveToDosEntryL(firstEntryPos,firstEntry);
  1448         if ((firstEntry.Attributes()&KEntryAttDir) || (firstEntry.Attributes()&KEntryAttVolume))
  1449             User::Leave(KErrAccessDenied);
  1450         if (anOpen==EFileCreate)
  1451             User::Leave(KErrAlreadyExists);
  1452         if ((firstEntry.Attributes()&KEntryAttReadOnly) && aMode&EFileWrite)
  1453             User::Leave(KErrAccessDenied);
  1454         if((aMode & EFileWrite) && (IsFileClamped(StartCluster(firstEntry))>0))
  1455             User::Leave(KErrInUse);
  1456         CheckFatForLoopsL(firstEntry);
  1457         }
  1458     else
  1459         {
  1460         if (anOpen==EFileOpen)
  1461             User::Leave(KErrNotFound);
  1462 
  1463         //-- here we try to either create or replace file
  1464         CheckWritableL();
  1465 
  1466     	TLeafDirData leafDir;
  1467 
  1468         TInt numEntries = iFileCreationHelper.NumOfAddingEntries();
  1469         TShortName shortName;
  1470         if (iFileCreationHelper.GetValidatedShortName(shortName) == KErrNotFound)
  1471         	{
  1472             firstEntryPos.iCluster=FindLeafDirL(fullName.Left(nPos), leafDir);
  1473             GenerateShortNameL(firstEntryPos.iCluster,name,shortName,ETrue);
  1474         	}
  1475 
  1476         if (iFileCreationHelper.IsNewEntryPosFound())
  1477 	    	{
  1478 	    	firstEntryPos = iFileCreationHelper.EntryAddingPos();
  1479 	    	}
  1480         else
  1481         	{
  1482         	firstEntryPos.iCluster=FindLeafDirL(fullName.Left(nPos), leafDir);
  1483         	firstEntryPos.iPos=0;
  1484         	}
  1485 
  1486         AddDirEntryL(firstEntryPos,numEntries);
  1487         firstEntry.InitZ();
  1488         firstEntry.SetName(shortName);
  1489         firstEntry.SetStartCluster(0);
  1490 
  1491         TTime now;
  1492 		now.UniversalTime();
  1493         firstEntry.SetCreateTime(now, TimeOffset() );
  1494 
  1495         if (iFileCreationHelper.IsTrgNameLegalDosName())
  1496             WriteDirEntryL(firstEntryPos,firstEntry);
  1497         else
  1498             WriteDirEntryL(firstEntryPos,firstEntry,name);
  1499         }
  1500 
  1501     CFatFileCB& file=(*((CFatFileCB*)aFile));
  1502     file.SetL(firstEntry,(TShare)(aMode&KFileShareMask),firstEntryPos);
  1503     if (anOpen==EFileReplace && file.Size())
  1504         {
  1505         file.SetSizeL(0);
  1506         file.SetSize(0);
  1507         }
  1508     if (file.IsSeekIndex()==EFalse)
  1509         file.CreateSeekIndex();
  1510     if (anOpen==EFileReplace || anOpen==EFileCreate)
  1511         file.SetArchiveAttribute();
  1512 
  1513     if(!IsRuggedFSys())
  1514         FAT().FlushL();
  1515 
  1516     iFileCreationHelper.Close();
  1517     }
  1518 
  1519 //-----------------------------------------------------------------------------------------
  1520 
  1521 
  1522 /**
  1523     Open a directory on the current mount.
  1524 
  1525     @param  aName   path to the object in the directory we want to open; all trailing dots from the name will be removed
  1526     @param  aDir    dir. CB to be filled in.
  1527 
  1528     If there is no such a path, this method must leave with KErrPathNotFound
  1529 
  1530     @leave  KErrPathNotFound if thereis no such path
  1531     @leave  error code on media read fault
  1532 */
  1533 void CFatMountCB::DirOpenL(const TDesC& aName,CDirCB* aDir)
  1534     {
  1535     __PRINT2(_L("CFatMountCB::DirOpenL, drv:%d, %S"), DriveNumber(), &aName);
  1536 
  1537     CheckStateConsistentL();
  1538 
  1539     const TPtrC dirName = RemoveTrailingDots(aName); //-- remove trailing dots from the name
  1540 
  1541     TInt namePos=dirName.LocateReverse(KPathDelimiter);
  1542 
  1543     TFatDirEntry dosEntry;
  1544     TEntryPos dosEntryPos(RootIndicator(),0);
  1545     if (namePos==0)
  1546         InitializeRootEntry(dosEntry);
  1547     else
  1548         {
  1549         TPtrC dirPath=dirName.Left(namePos);
  1550         TInt dirPos=dirPath.LocateReverse(KPathDelimiter)+1;
  1551         TLeafDirData leafDir;
  1552         dosEntryPos.iCluster=FindLeafDirL(dirPath.Left(dirPos), leafDir); // Find directory before leaf
  1553         dosEntryPos.iPos=0;
  1554 
  1555         TFileName fileName;
  1556         TEntryPos startPos;
  1557         TFatDirEntry startEntry;
  1558         DoFindL(dirPath.Mid(dirPos),
  1559         		KEntryAttMatchMask|KEntryAttMatchExclusive,
  1560         		startPos, startEntry, dosEntryPos, dosEntry,
  1561         		fileName, KErrPathNotFound,
  1562         		NULL,
  1563         		leafDir);
  1564 
  1565 
  1566         }
  1567 
  1568     TPtrC matchName(dirName.Mid(namePos+1));
  1569     if (matchName.Length()==0)
  1570         matchName.Set(_L("*"));
  1571 
  1572     ((CFatDirCB*)aDir)->SetDirL(dosEntry,matchName);
  1573 
  1574     }
  1575 
  1576 //-----------------------------------------------------------------------------------------
  1577 
  1578 TBool CFatMountCB::IsDirectoryEmptyL(TInt aCluster)
  1579 //
  1580 // Check aCluster contains no directory entries other than . and ..
  1581 //
  1582     {
  1583 
  1584     __PRINT(_L("CFatMountCB::IsDirectoryEmptyL"));
  1585     TEntryPos dirEntryPos(aCluster,0);
  1586     TFatDirEntry dirEntry;
  1587     FOREVER
  1588         {
  1589         ReadDirEntryL(dirEntryPos,dirEntry);
  1590         MoveToDosEntryL(dirEntryPos,dirEntry);
  1591         if (dirEntry.IsParentDirectory() || dirEntry.IsCurrentDirectory())
  1592             goto LoopEnd;
  1593         if (dirEntry.IsEndOfDirectory())
  1594             return ETrue;
  1595         if (IsRootDir(dirEntryPos)&&(dirEntryPos.iPos+StartOfRootDirInBytes()==RootDirEnd()))
  1596             return ETrue;   //  Root Directory has no end of directory marker
  1597         if (!dirEntry.IsErased())
  1598             return EFalse;
  1599 LoopEnd:
  1600         MoveToNextEntryL(dirEntryPos);
  1601         }
  1602     }
  1603 
  1604 //-----------------------------------------------------------------------------------------
  1605 
  1606 /**
  1607     Overwrite as many contiguous file clusters as possible.
  1608 */
  1609 void CFatMountCB::DoWriteToClusterListL(TEntryPos& aPos,TInt aLength,const TAny* aSrc,const RMessagePtr2& aMessage,TInt anOffset, TInt aLastcluster, TInt &aBadcluster, TInt &aGoodcluster)
  1610     {
  1611 
  1612     __PRINT(_L("CFatMountCB::DoWriteToClusterListL"));
  1613     __ASSERT_ALWAYS(aPos.Cluster()>=KFatFirstSearchCluster,User::Leave(KErrCorrupt));
  1614 
  1615     TInt endCluster=0;
  1616 
  1617     const TInt clusterRelativePos=ClusterRelativePos(aPos.iPos);
  1618     const TInt maxClusters=((aLength+clusterRelativePos-1)>>ClusterSizeLog2())+1;
  1619     const TInt clusterListLen=FAT().CountContiguousClustersL(aPos.iCluster,endCluster,maxClusters);
  1620     const TInt writeLength=Min(aLength,(clusterListLen<<ClusterSizeLog2())-clusterRelativePos);
  1621     TInt64 dataStart=FAT().DataPositionInBytes(aPos.iCluster)+clusterRelativePos;
  1622 
  1623     TRAPD(r, iRawDisk->WriteL(dataStart,writeLength,aSrc,aMessage,anOffset));
  1624 
  1625     if(r == KErrNone) // Write succeded
  1626         {
  1627         aPos.iPos+=writeLength;
  1628         aPos.iCluster=endCluster;
  1629         return;
  1630         }
  1631 
  1632     if(r != KErrCorrupt) // failure not due to corruption so propogate up
  1633         User::Leave(r);
  1634 
  1635     TErrorInfoBuf errinf;
  1636     r = iRawDisk->GetLastErrorInfo(errinf);
  1637 
  1638     if(r == KErrNone && errinf().iReasonCode == TErrorInfo::EBadSector) // GetLastErrorInfo succeded and Last Error was caused by bad sector
  1639         {
  1640 
  1641         const TInt badcluster = (TInt)(((dataStart + errinf().iErrorPos) - ClusterBasePosition())>>ClusterSizeLog2())+KFatFirstSearchCluster;
  1642               TInt goodcluster = FAT().AllocateSingleClusterL(badcluster);
  1643 
  1644         //Calculate cluster number to check whether this write started at the beginning of new cluster or middle of previous cluster.
  1645         TInt cluster = aPos.iCluster;
  1646         if ( (aPos.iPos) && ((aPos.iPos)==((aPos.iPos >> ClusterSizeLog2())<<ClusterSizeLog2())))
  1647             cluster--;
  1648 
  1649         if((aPos.iPos != 0) && (badcluster == aPos.iCluster) && (aLastcluster == 0) && (aPos.iCluster == cluster))
  1650             { //Copy the contents already present in this cluster to new cluster allocated.
  1651             const TInt sizeToRead = aPos.iPos - ((aPos.iPos >> ClusterSizeLog2()) << ClusterSizeLog2());
  1652             dataStart = FAT().DataPositionInBytes(aPos.iCluster) + ClusterRelativePos((aPos.iPos - sizeToRead));
  1653 
  1654 
  1655             //-- Allocate the buffer required to copy the contents from bad cluster
  1656             RBuf8 clustBuf;
  1657             CleanupClosePushL(clustBuf);
  1658             if(clustBuf.CreateMax(sizeToRead) != KErrNone)
  1659                 {
  1660                 FAT().FreeClusterListL(goodcluster);
  1661                 User::Leave(KErrNoMemory);
  1662                 }
  1663 
  1664             r = LocalDrive()->Read(dataStart, sizeToRead, clustBuf); //Read the contents into buffer
  1665             if(r != KErrNone) //If read fails dont do anything not even marking bad cluster.
  1666                 {
  1667                 FAT().FreeClusterListL(goodcluster);
  1668                 User::Leave(r);
  1669                 }
  1670 
  1671             //Copy the bad and good cluster,required to adjust the start cluster number.
  1672             if(aBadcluster == 0)
  1673                 aBadcluster = badcluster;
  1674 
  1675             aGoodcluster = goodcluster;
  1676 
  1677             FOREVER
  1678                 {
  1679                 //Calculate and copy the contents to new cluster.
  1680                 aPos.iCluster = goodcluster;
  1681                 dataStart = FAT().DataPositionInBytes(aPos.iCluster) + ClusterRelativePos(aPos.iPos - sizeToRead);
  1682 
  1683                 r = LocalDrive()->Write(dataStart, clustBuf);
  1684                 if(r == KErrNone)
  1685                     { // Copied contents to new cluster so fix up the chain and mark the cluster as bad.
  1686                     FAT().WriteL(goodcluster, FAT().ReadL(badcluster));
  1687                     FAT().MarkAsBadClusterL(badcluster);
  1688                     aGoodcluster = goodcluster;
  1689                     CleanupStack::PopAndDestroy(&clustBuf); //-- deallocate a cluster buffer
  1690                     return;
  1691                     }
  1692                 else if(r == KErrCorrupt)
  1693                     {
  1694                     r = LocalDrive()->GetLastErrorInfo(errinf);
  1695                     if(r == KErrNone && errinf().iReasonCode == TErrorInfo::EBadSector)
  1696                         { //Allocate new cluster and adjust the cluster list.
  1697                         goodcluster = FAT().AllocateSingleClusterL(aPos.iCluster);
  1698                         FAT().MarkAsBadClusterL(aPos.iCluster);
  1699                         continue;
  1700                         }
  1701                         r = KErrCorrupt;
  1702                     }
  1703                     //Not able to write successfully so dont alter the original list.
  1704                     aBadcluster = aGoodcluster = 0;
  1705                     FAT().FreeClusterListL(goodcluster);
  1706                     User::Leave(r);
  1707                 }
  1708 
  1709         }//if((aPos.iPos != 0) && (badcluster == aPos.iCluster) && (aLastcluster == 0) && (aPos.iCluster == cluster))
  1710 
  1711         if((badcluster == aPos.iCluster) && (aLastcluster == 0)) //bad cluster at beginning of original clusterlist
  1712             {
  1713             // return bad and good clusters for CFatFileCB to fix up
  1714             FAT().WriteL(goodcluster, FAT().ReadL(badcluster));
  1715             aBadcluster = badcluster;
  1716             aGoodcluster = goodcluster;
  1717             aPos.iCluster = goodcluster;
  1718             }
  1719         else    //fix up chain
  1720             {
  1721             FAT().WriteL(goodcluster, FAT().ReadL(badcluster));
  1722             if(badcluster > aPos.iCluster)  //bad cluster not first in this contiguous list
  1723                 FAT().WriteL(badcluster-1, goodcluster);
  1724             else    //first cluster of this contigous list bad so update last cluster of previous contiguous list
  1725                 FAT().WriteL(aLastcluster, goodcluster);
  1726             }
  1727 
  1728         FAT().MarkAsBadClusterL(badcluster);
  1729 
  1730 
  1731         return;
  1732         }
  1733     User::Leave(KErrCorrupt);
  1734     }
  1735 
  1736 //-----------------------------------------------------------------------------------------
  1737 
  1738 void CFatMountCB::WriteToClusterListL(TEntryPos& aPos,TInt aLength,const TAny* aSrc,const RMessagePtr2& aMessage,TInt anOffset, TInt &aBadcluster, TInt& aGoodcluster)
  1739 //
  1740 // Overwrite cluster list.
  1741 //
  1742     {
  1743 
  1744     __PRINT(_L("CFatMountCB::WriteToClusterListL"));
  1745     __ASSERT_ALWAYS(aPos.Cluster()>=KFatFirstSearchCluster,User::Leave(KErrCorrupt));
  1746 
  1747     const TUint startPos=aPos.iPos;
  1748     const TUint temp=startPos>>ClusterSizeLog2();
  1749     const TUint length = (TUint)aLength;
  1750 
  1751     if ( (startPos) && ((startPos)==(temp<<ClusterSizeLog2())) )
  1752         {
  1753         __ASSERT_ALWAYS(FAT().GetNextClusterL(aPos.iCluster),User::Leave(KErrCorrupt));
  1754         }
  1755 
  1756     TUint offset=0;
  1757     TInt previouscluster=0;
  1758     FOREVER
  1759         {
  1760         DoWriteToClusterListL(aPos,length-offset,aSrc,aMessage,anOffset+offset, previouscluster, aBadcluster, aGoodcluster);
  1761         if (offset == (aPos.iPos-startPos))
  1762             continue;
  1763         offset=aPos.iPos-startPos;
  1764         __ASSERT_ALWAYS(aPos.iPos>startPos,User::Leave(KErrCorrupt));
  1765         previouscluster=aPos.iCluster;
  1766         if (offset<length)
  1767             {__ASSERT_ALWAYS(FAT().GetNextClusterL(aPos.iCluster),User::Leave(KErrCorrupt));}
  1768         if (offset>=length)
  1769             return;
  1770         }
  1771     }
  1772 
  1773 //-----------------------------------------------------------------------------------------
  1774 
  1775 void CFatMountCB::DoReadFromClusterListL(TEntryPos& aPos,TInt aLength,const TAny* aTrg,const RMessagePtr2& aMessage,TInt anOffset) const
  1776 //
  1777 // Read from as many contiguous file clusters as possible
  1778 //
  1779     {
  1780 
  1781     __PRINT(_L("CFatMountCB::DoReadFromClusterListL"));
  1782 
  1783     TInt endCluster=0;
  1784 
  1785     const TInt clusterRelativePos=ClusterRelativePos(aPos.iPos);
  1786     const TInt maxClusters=((aLength+clusterRelativePos-1)>>ClusterSizeLog2())+1;
  1787     const TInt clusterListLen=FAT().CountContiguousClustersL(aPos.iCluster,endCluster,maxClusters);
  1788     const TInt readLength=Min(aLength,(clusterListLen<<ClusterSizeLog2())-clusterRelativePos);
  1789     const TInt64 dataStart=FAT().DataPositionInBytes(aPos.iCluster)+clusterRelativePos;
  1790 
  1791     TRAPD(r, iRawDisk->ReadL(dataStart,readLength,aTrg,aMessage,anOffset));
  1792 
  1793     if(r == KErrNone) // Read succeded
  1794         {
  1795         aPos.iPos+=readLength;
  1796         aPos.iCluster=endCluster;
  1797         return;
  1798         }
  1799     if(r != KErrCorrupt) // failure not due to corruption so propogate up
  1800         User::Leave(r);
  1801 
  1802     TErrorInfoBuf errinf;
  1803     r = iRawDisk->GetLastErrorInfo(errinf);
  1804 
  1805     if(r == KErrNone && errinf().iReasonCode == TErrorInfo::EBadSector) // GetLastErrorInfo succeded and Last Error was caused by bad sector
  1806         {
  1807         TInt badcluster = (TInt)(((dataStart + errinf().iErrorPos) - ClusterBasePosition())>>ClusterSizeLog2())+KFatFirstSearchCluster;
  1808         FAT().MarkAsBadClusterL(badcluster);
  1809         }
  1810 
  1811     User::Leave(KErrCorrupt);
  1812     }
  1813 
  1814 //-----------------------------------------------------------------------------------------
  1815 
  1816 void CFatMountCB::ReadFromClusterListL(TEntryPos& aPos,TInt aLength,const TAny* aTrg,const RMessagePtr2& aMessage,TInt anOffset) const
  1817 //
  1818 // Read from cluster list
  1819 //
  1820     {
  1821 
  1822     __PRINT(_L("CFatMountCB::ReadFromClusterListL"));
  1823     __ASSERT_ALWAYS(aPos.Cluster()>=KFatFirstSearchCluster,User::Leave(KErrCorrupt));
  1824 
  1825     const TInt startPos=aPos.iPos;
  1826     const TInt temp=startPos>>ClusterSizeLog2();
  1827 
  1828     if ( (startPos) && ((startPos)==(temp<<ClusterSizeLog2())) )
  1829         {
  1830         __ASSERT_ALWAYS(FAT().GetNextClusterL(aPos.iCluster),User::Leave(KErrCorrupt));
  1831         }
  1832 
  1833     TInt offset=0;
  1834     FOREVER
  1835         {
  1836         DoReadFromClusterListL(aPos,aLength-offset,aTrg,aMessage,anOffset+offset);
  1837         offset=aPos.iPos-startPos;
  1838         if ((offset<aLength))
  1839             {
  1840             __ASSERT_ALWAYS(FAT().GetNextClusterL(aPos.iCluster),User::Leave(KErrCorrupt));
  1841             }
  1842         if (offset>=aLength)
  1843             return;
  1844         }
  1845     }
  1846 
  1847 //-----------------------------------------------------------------------------------------
  1848 
  1849 TInt CFatMountCB::FindLeafDirL(const TDesC& aName, TLeafDirData& aLeafDir) const
  1850 //
  1851 // Navigate the path to find the leaf directory.
  1852 // Returns the startcluster of data for the directory found.
  1853 //
  1854     {
  1855 
  1856     __PRINT(_L("CFatMountCB::FindLeafDirL"));
  1857 
  1858     TLex lex(aName);
  1859     TInt r;
  1860     TEntryPos entryPos(RootIndicator(),0);
  1861 
  1862     if (iLeafDirCache == NULL)
  1863     	{
  1864         TInt leaflen=(iLastLeafDir) ? iLastLeafDir->Length() : 0;
  1865         TInt namelen=aName.Length();
  1866         if (leaflen>1 && namelen>=leaflen && *iLastLeafDir==aName.Left(leaflen))
  1867             {
  1868             if (leaflen==namelen)
  1869                 return(iLastLeafDirCluster);
  1870             lex.Inc(leaflen-1);
  1871             entryPos.iCluster=iLastLeafDirCluster;
  1872             }
  1873     	}
  1874     else
  1875     	{
  1876         // Skip root directory
  1877         if (iLeafDirCache->CacheCount() > 0 && aName.Length() > 1)
  1878         	{
  1879         	TInt err = iLeafDirCache->FindInCache(aName, aLeafDir);
  1880         	if (err == KErrNone)
  1881         		{
  1882         		ASSERT(aLeafDir.iClusterNum > 0);
  1883         		return aLeafDir.iClusterNum;
  1884         		}
  1885         	else if (err != KErrNotFound)
  1886         		{
  1887         		User::LeaveIfError(err);
  1888         		}
  1889         	}
  1890     	}
  1891 
  1892     FOREVER
  1893         {
  1894         lex.Inc(); // Skip path delimiter
  1895         lex.Mark();
  1896         r=lex.Remainder().Locate(KPathDelimiter);
  1897         if (r==KErrNotFound)
  1898             r=lex.Remainder().Length();
  1899         if (r==0) // End of the path
  1900             break;
  1901         lex.Inc(r); // Set the token length
  1902         TFatDirEntry entry;
  1903 
  1904         TFileName fileName;
  1905         TEntryPos startPos;
  1906         TFatDirEntry startEntry;
  1907         DoFindL(lex.MarkedToken(),
  1908         		KEntryAttMatchMask|KEntryAttMatchExclusive,
  1909         		startPos, startEntry, entryPos, entry,
  1910         		fileName, KErrPathNotFound,
  1911         		NULL,
  1912         		aLeafDir);
  1913 
  1914 
  1915         entryPos.iCluster=StartCluster(entry);
  1916         entryPos.iPos=0;
  1917         }
  1918 
  1919     if (iLeafDirCache == NULL)
  1920     	{
  1921         AllocBufferL(((CFatMountCB*)this)->iLastLeafDir,aName);
  1922         ((CFatMountCB*)this)->iLastLeafDirCluster=entryPos.iCluster;
  1923     	}
  1924     else
  1925     	{
  1926         if (aName.Length() > 1)
  1927         	{
  1928         	aLeafDir = TLeafDirData(entryPos.iCluster);
  1929             iLeafDirCache->AddToCacheL(aName, aLeafDir);
  1930         	}
  1931     	}
  1932 
  1933     return entryPos.iCluster;
  1934     }
  1935 
  1936 //-----------------------------------------------------------------------------------------
  1937 
  1938 /**
  1939     Search for a specified name winthin directory cache
  1940     Works similary to TBool CFatMountCB::DoFindL()
  1941 
  1942     @param  anAtt           attributes of the object to find
  1943     @param  aStartEntryPos  on return in case of VFAT entry will contain start position of the VFAT dir. entry
  1944     @param  aStartEntry     on return will contain first VFAT dir entry
  1945     @param  aDosEntryPos    the search will start from this position of dir entry, on return it will contain result DOS entry position, last one for VFAT case
  1946     @param  aDosEntry       on return will contain DOS dir entry (the last one for VFAT case)
  1947     @param  aFileName       in the case of VFAT entry and on success here will be returned a long filename
  1948     @param  aAuxParam       some parameters package
  1949     @param  aFileCreationHelper       a helper package for file creations
  1950 
  1951     @return ETrue if the specified name is found in the cache. In this case aStartEntryPos, aStartEntry, aDosEntryPos, aDosEntry, aFileName will contain valid values
  1952 */
  1953 TBool CFatMountCB::DoRummageDirCacheL(const TUint anAtt, TEntryPos& aStartEntryPos,  
  1954 										TFatDirEntry& aStartEntry,	TEntryPos& aDosEntryPos,
  1955 										TFatDirEntry& aDosEntry,	TDes& aFileName,
  1956 										const TFindHelper& aAuxParam,
  1957 										XFileCreationHelper* aFileCreationHelper,
  1958 										const TLeafDirData& aLeafDir) const
  1959 	{
  1960     TBool bCacheMatchFound = EFalse;
  1961 
  1962     //-- get an interface to the Dir. cache
  1963     MWTCacheInterface* pDirCache = iRawDisk->DirCacheInterface();
  1964     ASSERT(pDirCache);
  1965     if(!pDirCache)
  1966         return EFalse;
  1967 
  1968     //-- save original values in order to restore them in the case of negative search results
  1969     TEntryPos       StartEntryPos1(aStartEntryPos);
  1970     TEntryPos       DosEntryPos1(aDosEntryPos);
  1971     TFatDirEntry    StartEntry1(aStartEntry);
  1972     TFatDirEntry    DosEntry1(aDosEntry);
  1973 
  1974     TInt64          nCachedLinPos;
  1975 
  1976     const TUint32 clSize = 1 << ClusterSizeLog2(); //-- media cluster size
  1977     const TUint32 cacheSz = pDirCache->CacheSizeInBytes(); //-- cache size in bytes
  1978     const TUint32 maxDirEntries = cacheSz >> KSizeOfFatDirEntryLog2;  //-- maximal number of dir entries that can be in the cache
  1979 
  1980     const TUint	  pageSzLog2 = pDirCache->PageSizeInBytesLog2();
  1981     TBool ScanMRUPageFirst 	= EFalse;
  1982 	TBool MRUPageScanned 	= EFalse;
  1983 
  1984 	// if MRU pos is availale, start with MRU page
  1985 	if (aLeafDir.iMRUPos.Cluster())
  1986     	{
  1987     	ScanMRUPageFirst = ETrue;
  1988     	DosEntryPos1 = aLeafDir.iMRUPos;
  1989     	}
  1990 
  1991 	TInt numFound = 0;
  1992 	TEntryPos startPos = DosEntryPos1;
  1993 	TInt clusterNum = DosEntryPos1.iCluster;
  1994 
  1995     for(TUint32 entryCnt=0; entryCnt < maxDirEntries; ++entryCnt)
  1996         {//-- walk through directory cluster list. The loop is limited by maximal number of dir entries
  1997          //-- that can be cached. Helps to avoid problems with infinite (looped) directories
  1998 
  1999         if (IsEndOfClusterCh(DosEntryPos1.iCluster))
  2000         	{
  2001         	// refer back to the last stored cluster position
  2002         	//  note aFileCreationHelper may not be initialised for file opening operations
  2003         	if (aFileCreationHelper && aFileCreationHelper->IsInitialised() && clusterNum != DosEntryPos1.iCluster)
  2004         		{
  2005         	    TEntryPos dummyPos(clusterNum, clSize - KSizeOfFatDirEntry);
  2006         	    aFileCreationHelper->SetEntryAddingPos(dummyPos);
  2007         		aFileCreationHelper->SetIsNewEntryPosFound(ETrue);
  2008         		}
  2009 
  2010         	if (ScanMRUPageFirst && !MRUPageScanned)
  2011         		{
  2012             	DosEntryPos1 = aDosEntryPos;
  2013             	MRUPageScanned = ETrue;
  2014             	continue;
  2015         		}
  2016             break; //-- this was the last cluster in this directory
  2017         	}
  2018 
  2019         const TUint32 pageStartPos = CalculatePageOffsetInCluster(DosEntryPos1.iPos, pageSzLog2);
  2020     	DosEntryPos1.iPos = pageStartPos;
  2021         TBool	PassedPageBoundary = EFalse;
  2022 
  2023         const TInt64  entryLinPos = MakeLinAddrL(DosEntryPos1); //-- linear media position of the cluster for this directory
  2024         const TUint32 cachePageSz = pDirCache->PosCached(entryLinPos, nCachedLinPos); //-- indicates if entryLinPos is cached
  2025         if(cachePageSz)
  2026             {//-- current page is in the directory cache
  2027              //__PRINT2(_L("#-!! CFatMountCB::DoRummageDirCacheL() Searching cl:%d, lin Pos:%X"),DosEntryPos1.iCluster,(TUint32)entryLinPos);
  2028 
  2029             //-- search to the end of the cached page.
  2030             // Note GetDirEntry() will read data beyond cache page boundary
  2031             const TUint32 nEntries = (1 << pageSzLog2) >> KSizeOfFatDirEntryLog2;
  2032 
  2033             TInt nErr;
  2034             //-- extract dir entries from the cached page and see if they match given name (aName)
  2035             /// until it reaches the next page
  2036             for(;;)
  2037                 {
  2038                 StartEntryPos1 = DosEntryPos1;
  2039                 TInt clSave = DosEntryPos1.iCluster; //-- need to save current cluster number because GetDirEntry() & MoveToNextEntryL() can change it
  2040 
  2041                 //-- get directory entry from the cache. We know that the DosEntryPos1 is cached.
  2042                 nErr = GetDirEntry(DosEntryPos1, DosEntry1, StartEntry1, aFileName);
  2043                 if(nErr != KErrNone)
  2044                     break;
  2045 
  2046                 if(DosEntry1.IsEndOfDirectory())
  2047                 	{
  2048                 	if (aFileCreationHelper && aFileCreationHelper->IsInitialised() && !aFileCreationHelper->IsNewEntryPosFound())
  2049 		            	{
  2050 		            	// note it is impossible to be at the end of the cluster chain here.
  2051 	            		aFileCreationHelper->SetEntryAddingPos(DosEntryPos1);
  2052 	            		aFileCreationHelper->SetIsNewEntryPosFound(ETrue);
  2053 		            	}
  2054 
  2055                 	if (ScanMRUPageFirst && !MRUPageScanned)
  2056                 		{
  2057                     	break;
  2058                 		}
  2059 
  2060                 	// if (!ScanMRUPageFirst || ScanMRUPageFirst && MRUPageScanned)
  2061                     goto Exit; //-- this was the last entry in this directory, no reason to look further
  2062                 	}
  2063 
  2064                 if (aFileCreationHelper && aFileCreationHelper->IsInitialised() && !aFileCreationHelper->IsNewEntryPosFound())
  2065 		        	{
  2066 		            if (!DosEntry1.IsErased() && !DosEntry1.IsGarbage())
  2067 		            	{
  2068 		            	numFound = 0;
  2069 		            	}
  2070 		            else
  2071 		            	{
  2072 		            	if (numFound == 0)
  2073 		            		{
  2074 		            		startPos = DosEntryPos1;
  2075 		            		}
  2076 		            	numFound++;
  2077 		            	if (numFound == aFileCreationHelper->NumOfAddingEntries())
  2078 		            		{
  2079 		            		aFileCreationHelper->SetEntryAddingPos(startPos);
  2080 		            		aFileCreationHelper->SetIsNewEntryPosFound(ETrue);
  2081 		            		}
  2082 		            	}
  2083 		        	}
  2084                 if(MatchEntryAtt(DosEntry1.Attributes(),anAtt))
  2085                     {//-- FAT or VFAT dir entry is extracted and attributes match. Compare names then.
  2086 
  2087                     if(StartEntry1.IsVFatEntry())
  2088                         {//-- extracted entry is VFAT one, name can be in UNICODE
  2089 
  2090                         // we only check short name candidates for long file names with VFAT entries,
  2091                         //  if it is a valid dos name, it will be checked by default
  2092                         // note here target name is always fully specified
  2093                         if (aFileCreationHelper && aFileCreationHelper->IsInitialised())
  2094                         	{
  2095                         	aFileCreationHelper->CheckShortNameCandidates(DosEntry1.Name().Ptr());
  2096                         	}
  2097 
  2098                         TPtrC ptrAssembledName = RemoveTrailingDots(aFileName);
  2099 
  2100                         if(ptrAssembledName.MatchF(aAuxParam.iTargetName) != KErrNotFound)
  2101                             {//-- found match in cache
  2102                             bCacheMatchFound = ETrue;
  2103                             goto Exit;
  2104                             }
  2105                         else if(aAuxParam.TrgtNameIsLegalDos())
  2106                             {
  2107                             if(aAuxParam.MatchDosEntryName(DosEntry1.Name().Ptr()))
  2108                                 {
  2109                                 bCacheMatchFound = ETrue;
  2110                                 goto Exit;
  2111                                 }
  2112                             }
  2113                         }//if(StartEntry1.IsVFatEntry())
  2114                     else if(aAuxParam.TrgtNameIsLegalDos())
  2115                         {//-- this is an old DOS FAT entry
  2116 
  2117                           if(aAuxParam.MatchDosEntryName(DosEntry1.Name().Ptr()))
  2118                             {
  2119                             //-- Here is the trick that helps with the situation when VFAT entry is split into 2 halves
  2120                             //-- between 2 clusters (or/and cache pages). I.e. 1st part of this long entry belongs to one cluster and even more might not be cached,
  2121                             //-- While the rest of the entry, DOS part of it is the 1st entry in the cluster and cached.
  2122                             //-- In this case if we search for short file name, we find it, but the aStartEntryPos will be incorrect, which leads to the directory corruption.
  2123                             //-- The simple and quick solution - discard 1st DOS entry and return to old search. It shall be quite rare.
  2124                             if(StartEntryPos1.iPos == 0)
  2125                                 {//-- this is the 1st FAT entry in the cluster. Discard it, see comments above.
  2126                                 __PRINT(_L("#------ CFatMountCB::DoRummageDirCacheL() discarding FAT Entry!!"));
  2127                                 goto Exit;
  2128                                 }
  2129 
  2130                             bCacheMatchFound = ETrue;
  2131                             goto Exit;
  2132                             }
  2133                         }
  2134 
  2135                     }//if(bGotEntry && MatchEntryAtt(DosEntry1.Attributes(),anAtt))
  2136 
  2137                 // check boundaries after GetDirEntry()
  2138                 // if we have cross the cluster boundary, break the for loop
  2139                 if(DosEntryPos1.iCluster != clSave)
  2140                     {//-- GetDirEntry() has decided to move to the next cluster.
  2141                     DosEntryPos1.iCluster = clSave;
  2142                     break;
  2143                     }
  2144 
  2145                 // if we are still in the same cluster, check the page boundary by
  2146                 /// exam how many entries we have scanned within the cluster
  2147                 const TUint entriesLooked =  ((DosEntryPos1.iPos + KSizeOfFatDirEntry)- pageStartPos) >> KSizeOfFatDirEntryLog2;
  2148                 if(entriesLooked > nEntries)
  2149                 	{
  2150                 	PassedPageBoundary = ETrue;
  2151                     break;
  2152                 	}
  2153 
  2154 
  2155                 // move to next entry before scanning next file
  2156                 TRAP(nErr,MoveToNextEntryL(DosEntryPos1));
  2157                 if(nErr != KErrNone)
  2158                     goto Exit;
  2159 
  2160                 // check boundaries after MoveToNextEntryL()
  2161                 if(DosEntryPos1.iCluster != clSave)
  2162                     {
  2163                     DosEntryPos1.iCluster = clSave;
  2164                     break;
  2165                     }
  2166 
  2167                 if (entriesLooked + 1 > nEntries)
  2168                 	{
  2169                 	PassedPageBoundary = ETrue;
  2170                     break;
  2171                 	}
  2172 
  2173                 } //for(;;)
  2174 
  2175             } //if(iRawDisk->PosCached(...))
  2176 
  2177         // scanning did not happen because the page is not cached,
  2178         // or
  2179         // scanning finished in last page and file is not found
  2180 
  2181         // if MRU page is not cached or
  2182         // we scan MRU page first and it is not scanned yet, then this must be the MRU page,
  2183         //  we now start to scan from the beginning
  2184         if (ScanMRUPageFirst && !MRUPageScanned)
  2185         	{
  2186         	MRUPageScanned = ETrue;
  2187         	DosEntryPos1 = aDosEntryPos;
  2188         	DosEntryPos1.iPos = 0;
  2189         	continue;
  2190         	}
  2191 
  2192         // if we just finished scanning a page and still in the same cluster, then we crossed page
  2193         // 	boundary, continue with next page.
  2194         // note: although we are in the 'next page' already, this page might not be cached, so we need to
  2195         //  check it via pDirCache->PosCached(entryLinPos, nCachedLinPos) and scan it properly.
  2196         if (PassedPageBoundary)
  2197         	{
  2198         	DosEntryPos1.iPos = CalculatePageOffsetInCluster(DosEntryPos1.iPos, pageSzLog2);
  2199         	PassedPageBoundary = EFalse;
  2200         	continue;
  2201         	}
  2202 
  2203         //-- try to move to the next cluster of the directory file
  2204 
  2205         if(DosEntryPos1.Cluster() < KFatFirstSearchCluster)  //-- small trick to get rid of TRAPping GetNextClusterL()
  2206             break;
  2207 
  2208         // record previous cluster number before move on
  2209         clusterNum = DosEntryPos1.iCluster;
  2210 
  2211         if(! FAT().GetNextClusterL(DosEntryPos1.iCluster))
  2212             break;
  2213 
  2214 
  2215     } //for(TUint32 entryCnt=0; entryCnt< maxDirEntries; ++entryCnt)
  2216 
  2217     //---------------------------------
  2218     Exit:
  2219 
  2220     if(bCacheMatchFound)
  2221         {
  2222         //-- if the position of the found in cache object is less than given, pretend that we haven't found anything
  2223         //-- Return to the old search, because it can be the case of the end of directory, which is quite difficult to
  2224         //-- detect in this situation. Note that the old part of DoFindL() leaves when the search reaches the end of dir.
  2225         TBool bFallBack=EFalse;
  2226 
  2227         if(DosEntryPos1.iCluster == aDosEntryPos.iCluster)
  2228             {
  2229             if(DosEntryPos1.iPos < aDosEntryPos.iPos)
  2230                 bFallBack = ETrue;
  2231             }
  2232         else
  2233             {
  2234             if(MakeLinAddrL(DosEntryPos1) < MakeLinAddrL(aDosEntryPos))
  2235                 bFallBack = ETrue;
  2236             }
  2237 
  2238         if(bFallBack)
  2239             {
  2240             return EFalse;
  2241             }
  2242 
  2243         //-- Update parameters with new values
  2244         aStartEntryPos= StartEntryPos1;
  2245         aDosEntryPos  = DosEntryPos1;
  2246         aStartEntry   = StartEntry1;
  2247         aDosEntry     = DosEntry1;
  2248 
  2249         const TInt64  mruPos = MakeLinAddrL(aDosEntryPos);
  2250         
  2251         pDirCache->MakePageMRU(mruPos);
  2252 
  2253     	//-- if the corresponding leaf directory name is cached, associate the last search positionin this directory.
  2254         //-- the next search in this dir. will start from this position (and will wrap around over the dir. beginning).
  2255         //-- the "last search position" will is the position of current VFAT entryset start. 
  2256     	if (aLeafDir.iClusterNum)
  2257     		{
  2258             iLeafDirCache->UpdateMRUPos(TLeafDirData(aLeafDir.iClusterNum, aStartEntryPos));
  2259     		}
  2260         }
  2261     return bCacheMatchFound;
  2262     }
  2263 
  2264 //-----------------------------------------------------------------------------------------
  2265 
  2266 /**
  2267     initialise find helper with the target file name.
  2268     This is a quite expensive operation and initialisation is done only once. After this we know if the name is a legal dos one
  2269     and also have the corresponding generated DOS name for it.
  2270 
  2271     @param aTargetName target file name we are looking for in ::DoFindL()
  2272 */
  2273 void CFatMountCB::TFindHelper::InitialiseL(const TDesC& aTargetName)
  2274     {
  2275     if(isInitialised)
  2276         return;
  2277 
  2278      TInt count = 1;
  2279 
  2280      iTargetName.Set(aTargetName);
  2281      isLegalDosName = IsLegalDosName(aTargetName, ETrue, EFalse, EFalse, ETrue, EFalse); 
  2282 
  2283      if(isLegalDosName)
  2284         {//-- iShortName will contain generated short DOS name by long filename
  2285         iShortName = DoGenerateShortNameL(aTargetName, count, ETrue);
  2286         }
  2287 
  2288      isInitialised = ETrue;
  2289     }
  2290 
  2291 /**
  2292     Perform binary comparison between a given the DOS entry name and the DOS name we generated in TFindHelper::Initialise().
  2293     @param  apDosEntryName pointer to the DOS entry name in XXXXXXXXYYY format
  2294     @return ETrue if the apDosEntryName is the same as generated iShortName
  2295 */
  2296 TBool CFatMountCB::TFindHelper::MatchDosEntryName(const TUint8* apDosEntryName) const
  2297     {
  2298     ASSERT(isInitialised);
  2299 
  2300     if(!isLegalDosName)
  2301         return EFalse;
  2302 
  2303     return (Mem::Compare(iShortName.Ptr(), KFatDirNameSize, apDosEntryName, KFatDirNameSize) == 0);
  2304     }
  2305 
  2306 //-----------------------------------------------------------------------------------------
  2307 const TInt KShortNameCandidatesNum = 4;
  2308 /**
  2309 Constructor of XFileCreationHelper class
  2310 */
  2311 CFatMountCB::XFileCreationHelper::XFileCreationHelper()
  2312 	{
  2313     isInitialised = EFalse;
  2314 	}
  2315 
  2316 /**
  2317 Destructor of XFileCreationHelper class
  2318 */
  2319 CFatMountCB::XFileCreationHelper::~XFileCreationHelper()
  2320 	{
  2321 	Close();
  2322 	}
  2323 
  2324 /**
  2325 Initialises a TFileCreationHelper object, generate a short name candidate pool.
  2326 
  2327 @param	aTargetName	Target file name for the potential new file.
  2328 @post	TFileCreationHelper is fully initialised.
  2329 */
  2330 void CFatMountCB::XFileCreationHelper::InitialiseL(const TDesC& aTargetName)
  2331 	{
  2332     // close before use, to avoid memory leak
  2333 	Close();
  2334 
  2335     iTargetName.Set(aTargetName);
  2336 	// generates short name candidate(s)
  2337     TInt count = 1;
  2338     while (count <= KShortNameCandidatesNum)
  2339 		{
  2340 		TShortName shortNameCandidate = DoGenerateShortNameL(aTargetName, count, ETrue);
  2341 		TInt err = iShortNameCandidates.Append(shortNameCandidate);
  2342 		User::LeaveIfError(err);
  2343 
  2344 		if (count == -1)	// No tilde and number is needed
  2345 			{
  2346 			break;
  2347 			}
  2348 		else
  2349 			count++;
  2350 		}
  2351 
  2352     // calculate number of new entries needed
  2353     iNumOfAddingEntries = 1;
  2354     isTrgNameLegalDosName = IsLegalDosName(aTargetName, EFalse, EFalse, EFalse, EFalse, ETrue);
  2355     if (!isTrgNameLegalDosName)
  2356     	iNumOfAddingEntries = (TUint16) NumberOfVFatEntries(iTargetName.Length());
  2357 
  2358     isNewEntryPosFound = EFalse;
  2359     isInitialised = ETrue;
  2360     }
  2361 
  2362 /**
  2363 Close function of XFileCreationHelper class
  2364 */
  2365 void CFatMountCB::XFileCreationHelper::Close()
  2366 	{
  2367 	iShortNameCandidates.Close();
  2368 	isInitialised = EFalse;
  2369 	}
  2370 
  2371 /**
  2372 Validates short name candidates. If the input dos entry name is found in the short name
  2373  candidate pool, the corresponding short name candidate will be removed from the pool.
  2374 
  2375 @param	apDosEntryName	An existing short name, to compare with the candidates.
  2376 @pre 	Object should be initialised
  2377 */
  2378 void CFatMountCB::XFileCreationHelper::CheckShortNameCandidates(const TUint8* apDosEntryName)
  2379     {
  2380     ASSERT(isInitialised);
  2381     if (!isInitialised)
  2382     	return;
  2383 
  2384     if (iShortNameCandidates.Count() > 0)
  2385     	{
  2386     	for (TInt i = 0; i < iShortNameCandidates.Count(); i++)
  2387     		{
  2388     		if (Mem::Compare(iShortNameCandidates[i].Ptr(), KFatDirNameSize, apDosEntryName, KFatDirNameSize) == 0)
  2389     			{
  2390     			iShortNameCandidates.Remove(i);
  2391     			break;
  2392     			}
  2393     		}
  2394     	}
  2395     }
  2396 
  2397 /**
  2398 Gets a validated short name from the short name candidate pool.
  2399 
  2400 @param	aShortName	On return, contains a validated short name if found, otherwise zeroed.
  2401 @return	TInt		Returns KErrNone if a validated short name found successfully,
  2402  					 else KErrNotFound is returned.
  2403  					Returns KErrNotReady if object is not initialised.
  2404 @pre 	Object should be initialised
  2405 */
  2406 TInt CFatMountCB::XFileCreationHelper::GetValidatedShortName(TShortName& aShortName) const
  2407 	{
  2408 	aShortName.Zero();
  2409 
  2410 	ASSERT(isInitialised);
  2411 	if (!isInitialised)
  2412 		return KErrNotReady;
  2413 
  2414 	if (iShortNameCandidates.Count() > 0)
  2415 		{
  2416 		aShortName.Copy(iShortNameCandidates[0]);
  2417 		return KErrNone;
  2418 		}
  2419 
  2420 	return KErrNotFound;
  2421 	}
  2422 
  2423 //-----------------------------------------------------------------------------------------
  2424 
  2425 
  2426 /**
  2427     Scan a directory looking for aName.
  2428 
  2429     @param  aTrgtName       a name of an object we are looking up in directory
  2430     @param  anAtt           attributes of this object
  2431     @param  aStartEntryPos  on return in case of VFAT entry will contain start position of the VFAT dir. entry
  2432     @param  aStartEntry     on return will contain first VFAT dir entry
  2433     @param  aDosEntryPos    the search will start from this position of dir entry, on return it will contain result DOS entry position, last one for VFAT case
  2434     @param  aDosEntry       on return will contain DOS dir entry (the last one for VFAT case)
  2435     @param  aFileName       in the case of VFAT entry and on success here will be returned a long filename
  2436     @param  anError         This function might leave with this given error code
  2437 	@param  aFileCreationHelper       a helper package for file creations
  2438 
  2439     @return ETrue if extracted entry is VFAT one, EFalse, if it's old DOS-style one
  2440     @leave  can leave with anError code on error or if the search has reached the end of directory (!)
  2441 */
  2442 TBool CFatMountCB::DoFindL(const TDesC& aTrgtName,TUint anAtt,
  2443 						TEntryPos& aStartEntryPos,TFatDirEntry& aStartEntry,
  2444 						TEntryPos& aDosEntryPos,TFatDirEntry& aDosEntry,
  2445 						TDes& aFileName,TInt anError,
  2446 						XFileCreationHelper* aFileCreationHelper,
  2447 						const TLeafDirData& aLeafDirData) const
  2448 	{
  2449     // check that the entry position to be read next is not past the end of the
  2450     // root directory. If this is the case then when GetDirEntryL(..) is called
  2451     // this will lead to MakeLinAddr(..) leaving with KErrDirFull.
  2452 
  2453     if (IsRootDir(aDosEntryPos)&&(aDosEntryPos.iPos+StartOfRootDirInBytes()>=RootDirEnd()))
  2454         User::Leave(anError);//Allows maximum number of entries in root directory
  2455 
  2456     __PRINT2(_L("CFatMountCB::DoFindL() drv:%d, %S"),Drive().DriveNumber(),&aTrgtName);
  2457 
  2458     TInt previousCluster=aDosEntryPos.iCluster;
  2459     TUint previousPosition=aDosEntryPos.iPos;
  2460     TInt changePreviousCluster=1;
  2461     TInt count=0;
  2462 
  2463     TBool trgNameIsWildCard     = EFalse; //-- ETrue if the name we are looking for is a wildcard
  2464     TBool trgNameFullySpecified = ETrue;  //-- ETrue if the name we are looking for doesn't contain wildcards
  2465 
  2466 
  2467     {
  2468     //-- find out if the name we are looking for is a wildcard ("*" or "*.*")
  2469     const TInt len = aTrgtName.Length();
  2470 
  2471     if(len == 1)
  2472         trgNameIsWildCard = (aTrgtName[0] == '*');
  2473     else if(len == 3)
  2474         {
  2475         _LIT(KAllFiles, "*.*");
  2476         trgNameIsWildCard = (aTrgtName==KAllFiles);
  2477         }
  2478 
  2479     //-- find out if the name we are looking for contains wildcharacters: "*" or "?"
  2480     if(trgNameIsWildCard)
  2481         trgNameFullySpecified = EFalse;
  2482     else
  2483         {
  2484         for(TInt i=0; i<len; ++i)
  2485             {
  2486             const TChar ch = aTrgtName[i];
  2487             if(ch == (TChar)'*' || ch == (TChar)'?')
  2488                 {
  2489                 trgNameFullySpecified = EFalse;
  2490                 break;
  2491                 }
  2492             }
  2493         }
  2494     }
  2495 
  2496 
  2497     TPtrC trgtNameNoDot(aTrgtName);
  2498 
  2499     TFindHelper findHelper;
  2500     //---------------------------------------------------
  2501     //-- if we have fully specified name and directory cache is present, try to
  2502     //-- locate the name in the cache first to avoid reading from media
  2503     //-- if the entry belongs to the root directory (for FAT12,16) skip the lookup, because root directory isn't aligned by cluster size boundary,
  2504     //-- while directory cache pages are. For FAT32 it doesn't matter, because root dir is a usual file.
  2505     
  2506     //-- the "rummage dir. cache" can be swithed off. This is not affecting the functionality, only the performance.
  2507  #ifdef USE_DIR_CACHE_RUMMAGE
  2508                                                                  
  2509     if(iRawDisk->DirCacheInterface() && trgNameFullySpecified && !IsRootDir(aDosEntryPos) && !aFileCreationHelper)
  2510         {//-- aName is fully specified, i.e doesn't contain wildcards
  2511 
  2512         findHelper.InitialiseL(trgtNameNoDot);
  2513 
  2514         const TBool bMatchFound = DoRummageDirCacheL(anAtt, aStartEntryPos, aStartEntry, aDosEntryPos, aDosEntry, aFileName, findHelper, aFileCreationHelper, aLeafDirData);
  2515         if(bMatchFound)
  2516             {
  2517             return(aStartEntry.IsVFatEntry());
  2518             }
  2519         }
  2520  #endif
  2521 
  2522     //---------------------------------------------------
  2523 
  2524     // we need to scan ahead from the mru pos then come back to beginning, if startcluster is provided
  2525     TBool scanAhead = EFalse;
  2526     // if we have a starting cluster number (and it's not root directory in FAT16/12 case)&&
  2527     //  we found a lastScanned entry's cluster (and it's not root directory in FAT16/12 case)&&
  2528     // if we don't have a starting cluster number, we draw back to original scanning algorithm
  2529     if (!IsRootDir(aDosEntryPos) 			// we don't do forward scanning for root dir &
  2530     		&& aLeafDirData.iClusterNum != 0 	// if we have a starting cluster number &
  2531     		&& aLeafDirData.iMRUPos.Cluster() != 0) 	// if we have a starting cluster number &
  2532     	{
  2533     	scanAhead = ETrue;
  2534     	aDosEntryPos = aLeafDirData.iMRUPos;
  2535     	}
  2536 
  2537     TInt numFound = 0;
  2538     TEntryPos startPos = aDosEntryPos;
  2539     TInt clustNum = aDosEntryPos.Cluster();
  2540 
  2541     for (TInt scanCnt = 1; scanCnt <= 2; ++scanCnt)
  2542     	{
  2543     	// if we are not scanning ahead, we don't need this outer for loop
  2544     	if (!scanAhead)
  2545     		scanCnt++;
  2546 
  2547     	TBool found = EFalse;
  2548 
  2549         FOREVER //FOREVER2 -- walk through all directory entries in the current directory until find a match or directory end
  2550             {
  2551 	        //-- read full directory entry starting from aDosEntryPos. On return aFileName may contain assembled long filename (if the entry is VFAT)
  2552 	        //-- aDosEntry will contain a DOS entry of the directory entry we have read.
  2553 	        aStartEntryPos=aDosEntryPos;
  2554 	        User::LeaveIfError(GetDirEntry(aDosEntryPos, aDosEntry, aStartEntry, aFileName));
  2555 
  2556 	        if (aDosEntry.IsEndOfDirectory())
  2557 	            {//-- the end of directory reached.
  2558 
  2559 	            // if new entry position for adding has not been found yet.
  2560 	            // note aFileCreationHelper may not be initialised for pure file opening operations
  2561 	            if (aFileCreationHelper && aFileCreationHelper->IsInitialised() && !aFileCreationHelper->IsNewEntryPosFound())
  2562 	            	{
  2563 	            	// if MoveToNextEntryL have gone to the next cluster which is the end of cluster chain,
  2564 	            	//  we pass the last scanned entry position to AddDirEntryL
  2565 	            	if (IsEndOfClusterCh(aDosEntryPos.iCluster))
  2566 	            		{
  2567 	            	    TInt clusterSize=1<<ClusterSizeLog2();
  2568 	            	    TEntryPos dummyPos(clustNum, clusterSize - KSizeOfFatDirEntry);
  2569 	            	    aFileCreationHelper->SetEntryAddingPos(dummyPos);
  2570 	            		aFileCreationHelper->SetIsNewEntryPosFound(ETrue);
  2571 	            		}
  2572 	            	// or we reached the end of the directory.
  2573 	            	else
  2574 	            		{
  2575 	            		aFileCreationHelper->SetEntryAddingPos(aDosEntryPos);
  2576 	            		aFileCreationHelper->SetIsNewEntryPosFound(ETrue);
  2577 	            		}
  2578 	            	}
  2579 
  2580 	            // if we are scanning ahead and this is the first scanning, we break out to restart scanning
  2581 	            if (scanAhead && scanCnt == 1)
  2582 	            	{
  2583 	            	break; // from FOREVER, restart scanning
  2584 	            	}
  2585 
  2586 	            // if (!scanAhead || scanAhead && scanCnt == 2)
  2587 	            User::Leave(anError);
  2588 	            }
  2589 
  2590 
  2591 	        // entry space searching for potential new file/directory creation
  2592 	        if (aFileCreationHelper && aFileCreationHelper->IsInitialised() && !aFileCreationHelper->IsNewEntryPosFound())
  2593 	        	{
  2594 	            if (!aDosEntry.IsErased() && !aDosEntry.IsGarbage())
  2595 	            	{
  2596 	            	numFound = 0;
  2597 	            	}
  2598 	            else
  2599 	            	{
  2600 	            	if (numFound == 0)
  2601 	            		{
  2602 	            		startPos = aDosEntryPos;
  2603 	            		}
  2604 	            	numFound++;
  2605 	            	if (numFound == aFileCreationHelper->NumOfAddingEntries())
  2606 	            		{
  2607 	            		aFileCreationHelper->SetEntryAddingPos(startPos);
  2608 	            		aFileCreationHelper->SetIsNewEntryPosFound(ETrue);
  2609 	            		}
  2610 	            	}
  2611 	        	}
  2612 
  2613 
  2614 	        if (IsRootDir(aDosEntryPos)&&(aDosEntryPos.iPos+StartOfRootDirInBytes()==(RootDirEnd()-KSizeOfFatDirEntry)))
  2615 	            if (aDosEntry.IsErased())
  2616 	                {
  2617 	                User::Leave(anError);//Allows maximum number of entries in root directory
  2618 	                }
  2619 
  2620 
  2621 	        const TBool bFileNameEntry = !aDosEntry.IsCurrentDirectory() && !aDosEntry.IsParentDirectory() && !aDosEntry.IsErased() && !aDosEntry.IsGarbage();
  2622 
  2623 	        if (bFileNameEntry && MatchEntryAtt(aDosEntry.Attributes(), anAtt))
  2624 	            {//-- we have read a filename entry and entry's attributes match required; compare names then.
  2625 
  2626 	            if (trgNameIsWildCard)
  2627 	            	{
  2628 	            	found = ETrue;
  2629 	            	break; //-- we were looking for '*' or '*.*', so will be satisfied with any current file name.
  2630 	            	}
  2631 
  2632 
  2633 	            if (aStartEntry.IsVFatEntry())
  2634 	                {//-- we've read a VFAT entry, aFileName is supposed to contain long filename, aDosEntry - DOS entry for this name.
  2635 	                 //-- note: aFileName.Length() may be 0, while DOS entry (short name is OK) in the case of orphaned VFAT entries
  2636 
  2637 
  2638 	                // we only check short name candidates for long file names with VFAT entries,
  2639 	                //  if it is a valid dos name, it will be checked by default
  2640 	                // note, for file creation cases, target name will be always fully specified
  2641 	                if (aFileCreationHelper && aFileCreationHelper->IsInitialised() && trgNameFullySpecified)
  2642 		            	 {
  2643 		            	 aFileCreationHelper->CheckShortNameCandidates(aDosEntry.Name().Ptr());
  2644 		            	 }
  2645 
  2646 	                //-- discard trailing dots from aFileName if present
  2647 	                 TPtrC ptrAssembledName = RemoveTrailingDots(aFileName);
  2648 
  2649 	                 if(ptrAssembledName.MatchF(trgtNameNoDot) != KErrNotFound)
  2650 	                	 {
  2651 						 found = ETrue;
  2652 	                	 break; //-- OK, found a match.
  2653 	                	 }
  2654 	                 else if (trgNameFullySpecified)
  2655 	                	 {
  2656 	                	 //-- long name assembled by GetDirEntry() doesn't match the target. But if he target name is fully specified,
  2657 	                	 //-- we need to compare corresponding DOS entries, because VFAT entries may be damaged, while DOS ones are OK.
  2658 	                     findHelper.InitialiseL(trgtNameNoDot);
  2659 
  2660 	                     if(findHelper.MatchDosEntryName(aDosEntry.Name().Ptr()))
  2661 	                    	 {
  2662 							 found = ETrue;
  2663 	                    	 break; //-- DOS entries match, success.
  2664 	                    	 }
  2665 
  2666 	                	 }
  2667 	                 else if (!trgNameFullySpecified)
  2668 	                	 {//-- target name contains wildcards, we need to use MatchF with dos name
  2669                          TBuf8<0x20> dosName8(DosNameFromStdFormat(aDosEntry.Name()));
  2670 	                     TBuf<0x20>  dosName;
  2671 	                     LocaleUtils::ConvertToUnicodeL(dosName, dosName8); //-- convert DOS name to unicode (implies locale settings)
  2672 	                     if (dosName.MatchF(trgtNameNoDot)!=KErrNotFound)
  2673 	                    	 {
  2674 							 found = ETrue;
  2675 							 break;
  2676 	                    	 }
  2677                          }
  2678 
  2679 
  2680 	                }
  2681 	            else //if (aStartEntry.IsVFatEntry())
  2682 	                {//-- we've read a legacy FAT entry, so compare DOS entries
  2683 	                findHelper.InitialiseL(trgtNameNoDot);
  2684 
  2685 	                if(findHelper.TrgtNameIsLegalDos())
  2686 	                    {//-- we are looking for a legal DOS name
  2687 	                    if(trgNameFullySpecified)
  2688 	                        {//-- if the target name is fully specified, we can yse binary comparison of the DOS entries
  2689 	                        if(findHelper.MatchDosEntryName(aDosEntry.Name().Ptr()))
  2690 	                        	{
  2691 								found = ETrue;
  2692 	                            break;
  2693 	                        	}
  2694 	                        }
  2695 	                    else
  2696 	                        {//-- target name contains wildcards, we neeed to use MatchF
  2697 	                        TBuf8<0x20> dosName8(DosNameFromStdFormat(aDosEntry.Name()));
  2698 	                        TBuf<0x20>  dosName;
  2699 	                        LocaleUtils::ConvertToUnicodeL(dosName, dosName8); //-- convert DOS name to unicode (implies locale settings)
  2700 	                        if (dosName.MatchF(trgtNameNoDot)!=KErrNotFound)
  2701 	                        	{
  2702 								found = ETrue;
  2703 	                            break;
  2704 	                        	}
  2705 
  2706 	                        }
  2707 	                    } //if(findHelper.TrgtNameIsLegalDos())
  2708 
  2709 	                } //else if (aStartEntry.IsVFatEntry())
  2710 
  2711 	            } //if (bFileNameEntry && MatchEntryAtt(aDosEntry.Attributes(),anAtt))
  2712 
  2713 
  2714 	        // record previous cluster number
  2715 	        clustNum = aDosEntryPos.iCluster;
  2716 
  2717 	        // this is the 2nd scanning and we have just passed the pos we started.
  2718 	        if (scanAhead && scanCnt == 2)
  2719 	        	{
  2720 	        	if (aDosEntryPos.Cluster() == aLeafDirData.iMRUPos.Cluster()
  2721 	        			&& aDosEntryPos.Pos() >= aLeafDirData.iMRUPos.Pos())
  2722 	        		{
  2723 	        		User::Leave(anError);
  2724 	        		}
  2725 	        	}
  2726 
  2727 
  2728 	        MoveToNextEntryL(aDosEntryPos); //-- goto the next entry in the directory
  2729 
  2730 	        if (IsRootDir(aDosEntryPos)&&(aDosEntryPos.iPos+StartOfRootDirInBytes()>=RootDirEnd()))
  2731 	            {
  2732 	            User::Leave(anError);//Allows maximum number of entries in root directory
  2733 	            }
  2734 
  2735 
  2736 	        if (!scanAhead || scanCnt == 2)
  2737 	        	{
  2738 		        if (aDosEntryPos.iCluster && (aDosEntryPos.iPos <= previousPosition))
  2739 		            DoCheckFatForLoopsL(aDosEntryPos.iCluster,previousCluster,changePreviousCluster,count);
  2740 
  2741 		        previousPosition=aDosEntryPos.iPos;
  2742 	        	}
  2743 	    	}	// FOREVER -- the actual scanning is done inside this loop
  2744 
  2745 
  2746         if (found)
  2747         	{
  2748         	break;
  2749         	}
  2750 
  2751         // if not found:
  2752     	// if we have not found in the first scanning and we are doing scanning ahead,
  2753         //  we need to go back to the starting pos of this dir and scan from start until
  2754         //  we reach lastscannedPos
  2755         if (scanAhead && scanCnt == 1)
  2756         	{
  2757         	aDosEntryPos = TEntryPos(aLeafDirData.iClusterNum, 0);
  2758         	continue;
  2759         	}
  2760         else
  2761         	{
  2762         	// there are only two exits: either found or reached end of dir in the 1st scanning
  2763         	ASSERT(0);
  2764         	break;
  2765         	}
  2766     	} // for (TInt scanCnt = 1; scanCnt <= 2; ++scanCnt)
  2767 
  2768     //---------------------------------------------------
  2769     if (iRawDisk->DirCacheInterface() && aDosEntryPos.Cluster())
  2770     	{
  2771     	TInt64 mruPos = MakeLinAddrL(aDosEntryPos);
  2772         iRawDisk->DirCacheInterface()->MakePageMRU(mruPos);
  2773 
  2774     	//-- if the corresponding leaf directory name is cached, associate the last search positionin this directory.
  2775         //-- the next search in this dir. will start from this position (and will wrap around over the dir. beginning).
  2776         //-- the "last search position" will is the position of current VFAT entryset start. 
  2777     	if(aLeafDirData.iClusterNum)
  2778     		{
  2779             iLeafDirCache->UpdateMRUPos(TLeafDirData(aLeafDirData.iClusterNum, aStartEntryPos));
  2780             }
  2781     	}
  2782 
  2783     return (aStartEntry.IsVFatEntry());
  2784     }
  2785 
  2786 //-----------------------------------------------------------------------------------------
  2787 /**
  2788     Locate an directory entry entry from its full path name.
  2789 
  2790     @param  aName           a name of an object we are looking for
  2791     @param  anAtt           attributes of this object
  2792     @param  anEntry         on return will contain first VFAT dir entry
  2793     @param  anEntryPos      on return in case of VFAT entry will contain start position of the VFAT dir. entry
  2794 
  2795     @leave  can leave with KErrNotFound if the search has reached the end of directory
  2796 */
  2797 void CFatMountCB::FindEntryStartL(const TDesC& aName,TUint anAtt,TFatDirEntry& anEntry,TEntryPos& anEntryPos) const
  2798     {
  2799     __PRINT(_L("CFatMountCB::FindEntryStartL()"));
  2800     TInt namePos=aName.LocateReverse(KPathDelimiter)+1; // There is always a path delimiter
  2801     TFileName fileName;
  2802     TLeafDirData leafDir;
  2803     TEntryPos dosEntryPos(FindLeafDirL(aName.Left(namePos),leafDir),0);
  2804     TFatDirEntry dosEntry;
  2805 
  2806     DoFindL(aName.Mid(namePos),anAtt,anEntryPos,anEntry,dosEntryPos,dosEntry,fileName,KErrNotFound,NULL,leafDir);
  2807     }
  2808 
  2809 
  2810 //-----------------------------------------------------------------------------------------
  2811 
  2812 /**
  2813     Locate an directory entry entry from its full path name.
  2814 
  2815     @param  aName           a name of an object we are looking for
  2816     @param  anAtt           attributes of this object
  2817     @param  anEntry         on return will contain first VFAT dir entry
  2818     @param  anEntryPos      on return in case of VFAT entry will contain start position of the VFAT dir. entry
  2819 
  2820     @leave  can leave with KErrNotFound if the search has reached the end of directory
  2821 */
  2822 void CFatMountCB::FindEntryStartL(const TDesC& aName,TUint anAtt,TFatDirEntry& anEntry,TEntryPos& anEntryPos,XFileCreationHelper* aFileCreationHelper) const
  2823     {
  2824     __PRINT(_L("CFatMountCB::FindEntryStartL()"));
  2825     TInt namePos=aName.LocateReverse(KPathDelimiter)+1; // There is always a path delimiter
  2826     TFileName fileName;
  2827     TLeafDirData leafDir;
  2828     TEntryPos dosEntryPos(FindLeafDirL(aName.Left(namePos),leafDir),0);
  2829     TFatDirEntry dosEntry;
  2830     DoFindL(aName.Mid(namePos),anAtt,anEntryPos,anEntry,dosEntryPos,dosEntry,fileName,KErrNotFound,aFileCreationHelper,leafDir);
  2831     }
  2832 
  2833 //-----------------------------------------------------------------------------------------
  2834 void CFatMountCB::FindDosNameL(const TDesC& aName,TUint anAtt,TEntryPos& aDosEntryPos,TFatDirEntry& aDosEntry,TDes& aFileName,TInt anError) const
  2835 //
  2836 // Scan a directory looking for aName.
  2837 // aCluster and anEntryAddr give the location of the entry.
  2838 //
  2839     {
  2840 
  2841     __PRINT(_L("CFatMountCB::FindDosNameL()"));
  2842     TEntryPos startPos;
  2843     TFatDirEntry startEntry;
  2844 
  2845     TLeafDirData leafDir;			// leaf dir data is zero initialized, no scannig ahead
  2846     DoFindL(aName,anAtt,startPos,startEntry,aDosEntryPos,aDosEntry,aFileName,anError,NULL,leafDir);
  2847     }
  2848 //-----------------------------------------------------------------------------------------
  2849 
  2850 void CFatMountCB::AddDirEntryL(TEntryPos& aPos,TInt aNumOfEntries)
  2851 //
  2852 // Find space for a new directory entry. Leave KErrEof if no space
  2853 //
  2854     {
  2855 
  2856     __PRINT(_L("CFatMountCB::AddDirEntryL"));
  2857     TInt numFound=0;
  2858     TFatDirEntry entry;
  2859     TEntryPos startPos(RootIndicator(),0);
  2860     TInt clusterNum=aPos.iCluster;
  2861     FOREVER
  2862         {
  2863         ReadDirEntryL(aPos,entry);
  2864         if (entry.IsEndOfDirectory())
  2865             break;
  2866         if (!entry.IsErased() && !entry.IsGarbage())
  2867             numFound=0;
  2868         else
  2869             {
  2870             if (numFound==0)
  2871                 startPos=aPos;
  2872             numFound++;
  2873             if (numFound==aNumOfEntries)
  2874                 {
  2875                 aPos=startPos;
  2876                 return;
  2877                 }
  2878             }
  2879         clusterNum=aPos.iCluster;
  2880         MoveToNextEntryL(aPos);
  2881         if (IsRootDir(aPos)&&(StartOfRootDirInBytes()+aPos.iPos==RootDirEnd()))
  2882     //  No end of directory marker at end of root directory
  2883             User::Leave(KErrDirFull);
  2884         }
  2885 
  2886     TUint clusterSize=1<<ClusterSizeLog2();
  2887     if (IsEndOfClusterCh(aPos.iCluster))
  2888         { // End of last cluster in directory
  2889         aPos.iCluster=clusterNum;
  2890         aPos.iPos=clusterSize;
  2891         }
  2892 
  2893     TEntryPos eofPos(aPos.iCluster,aPos.iPos+KSizeOfFatDirEntry*aNumOfEntries);
  2894 
  2895     if (IsRootDir(aPos))
  2896         { // Special case of root directory
  2897         if (eofPos.iPos+StartOfRootDirInBytes()>RootDirEnd())
  2898             User::Leave(KErrDirFull);
  2899         else
  2900             return;
  2901         }
  2902 
  2903     if (eofPos.iPos==clusterSize)
  2904         return; // No need to allocate
  2905     if (eofPos.iPos>clusterSize)
  2906         {
  2907         TInt numNeeded=eofPos.iPos>>ClusterSizeLog2();
  2908         if(IsRuggedFSys())
  2909             {
  2910             ExtendClusterListZeroedL(numNeeded,eofPos.iCluster);
  2911             }
  2912         else
  2913             {
  2914             FAT().ExtendClusterListL(numNeeded,eofPos.iCluster);
  2915             ZeroDirClusterL(eofPos.iCluster);
  2916             }
  2917 
  2918         eofPos.iPos-=numNeeded<<ClusterSizeLog2();
  2919         if(aPos.iPos==clusterSize)
  2920             {
  2921             if (!FAT().GetNextClusterL(aPos.iCluster))
  2922                 {
  2923                 __PRINT(_L("CFatMountCB::AddDirEntryL corrupt#1"))
  2924                 User::Leave(KErrCorrupt);
  2925                 }
  2926             aPos.iPos=0;
  2927             }
  2928         }
  2929     else if(Drive().IsRemovable())
  2930         {
  2931         // check if entry is already zeroed
  2932         ReadDirEntryL(eofPos,entry);
  2933         if(!entry.IsEndOfDirectory())
  2934             {
  2935             // some removable media may not have directory zeroed
  2936             entry.SetEndOfDirectory();
  2937             WriteDirEntryL(eofPos,entry);
  2938             }
  2939         }
  2940     }
  2941 
  2942 /**
  2943     Zero fill a cluster
  2944     @param  aCluster cluster number to zero-fill
  2945 */
  2946 void CFatMountCB::ZeroDirClusterL(TInt aCluster)
  2947     {
  2948 
  2949     __PRINT1(_L("CFatMountCB::ZeroDirClusterL %d"),aCluster);
  2950 
  2951     const TUint32 KClusterSz= 1<<ClusterSizeLog2();
  2952     const TUint32 KMaxBufSz = KClusterSz;           //-- max. nuffer size is a cluster
  2953     const TUint32 KMinBufSz = 1<<SectorSizeLog2();  //-- min. buffer size is 1 sector (for OOM case)
  2954 
  2955     //-- allocate a buffer for zero-filling a cluster
  2956     RBuf8 buf;
  2957     CleanupClosePushL(buf);
  2958 
  2959     if(buf.CreateMax(KMaxBufSz) != KErrNone)
  2960         buf.CreateMaxL(KMinBufSz); //-- OOM, try to create smaller buffer
  2961 
  2962     buf.FillZ();
  2963 
  2964     TEntryPos entryPos(aCluster,0);
  2965 
  2966     //-- write buffer to the beginning of the directory file.
  2967     DirWriteL(entryPos, buf); //-- use special interface to access FAT directory file
  2968 
  2969     //-- fill in the rest of the cluster if we used a small buffer
  2970     if((TUint32)buf.Size() < KClusterSz) //--  KMaxBufSz may == KMinBufSz if we have 1 sector per cluster
  2971     {
  2972         const TInt restCnt = SectorsPerCluster() - 1;
  2973         ASSERT(restCnt >=1);
  2974 
  2975         for(TInt i=0; i<restCnt; ++i)
  2976         {
  2977             entryPos.iPos += KMinBufSz;
  2978             DirWriteL(entryPos, buf); //-- use special interface to access FAT directory file
  2979         }
  2980 
  2981     }
  2982 
  2983     CleanupStack::PopAndDestroy(&buf);
  2984     }
  2985 
  2986 
  2987 /**
  2988     Internal method. Retrieves directory entry from given position.
  2989 
  2990     @param  aPos            on enter shall contain start position, from where the entry will be read. On return contains position of the DOS entry (the last one for object name for the VFAT case)
  2991     @param  aDosEntry       On return contains DOS entry for the VFAT case
  2992     @param  aStartEntry     On return contains start entry of the directory object for the VFAT case
  2993     @param  aLongFileName   On return contains VFAT or long filename
  2994 
  2995     @return  ETrue  if whole FAT entry is OK: only 1 entry for DOS name or _ALL_ entries for a long name
  2996              EFalse if there was an error in assembling entries to the long file name. In this case this entry shall be ignored by upper level.
  2997 
  2998     can leave because of ReadDirEntryL() and MoveToNextEntryL() [end of dir].
  2999 */
  3000 TBool CFatMountCB::DoGetDirEntryL(TEntryPos& aPos, TFatDirEntry& aDosEntry, TFatDirEntry& aStartEntry, TDes& aLongFileName) const
  3001     {
  3002 
  3003 //    __PRINT3(_L("CFatMountCB::GetDirEntryL() drv:%d, pos:%d:%d"), Drive().DriveNumber(), aPos.iCluster, aPos.iPos);
  3004 
  3005     ReadDirEntryL(aPos,aStartEntry);
  3006     aDosEntry=aStartEntry;
  3007     if (!aDosEntry.IsVFatEntry() || aDosEntry.IsErased() || aDosEntry.IsGarbage())
  3008         {//-- This is either a 8.3 FAT entry or garbage
  3009         aLongFileName.SetLength(0);
  3010         return ETrue;
  3011         }
  3012 
  3013     //-- process VFAT entries
  3014 
  3015     if(!aDosEntry.IsLongNameStart())
  3016         return EFalse; //-- wrong counter in the 1st VFat entry, consider it as orphaned
  3017 
  3018 
  3019     TInt count = aDosEntry.NumFollowing(); //-- count of the following VFat entries
  3020 
  3021     TBuf16<KMaxVFatEntryName> vBuf(KMaxVFatEntryName);
  3022     aDosEntry.ReadVFatEntry(vBuf);
  3023 
  3024     TInt vLength=vBuf.Locate('\0');
  3025     if (vLength==KErrNotFound)
  3026         vLength=KMaxVFatEntryName;
  3027 
  3028     vBuf.SetLength(vLength);
  3029 
  3030     const TInt nameLen = vLength+KMaxVFatEntryName*(count-1);
  3031     if(nameLen <= 0 || nameLen > KMaxFileName)
  3032         return EFalse; //--  wrong long file name length, consider VFAT entry as orphaned
  3033 
  3034     aLongFileName.SetLength(nameLen);
  3035 
  3036     const TUint8 entryCheckSum = aDosEntry.CheckSum(); //-- check sum from the 1st VFat entry
  3037 
  3038     while (count--)
  3039         {
  3040         TPtr fileNamePtr(&aLongFileName[0]+KMaxVFatEntryName*count,aLongFileName.Length()-KMaxVFatEntryName*count);
  3041         fileNamePtr.Copy(vBuf);
  3042         if (count==0)
  3043             break; //-- all VFat entries read, only DOS entry remained
  3044 
  3045         MoveToNextEntryL(aPos);
  3046         ReadDirEntryL(aPos,aDosEntry);
  3047 
  3048         //-- check if it is correct VFat entry.
  3049         //-- if not, this is the "orphaned" entry and will be ignored
  3050         if(!aDosEntry.IsVFatEntry() || aDosEntry.IsErased() || entryCheckSum != aDosEntry.CheckSum() || aDosEntry.NumFollowing() != count)
  3051             return EFalse;  //-- bad VFAT entry
  3052 
  3053         aDosEntry.ReadVFatEntry(vBuf);
  3054         }
  3055 
  3056     if (IsRootDir(aPos)&&(aPos.iPos+StartOfRootDirInBytes()==(RootDirEnd()-KSizeOfFatDirEntry)))
  3057         return ETrue;//Allows maximum number of entries in root directory
  3058 
  3059     //-- read the last, DOS FAT entry
  3060     MoveToNextEntryL(aPos);
  3061     ReadDirEntryL(aPos,aDosEntry);
  3062 
  3063     //-- check if it is corect
  3064     if(aDosEntry.IsEndOfDirectory() || aDosEntry.IsErased() || aDosEntry.IsVFatEntry())
  3065         return EFalse; //-- Bad DOS entry
  3066 
  3067     //-- verify ChechSum here if it is incorrect, use DOS name only
  3068     const TUint8 calcNameChkSum = CalculateShortNameCheckSum(aDosEntry.Name());
  3069     if(calcNameChkSum != entryCheckSum)
  3070         {
  3071         aLongFileName.SetLength(0);//-- don't use long filename
  3072         __PRINT2(_L("CFatMountCB::GetDirEntryL() CheckSum mismatch: VFat:0x%x, DOS:0x%d"),entryCheckSum, calcNameChkSum);
  3073         }
  3074 
  3075     return ETrue;
  3076     }
  3077 
  3078 
  3079 /**
  3080     Read a number of VFAT entries from the directory file.
  3081     for parameters see DoGetDirEntryL()
  3082 
  3083     @return KErrNone if everything is OK, system wide error code otherwise
  3084 
  3085 */
  3086 TInt CFatMountCB::GetDirEntry(TEntryPos& aPos,TFatDirEntry& aDosEntry,TFatDirEntry& aStartEntry,TDes& aLongFileName) const
  3087     {
  3088 
  3089     TBool bEntryOK=ETrue;
  3090     TRAPD(nErr, bEntryOK = DoGetDirEntryL(aPos, aDosEntry, aStartEntry, aLongFileName));
  3091 
  3092     if(nErr !=KErrNone)
  3093         return nErr;
  3094 
  3095     if(!bEntryOK)
  3096         {//-- DoGetDirEntryL could not assemble whole VFat entry, probably some parts of it are damaged.
  3097          //-- consider it as an "orphaned" entry and skip
  3098         aDosEntry.iData[0] = 0xFF;      // Mark entry as garbage
  3099         aLongFileName.SetLength(0);     // No long filename
  3100         }
  3101 
  3102     return KErrNone;
  3103     }
  3104 
  3105 void CFatMountCB::MoveToNextEntryL(TEntryPos& aPos) const
  3106 //
  3107 // If anEntry is at the end of the cluster, and we are not the root dir,
  3108 // move it to the next in the list.
  3109 //
  3110     {
  3111 
  3112 //  __PRINT(_L("CFatMountCB::MoveToNextEntryL"));
  3113     if (IsEndOfClusterCh(aPos.iCluster))
  3114         return;
  3115     const TUint temp = 1<<ClusterSizeLog2();
  3116     if (aPos.iPos+KSizeOfFatDirEntry!=temp || IsRootDir(aPos))
  3117         {
  3118         aPos.iPos+=KSizeOfFatDirEntry;
  3119         }
  3120     else
  3121         {
  3122         if (FAT().GetNextClusterL(aPos.iCluster)==EFalse)
  3123             {
  3124             SetEndOfClusterCh(aPos.iCluster);
  3125             }
  3126         aPos.iPos=0;
  3127         }
  3128     }
  3129 
  3130 //-----------------------------------------------------------------------------------------
  3131 
  3132 /**
  3133     Starting from a VFat entry walk down the directory until the associated dos entry is found
  3134 
  3135     @param aPos     in: VFAT entry position. out: if this is a VFAT entry set, it will be DOS entry position. otherwise not changed
  3136     @param anEntry  on return will contain DOS dir. entry contents (if aPos points to the VFAT entry)
  3137 */
  3138 void CFatMountCB::MoveToDosEntryL(TEntryPos& aPos,TFatDirEntry& anEntry) const
  3139     {
  3140 
  3141     //__PRINT(_L("CFatMountCB::MoveToDosEntryL"));
  3142     if (anEntry.IsVFatEntry()==EFalse)
  3143         return;
  3144     FOREVER
  3145         {
  3146         MoveToNextEntryL(aPos);
  3147         ReadDirEntryL(aPos,anEntry);
  3148         if (anEntry.IsVFatEntry()==EFalse)
  3149             break;
  3150         if (IsRootDir(aPos)&&(aPos.iPos+StartOfRootDirInBytes()==(RootDirEnd()-KSizeOfFatDirEntry)))
  3151             break;  //  Allows maximum number of entries in root directory
  3152         }
  3153     }
  3154 
  3155 //-----------------------------------------------------------------------------------------
  3156 
  3157 /** Read the Uid of the entry starting at aCluster */
  3158 void CFatMountCB::ReadUidL(TInt aCluster,TEntry& anEntry) const
  3159     {
  3160 
  3161     __PRINT1(_L("CFatMountCB::ReadUidL(%d)"), aCluster);
  3162 
  3163     if((TUint)aCluster < KFatFirstSearchCluster || (TUint)aCluster >= UsableClusters()+KFatFirstSearchCluster)
  3164         User::Leave(KErrCorrupt);
  3165 
  3166     TBuf8<sizeof(TCheckedUid)> uidBuf;
  3167     iRawDisk->ReadCachedL(FAT().DataPositionInBytes(aCluster),sizeof(TCheckedUid),uidBuf);
  3168     __ASSERT_DEBUG(uidBuf.Length()==sizeof(TCheckedUid),Fault(EFatReadUidFailed));
  3169     TCheckedUid uid(uidBuf);
  3170     anEntry.iType=uid.UidType();
  3171     }
  3172 
  3173 //-----------------------------------------------------------------------------------------
  3174 
  3175 /**
  3176     Read file section without opening this file on a file server side.
  3177 
  3178     @param  aName       file name; all trailing dots from the name will be removed
  3179     @param  aFilePos    start read position within a file
  3180     @param  aLength     how many bytes to read; on return will be how many bytes actually read
  3181     @param  aDes        local buffer desctriptor
  3182     @param  aMessage    from file server, used to write data to the buffer in different address space.
  3183 
  3184     @leave on media read error
  3185 */
  3186 void CFatMountCB::ReadSectionL(const TDesC& aName,TInt aPos,TAny* aTrg,TInt aLength,const RMessagePtr2& aMessage)
  3187     {
  3188     __PRINT4(_L("CFatMountCB::ReadSectionL, drv:%d, pos:%d, len:%d, FN:%S"), DriveNumber(), aPos, aLength, &aName);
  3189 
  3190     CheckStateConsistentL();
  3191 
  3192     TEntryPos dosEntryPos(RootIndicator(),0);
  3193     TFatDirEntry dosEntry;
  3194     TFileName fileName;
  3195 
  3196 
  3197     TInt namePos=RemoveTrailingDots(aName).LocateReverse(KPathDelimiter)+1; // There is always a path delimiter
  3198     TLeafDirData leafDir;
  3199     dosEntryPos.iCluster=FindLeafDirL(RemoveTrailingDots(aName).Left(namePos), leafDir);
  3200     dosEntryPos.iPos=0;
  3201     TEntryPos startPos;
  3202     TFatDirEntry startEntry;
  3203     DoFindL(RemoveTrailingDots(aName).Mid(namePos),KEntryAttMaskSupported,
  3204     		startPos,startEntry,dosEntryPos,dosEntry,
  3205     		fileName,KErrNotFound,
  3206     		NULL,
  3207     		leafDir);
  3208 
  3209 //  Check that reading from aPos for aLength lies within the file
  3210 //  if aPos is within the file, and aLength is too long, read up to EOF
  3211 //  If aPos is beyond the end of the file, return a zero length descriptor
  3212 
  3213 	TUint32 fileSize = dosEntry.Size();
  3214 	if ((TUint)aPos>=fileSize)
  3215         User::Leave(KErrEof);
  3216 
  3217     if ((TUint)(aPos+aLength)>fileSize)
  3218         aLength=fileSize-aPos;
  3219 
  3220     TInt cluster=StartCluster(dosEntry);
  3221 	TInt pos = aPos;
  3222 
  3223     TInt endCluster;
  3224     TInt clusterSize=1<<ClusterSizeLog2();      //  Size of file clusters
  3225 	TInt readTotal = 0;
  3226 
  3227 	// Total number of clusters in file
  3228     TInt maxClusters=((fileSize+clusterSize-1)>>ClusterSizeLog2());
  3229 
  3230 	// Read data
  3231     FOREVER
  3232         {
  3233 		//  Get the maximum number of clusters that can be read contiguously
  3234         TInt clusterListLen=FAT().CountContiguousClustersL(cluster,endCluster,maxClusters);
  3235         __ASSERT_DEBUG(clusterListLen>0,Fault(EReadFileSectionFailed));
  3236 
  3237 		//  If start position within this block, then read some data
  3238         if (pos<(clusterListLen<<ClusterSizeLog2()))
  3239             {
  3240 			//  Read the remaining length or the entire cluster block whichever is smaller
  3241 			TInt readLength = Min(aLength-readTotal,(clusterListLen<<ClusterSizeLog2())-pos);
  3242 			__ASSERT_DEBUG(readLength>0,Fault(EReadFileSectionFailed));
  3243 			TInt64 dataAddress=(FAT().DataPositionInBytes(cluster))+pos;
  3244 			iRawDisk->ReadL(dataAddress,readLength,aTrg,aMessage,readTotal);
  3245 			readTotal += readLength;
  3246 
  3247 			if (readTotal == aLength)
  3248 				return;
  3249 
  3250 			pos += readLength;
  3251 			}
  3252 
  3253 		// Get the next cluster in file
  3254 		pos-=(clusterListLen<<ClusterSizeLog2());
  3255 #if defined(_DEBUG)
  3256 		TBool remainingClusters=
  3257 #endif
  3258 			((CFatMountCB*)this)->FAT().GetNextClusterL(endCluster);
  3259 		__ASSERT_DEBUG(remainingClusters,Fault(EReadFileSectionFailed));
  3260 		cluster=endCluster;
  3261 		}
  3262     }
  3263 
  3264 
  3265 //-----------------------------------------------------------------------------------------
  3266 
  3267 void CFatMountCB::RawReadL(TInt64 aPos,TInt aLength,const TAny* aTrg,TInt anOffset,const RMessagePtr2& aMessage) const
  3268 //
  3269 // Read aLength of data from disk directly to thread relative descriptor
  3270 //
  3271     {
  3272     iRawDisk->ReadL(aPos,aLength,aTrg,aMessage,anOffset);
  3273     }
  3274 
  3275 //-----------------------------------------------------------------------------------------
  3276 
  3277 void CFatMountCB::RawWriteL(TInt64 aPos,TInt aLength,const TAny* aSrc,TInt anOffset,const RMessagePtr2& aMessage)
  3278 //
  3279 // Write aLength of data from thread relative descriptor to disk
  3280 //
  3281     {
  3282     CheckWritableL();
  3283 
  3284 	//-- check if we are trying to write to the FAT directly and wait until FAT scan thread finishes in this case.
  3285     FAT().RequestRawWriteAccess(aPos, aLength);
  3286 
  3287     iRawDisk->WriteL(aPos,aLength,aSrc,aMessage,anOffset);
  3288     //-- Note: FAT directory cache will be invalidated in MountL()
  3289     }
  3290 
  3291 //-----------------------------------------------------------------------------------------
  3292 /**
  3293     This method must be used when writing to the FAT directory file.
  3294     If FAT directory cache is present on this drive, it will be used.
  3295     @param  aPos    entry media position
  3296     @param  aDes    data descriptor
  3297 */
  3298 void CFatMountCB::DirWriteL(const TEntryPos& aPos,const TDesC8& aDes)
  3299     {
  3300         CheckWritableL();
  3301         const TInt64 posAddr=MakeLinAddrL(aPos);
  3302 
  3303         if(!iRawDisk->DirCacheInterface())
  3304             {
  3305             iRawDisk->WriteCachedL(posAddr,aDes);
  3306             }
  3307         else
  3308             {//-- if there is an interface to the FAT directory cache, use it
  3309             iRawDisk->DirCacheInterface()->WriteL(posAddr, aDes);
  3310             }
  3311     }
  3312 
  3313 //-----------------------------------------------------------------------------------------
  3314 
  3315 /**
  3316     This method must be used when reading from the FAT directory file.
  3317     If FAT directory cache is present on this drive, it will be used.
  3318 
  3319     @param  aPos    entry media position
  3320     @param  aLength how many bytes to read
  3321     @param  aDes    input data descriptor
  3322 */
  3323 void CFatMountCB::DirReadL(const TEntryPos& aPos, TInt aLength, TDes8& aDes) const
  3324     {
  3325         const TInt64 posAddr=MakeLinAddrL(aPos);
  3326 
  3327         if(!iRawDisk->DirCacheInterface())
  3328             {
  3329             iRawDisk->ReadCachedL(posAddr,aLength,aDes);
  3330             }
  3331         else
  3332             {//-- if there is an interface to the FAT directory cache, use it
  3333             iRawDisk->DirCacheInterface()->ReadL(posAddr, aLength, aDes);
  3334             }
  3335     }
  3336 
  3337 //-----------------------------------------------------------------------------------------
  3338 
  3339 void CFatMountCB::WriteDirEntryL(const TEntryPos& aPos,const TFatDirEntry& aDirEntry)
  3340 //
  3341 // Write a FAT directory entry to disk.
  3342 // Assumes sufficient space has been created for it by AddDirEntry.
  3343 //
  3344     {
  3345 
  3346     __PRINT(_L("CFatMountCB::WriteDirEntryL"));
  3347 
  3348     //-- use special interface to access FAT directory file
  3349     DirWriteL(aPos,TPtrC8((TUint8*)&aDirEntry,KSizeOfFatDirEntry));
  3350     }
  3351 
  3352 //-----------------------------------------------------------------------------------------
  3353 
  3354 void CFatMountCB::EraseDirEntryL(const TEntryPos& aPos)
  3355 //
  3356 // Mark a dir entry as erased
  3357 //
  3358     {
  3359 
  3360     __PRINT(_L("CFatMountCB::EraseDirEntryL"));
  3361     if(!iLeafDirCache && iLastLeafDir)
  3362         iLastLeafDir->Des().SetLength(0);
  3363 
  3364     //-- use special interface to access FAT directory file
  3365     DirWriteL(aPos,TPtrC8((TUint8*)&KEntryErasedMarker,sizeof(TUint8)));
  3366     }
  3367 
  3368 //-----------------------------------------------------------------------------------------
  3369 
  3370 void CFatMountCB::ReadDirEntryL(const TEntryPos& aPos,TFatDirEntry& aDirEntry) const
  3371 //
  3372 // Read a FAT directory entry to disk
  3373 //
  3374     {
  3375 
  3376 //  __PRINT(_L("CFatMountCB::ReadDirEntryL"));
  3377     if (IsEndOfClusterCh(aPos.iCluster))
  3378         {
  3379         aDirEntry.InitZ();
  3380         return;
  3381         }
  3382     TPtr8 buf=TPtr8((TUint8*)&aDirEntry,KSizeOfFatDirEntry);
  3383 
  3384     //-- use special interface to access FAT directory file
  3385     DirReadL(aPos,KSizeOfFatDirEntry,buf);
  3386     }
  3387 
  3388 //-----------------------------------------------------------------------------------------
  3389 
  3390 /**
  3391     Enlarge the disk's size.
  3392     This method can be called only for variable size media, i.e. RAM drive
  3393 
  3394     @param aSize size increment (bytes)
  3395 */
  3396 void CFatMountCB::EnlargeL(TInt aSize)
  3397     {
  3398     __PRINT2(_L("CFatMountCB::EnlargeL by 0x%x currentsize=0x%x"),aSize,iSize);
  3399 
  3400     ASSERT(iRamDrive);
  3401 
  3402     TInt maxSize;
  3403     if (HAL::Get(HAL::EMaxRAMDriveSize, maxSize) == KErrNone && iSize + aSize > maxSize)
  3404         User::Leave(KErrDiskFull);
  3405     User::LeaveIfError(LocalDrive()->Enlarge(aSize));
  3406     iSize+=aSize;
  3407 
  3408     if (&FAT())
  3409         {
  3410         FAT().InitializeL();
  3411         }
  3412 
  3413     if (&RawDisk())
  3414         {
  3415         RawDisk().InitializeL();
  3416         }
  3417 
  3418     }
  3419 
  3420 //-----------------------------------------------------------------------------------------
  3421 
  3422 void CFatMountCB::ReduceSizeL(TInt aPos,TInt aLength)
  3423 //
  3424 // Reduce the disk's size
  3425 //
  3426     {
  3427 
  3428     __PRINT2(_L("CFatMountCB::ReduceSizeL aPos=0x%x aLength=0x%x"),aPos,aLength);
  3429     User::LeaveIfError(LocalDrive()->ReduceSize(aPos,aLength));
  3430     iSize-=aLength;
  3431     }
  3432 
  3433 //-----------------------------------------------------------------------------------------
  3434 
  3435 TInt64 CFatMountCB::MakeLinAddrL(const TEntryPos& aPos) const
  3436 //
  3437 // Convert cluster/position into linear address
  3438 //
  3439     {
  3440 
  3441     //__PRINT2(_L("CFatMountCB::MakeLinAddrL, cl:%d, pos:%d"), aPos.iCluster, aPos.iPos);
  3442     if (!IsRootDir(aPos))
  3443         {
  3444         TInt relPos=ClusterRelativePos(aPos.iPos);
  3445         return FAT().DataPositionInBytes(aPos.iCluster)+relPos;
  3446         }
  3447     if (aPos.iPos+StartOfRootDirInBytes()>=RootDirEnd())
  3448         User::Leave(KErrDirFull); // Past last root dir entry
  3449     return StartOfRootDirInBytes()+aPos.iPos;
  3450     }
  3451 
  3452 //-----------------------------------------------------------------------------------------
  3453 
  3454 void CFatMountCB::GetShortNameL(const TDesC& aLongName,TDes& aShortName)
  3455 //
  3456 // Get the short name associated with a long file name
  3457 //
  3458     {
  3459     __PRINT(_L("CFatMountCB::GetShortNameL"));
  3460     TEntryPos firstEntryPos(RootIndicator(),0);
  3461     TFatDirEntry firstEntry;
  3462     FindEntryStartL(aLongName,KEntryAttMaskSupported,firstEntry,firstEntryPos);
  3463     MoveToDosEntryL(firstEntryPos,firstEntry);
  3464     TBuf8<0x20> dosName(DosNameFromStdFormat(firstEntry.Name()));
  3465     LocaleUtils::ConvertToUnicodeL(aShortName, dosName);
  3466     }
  3467 
  3468 //-----------------------------------------------------------------------------------------
  3469 
  3470 void CFatMountCB::GetLongNameL(const TDesC& aShortName,TDes& aLongName)
  3471 //
  3472 // Get the long name associated with a short file name
  3473 //
  3474     {
  3475     __PRINT(_L("CFatMountCB::GetLongNameL"));
  3476     TEntryPos pos(RootIndicator(),0);
  3477     TFatDirEntry entry;
  3478     const TInt namePos=aShortName.LocateReverse(KPathDelimiter)+1; // There is always a path delimiter
  3479     const TPtrC shortNameWithoutPathDelimiter(aShortName.Mid(namePos));
  3480     __ASSERT_ALWAYS(shortNameWithoutPathDelimiter.Length()<=12,User::Leave(KErrBadName));
  3481 
  3482     TLeafDirData leafDir;
  3483     pos.iCluster=FindLeafDirL(aShortName.Left(namePos), leafDir);
  3484     FOREVER
  3485         {
  3486         TFatDirEntry startEntry;
  3487         User::LeaveIfError(GetDirEntry(pos,entry,startEntry,aLongName));
  3488         if (entry.IsEndOfDirectory())
  3489             User::Leave(KErrNotFound);
  3490         TBool entryIsVFat=EFalse;
  3491         if (startEntry.IsVFatEntry())
  3492             entryIsVFat=ETrue;
  3493         if (!entry.IsParentDirectory() && !entry.IsCurrentDirectory() && !entry.IsGarbage() && !entry.IsErased())
  3494             {
  3495             TBuf8<0x20> entryName8(DosNameFromStdFormat(entry.Name()));
  3496             TBuf<0x20> entryName;
  3497             LocaleUtils::ConvertToUnicodeL(entryName, entryName8);
  3498             if (shortNameWithoutPathDelimiter.MatchF(entryName)!=KErrNotFound)
  3499                 {
  3500                 if (entryIsVFat==EFalse)
  3501                     aLongName=shortNameWithoutPathDelimiter;
  3502                 return;
  3503                 }
  3504             }
  3505         MoveToNextEntryL(pos);
  3506         }
  3507     }
  3508 
  3509 
  3510 
  3511 //-----------------------------------------------------------------------------------------
  3512 
  3513 /**
  3514     Extend a file or directory, zeroing cluster chain and flushing after every write to FAT.
  3515     This method is called for rugged FAT only.
  3516     for parameters see CFatTable::ExtendClusterListL
  3517 */
  3518 void CFatMountCB::ExtendClusterListZeroedL(TInt aNumber,TInt& aCluster)
  3519     {
  3520     __PRINT(_L("CFatMountCB::ExtendClusterListZeroedL"));
  3521     __ASSERT_DEBUG(aNumber>0,Fault(EFatBadParameter));
  3522 
  3523     while(aNumber && FAT().GetNextClusterL(aCluster))
  3524         aNumber--;
  3525 
  3526     //-- request aNumber free clusters from the FAT, this request may wait until FAT scan thread counted enough free clusters if it is running.
  3527     if(!FAT().RequestFreeClusters(aNumber))
  3528         {
  3529         __PRINT(_L("CFatMountCB::ExtendClusterListL - leaving KErrDirFull"));
  3530         User::Leave(KErrDiskFull);
  3531         }
  3532     while (aNumber--)
  3533         {
  3534         TInt freeCluster=FAT().AllocateSingleClusterL(aCluster);
  3535         FAT().FlushL();
  3536         ZeroDirClusterL(freeCluster);
  3537         FAT().WriteL(aCluster,freeCluster);
  3538         FAT().FlushL();
  3539         aCluster=freeCluster;
  3540         }
  3541     }
  3542 
  3543 //-----------------------------------------------------------------------------------------
  3544 
  3545 #if defined(_DEBUG)
  3546 TInt CFatMountCB::ControlIO(const RMessagePtr2& aMessage,TInt aCommand,TAny* aParam1,TAny* aParam2)
  3547 //
  3548 // Debug function
  3549 //
  3550     {
  3551     if(aCommand>=EExtCustom)
  3552         {
  3553         if(LocalDrive())
  3554             return LocalDrive()->ControlIO(aMessage,aCommand-EExtCustom,aParam1,aParam2);
  3555         else
  3556             return KErrNotSupported;
  3557         }
  3558     switch(aCommand)
  3559         {
  3560         case ECriticalWriteFailOn:
  3561             {
  3562             TInt r;
  3563             TInt16 args[2];
  3564             TPtr8 des((TUint8*)args,4,4);
  3565             TRAP(r,aMessage.ReadL(2,des,0));
  3566             if(r!=KErrNone)
  3567                 return(r);
  3568             SetWriteFail(ETrue);
  3569             SetWriteFailCount(args[0]);
  3570             SetWriteFailError(args[1]);
  3571             break;
  3572             }
  3573         case ECriticalWriteFailOff:SetWriteFail(EFalse);break;
  3574         case ERuggedFSysOn: SetRuggedFSys(ETrue);break;
  3575         case ERuggedFSysOff: SetRuggedFSys(EFalse);break;
  3576         case EIsRuggedFSys:
  3577             {
  3578             TInt r;
  3579             TUint8 val = (IsRuggedFSys()!=0); // val = 0 or 1 for false/true
  3580             TPtr8 pVal(&val,1,1);
  3581             TRAP(r,aMessage.WriteL(2,pVal,0));
  3582             if(r!=KErrNone)
  3583                 return(r);
  3584             break;
  3585             }
  3586 		case ELocalTimeForRemovableMediaOn:
  3587 			{
  3588 			FatFileSystem().SetUseLocalTime(ETrue);
  3589 			break;
  3590 			}
  3591 		case ELocalTimeForRemovableMediaOff:
  3592 			{
  3593 			FatFileSystem().SetUseLocalTime(EFalse);
  3594 			break;
  3595 			}
  3596 		case ELocalTimeUsedOnRemovableMedia:
  3597 			{
  3598 			TBool flag = FatFileSystem().GetUseLocalTime();
  3599 			TPckgC<TBool> flagPckg(flag);
  3600 			TInt r = aMessage.Write(2, flagPckg);
  3601 			if(r!=KErrNone)
  3602 				return r;
  3603 			break;
  3604 			}
  3605 		case ECreationTime:
  3606 			{
  3607 			CheckStateConsistentL();
  3608 
  3609 			TEntryPos firstEntryPos(RootIndicator(),0);
  3610 			TFatDirEntry firstEntry;
  3611 			//RFs::ControlIO restricts you to use narrow descriptors
  3612 			//so convert narrow back to wide.
  3613 			TBuf8<KMaxPath> fileNameNarrow;
  3614 			aMessage.Read(2, fileNameNarrow);
  3615 
  3616 			TFileName fileNameWide;
  3617 			fileNameWide.Copy(fileNameNarrow);
  3618 
  3619 			//find the long file name entry
  3620 			TRAPD(r, FindEntryStartL(fileNameWide,KEntryAttMaskSupported,firstEntry,firstEntryPos) );
  3621 			if(r!=KErrNone)
  3622               return(r);
  3623 			//Find the corresponding 8.3 short name entry, for metadata
  3624 			MoveToDosEntryL(firstEntryPos,firstEntry);
  3625 			TTime creationTime=0;
  3626 			TPckg<TTime> timePckg(creationTime);
  3627 			SFatDirEntry* sEntry = reinterpret_cast<SFatDirEntry*>(firstEntry.iData);
  3628 			creationTime = DosTimeToTTime(sEntry->iTimeC, sEntry->iDateC);
  3629 			r = aMessage.Write(3, timePckg);
  3630 			if(r!=KErrNone)
  3631 				return r;
  3632 			break;
  3633 			}
  3634 		case EDisableFATDirCache:
  3635 			{
  3636 		    MWTCacheInterface* pDirCache = iRawDisk->DirCacheInterface();
  3637 		    TUint32 KEDisableFATDirCache = CDynamicDirCache::EDisableCache;
  3638 		    pDirCache->Control(KEDisableFATDirCache, (TUint32) aParam1, NULL);
  3639 			break;
  3640 			}
  3641 		case EDumpFATDirCache:
  3642 			{
  3643 		    MWTCacheInterface* pDirCache = iRawDisk->DirCacheInterface();
  3644 		    TUint32 KEDumpFATDirCache = CDynamicDirCache::EDumpCache;
  3645 		    pDirCache->Control(KEDumpFATDirCache, 0, NULL);
  3646 			break;
  3647 			}
  3648 		case EFATDirCacheInfo:
  3649 			{
  3650 		    MWTCacheInterface* pDirCache = iRawDisk->DirCacheInterface();
  3651 		    TUint32 KEFATDirCacheInfo = CDynamicDirCache::ECacheInfo;
  3652 		    pDirCache->Control(KEFATDirCacheInfo, 0, NULL);
  3653 			break;
  3654 			}
  3655 
  3656 
  3657         default: return(KErrNotSupported);
  3658         }
  3659     return(KErrNone);
  3660     }
  3661 #else
  3662 TInt CFatMountCB::ControlIO(const RMessagePtr2& /*aMessage*/,TInt /*aCommand*/,TAny* /*aParam1*/,TAny* /*aParam2*/)
  3663     {return(KErrNotSupported);}
  3664 #endif
  3665 
  3666 
  3667 //-----------------------------------------------------------------------------------------
  3668 
  3669 TInt CFatMountCB::Lock(TMediaPassword& aOld,TMediaPassword& aNew,TBool aStore)
  3670 //
  3671 // lock media device
  3672 //
  3673     {
  3674     __PRINT(_L("CFatMountCB::Lock"));
  3675 	TInt r=CreateDrive(Drive().DriveNumber());
  3676     if (r!=KErrNone)
  3677         return r;
  3678 
  3679     TBusLocalDrive* local;
  3680     r=LocalDrive()->GetLocalDrive(local);
  3681     if (r!=KErrNone)
  3682         return r;
  3683 
  3684 #ifdef _LOCKABLE_MEDIA
  3685     if(local->Status()==KErrLocked)
  3686         local->Status() = KErrNotReady;
  3687 #endif
  3688     r=local->SetPassword(aOld,aNew,aStore);
  3689     if(r==KErrNone&&aStore)
  3690         WritePasswordData();
  3691     return(r);
  3692     }
  3693 
  3694 //-----------------------------------------------------------------------------------------
  3695 
  3696 TInt CFatMountCB::Unlock(TMediaPassword& aPassword,TBool aStore)
  3697 //
  3698 // Unlock media device
  3699 //
  3700     {
  3701     __PRINT(_L("CFatMountCB::Unlock"));
  3702 	TInt r=CreateDrive(Drive().DriveNumber());
  3703     if (r!=KErrNone)
  3704         return r;
  3705 
  3706     TBusLocalDrive* local;
  3707     r=LocalDrive()->GetLocalDrive(local);
  3708     if (r!=KErrNone)
  3709         return r;
  3710 
  3711 #ifdef _LOCKABLE_MEDIA
  3712     if(local->Status()==KErrLocked)
  3713         local->Status() = KErrNotReady;
  3714 #endif
  3715     r=local->Unlock(aPassword,aStore);
  3716     if(r==KErrNone&&aStore)
  3717         WritePasswordData();
  3718     return(r);
  3719     }
  3720 
  3721 //-----------------------------------------------------------------------------------------
  3722 
  3723 TInt CFatMountCB::ClearPassword(TMediaPassword& aPassword)
  3724 //
  3725 // Clear password from media device
  3726 //
  3727     {
  3728     __PRINT(_L("CFatMountCB::ClearPassword"));
  3729 	TInt r=CreateDrive(Drive().DriveNumber());
  3730     if (r!=KErrNone)
  3731         return r;
  3732 
  3733     TBusLocalDrive* local;
  3734     r=LocalDrive()->GetLocalDrive(local);
  3735     if (r!=KErrNone)
  3736         return r;
  3737 
  3738 #ifdef _LOCKABLE_MEDIA
  3739     if(local->Status()==KErrLocked)
  3740         local->Status() = KErrNotReady;
  3741 #endif
  3742     r=local->Clear(aPassword);
  3743     if(r==KErrNone)
  3744         WritePasswordData();
  3745     return(r);
  3746     }
  3747 
  3748 //-----------------------------------------------------------------------------------------
  3749 
  3750 TInt CFatMountCB::ErasePassword()
  3751 //
  3752 // Forcibly erase the password from a media device
  3753 //
  3754     {
  3755     __PRINT(_L("CFatMountCB::ErasePassword"));
  3756 
  3757 	TInt r=CreateDrive(Drive().DriveNumber());
  3758     if (r!=KErrNone)
  3759         return r;
  3760 
  3761     TBusLocalDrive* local;
  3762     r=LocalDrive()->GetLocalDrive(local);
  3763     if (r!=KErrNone)
  3764         return r;
  3765 
  3766 #ifdef _LOCKABLE_MEDIA
  3767     if(local->Status()==KErrLocked)
  3768         local->Status() = KErrNotReady;
  3769 #endif
  3770     r=local->ErasePassword();
  3771     if(r==KErrNone)
  3772         {
  3773         // ...media change to ensure a fresh remount the drive
  3774         r = local->ForceRemount(0);
  3775         local->Status() = KErrNotReady;
  3776         WritePasswordData();
  3777         }
  3778     return(r);
  3779     }
  3780 
  3781 //-----------------------------------------------------------------------------------------
  3782 
  3783 TInt CFatMountCB::ForceRemountDrive(const TDesC8* aMountInfo,TInt aMountInfoMessageHandle,TUint aFlags)
  3784 //
  3785 // Force a remount of the drive
  3786 //
  3787     {
  3788     __PRINT(_L("CFatMountCB::ForceRemountDrive"));
  3789 	TInt r=CreateDrive(Drive().DriveNumber());
  3790     if (r==KErrNone)
  3791 		r=LocalDrive()->SetMountInfo(aMountInfo,aMountInfoMessageHandle);
  3792     if (r==KErrNone)
  3793         r=LocalDrive()->ForceRemount(aFlags);
  3794     return(r);
  3795     }
  3796 
  3797 //-----------------------------------------------------------------------------------------
  3798 
  3799 void CFatMountCB::WritePasswordData()
  3800 //
  3801 // Write store password data to disk
  3802 //
  3803     {
  3804     __PRINT(_L("CFatMountCB::WritePasswordData"));
  3805     TBuf<sizeof(KMediaPWrdFile)> mediaPWrdFile(KMediaPWrdFile);
  3806     mediaPWrdFile[0] = (TUint8) RFs::GetSystemDriveChar();
  3807     __PRINT1TEMP(_L("disk file = %S"),mediaPWrdFile);
  3808     TBusLocalDrive& local=GetLocalDrive(Drive().DriveNumber());
  3809     TInt length=local.PasswordStoreLengthInBytes();
  3810     if(length==0)
  3811         {
  3812         WriteToDisk(mediaPWrdFile,_L8(""));
  3813         return;
  3814         }
  3815     HBufC8* hDes=HBufC8::New(length);
  3816     if(hDes==NULL)
  3817         return;
  3818     TPtr8 pDes=hDes->Des();
  3819     TInt r=local.ReadPasswordData(pDes);
  3820     if(r==KErrNone)
  3821         WriteToDisk(mediaPWrdFile,pDes);
  3822     delete hDes;
  3823     }
  3824 
  3825 //-----------------------------------------------------------------------------------------
  3826 
  3827 /**
  3828 Trim trailing spaces of volume label descriptor and adjust its length
  3829 */
  3830 void CFatMountCB::TrimVolumeLabel(TDes8& aLabel) const
  3831     {
  3832     // Locate first '\0'
  3833     TInt nullPos = aLabel.Locate('\0');
  3834     if (nullPos == KErrNotFound)
  3835         nullPos = KVolumeLabelSize;
  3836 
  3837     // Trim trailing spaces
  3838     TInt i;
  3839     for (i=nullPos-1; i>=0; --i)
  3840         if (aLabel[i] != 0x20)
  3841             break;
  3842     aLabel.SetLength(i+1);
  3843     }
  3844 
  3845 //-----------------------------------------------------------------------------------------
  3846 
  3847 /**
  3848 Searches for the volume label file
  3849 
  3850 @param aLabel The name of the volume label file returned upon successful search
  3851 @return KErrNone if it finds the volume label file, otherwise KErrNotFound
  3852 */
  3853 TInt CFatMountCB::ReadVolumeLabelFile(TDes8& aLabel)
  3854     {
  3855     __PRINT(_L("+CFatMountCB::ReadVolumeLabelFile"));
  3856     TEntryPos pos(RootIndicator(),0);
  3857     TFatDirEntry entry;
  3858     TRAPD(r, FindVolumeLabelFileL(aLabel, pos, entry));
  3859     __PRINT1(_L("-CFatMountCB::ReadVolumeLabelFile: %d"),r);
  3860     return r;
  3861     }
  3862 
  3863 //-----------------------------------------------------------------------------------------
  3864 
  3865 /**
  3866 Creates or updates the volume label file with name aNewName
  3867 
  3868 @param aNewName The new name for the volume label file
  3869 */
  3870 void CFatMountCB::WriteVolumeLabelFileL(const TDesC8& aNewName)
  3871     {
  3872     __PRINT1(_L("+CFatMountCB::WriteVolumeLabelFileL: [%S]"), &aNewName);
  3873     TEntryPos pos(RootIndicator(),0);
  3874     TFatDirEntry entry;
  3875 
  3876     TBuf8<KVolumeLabelSize> oldName;
  3877     TRAPD(r, FindVolumeLabelFileL(oldName, pos, entry));
  3878 
  3879     if( KErrNone == r )
  3880         {
  3881         // Found existing volume label file, rename or delete
  3882         if(oldName == aNewName)
  3883             {
  3884             __PRINT(_L("-CFatMountCB::WriteVolumeLabelFileL: found: names match"));
  3885             return;
  3886             }
  3887         else
  3888             {
  3889             if(aNewName.Length() == 0)
  3890                 {
  3891                 // delete the volume label file
  3892                 __PRINT(_L("CFatMountCB::WriteVolumeLabelFileL: found: delete"));
  3893                 EraseDirEntryL(pos, entry);
  3894                 }
  3895             else
  3896                 {
  3897                 __PRINT(_L("CFatMountCB::WriteVolumeLabelFileL: found: replace"));
  3898                 entry.SetName(aNewName);
  3899                 WriteDirEntryL(pos, entry);
  3900                 }
  3901             FAT().FlushL();
  3902             }
  3903         }
  3904     else if( KErrNotFound == r )
  3905         {
  3906         // Not found, need to create if aNewName is not empty
  3907         // Windows allows a volume label file to have the same name as
  3908         // an existing file or directory
  3909         if(aNewName.Length() > 0)
  3910             {
  3911             __PRINT(_L("CFatMountCB::WriteVolumeLabelFileL: not found: create"));
  3912             TEntryPos dirPos(RootIndicator(),0);
  3913             AddDirEntryL(dirPos,1);
  3914             TFatDirEntry fatDirEntry;
  3915             fatDirEntry.SetName(aNewName);
  3916             fatDirEntry.SetAttributes(KEntryAttVolume);
  3917 
  3918             TTime now;
  3919 			now.UniversalTime();
  3920 			fatDirEntry.SetTime(now, TimeOffset() );
  3921             fatDirEntry.SetStartCluster(0);
  3922             fatDirEntry.SetSize(0);
  3923             WriteDirEntryL(dirPos, fatDirEntry);
  3924             FAT().FlushL();
  3925             }
  3926         }
  3927     else
  3928         {
  3929         // Some other error
  3930         User::Leave(r);
  3931         }
  3932     }
  3933 
  3934 
  3935 //-----------------------------------------------------------------------------------------
  3936 
  3937 /**
  3938 Scans the root directory for a volume label file. Leaves with an error if not found
  3939 
  3940 @param aLabel Name of the volume label file upon successful search
  3941 @param aDosEntryPos Pointer to position of the volume label file upon successful search
  3942 @param aDosEntry Contains the entry for the volume label file upon successful search
  3943 */
  3944 void CFatMountCB::FindVolumeLabelFileL(TDes8& aLabel, TEntryPos& aDosEntryPos, TFatDirEntry& aDosEntry)
  3945     {
  3946     __PRINT(_L("+CFatMountCB::FindVolumeLabelFileL"));
  3947 
  3948     if(IsRootDir(aDosEntryPos) && (aDosEntryPos.iPos+StartOfRootDirInBytes()>=RootDirEnd()))
  3949         {
  3950         __PRINT(_L("-CFatMountCB::FindVolumeLabelFileL: abort, exceeds root"));
  3951         User::Leave(KErrNotFound); // Allows maximum number of entries in root directory
  3952         }
  3953 
  3954     TInt previousCluster= aDosEntryPos.iCluster;
  3955     TUint previousPosition= aDosEntryPos.iPos;
  3956     TInt changePreviousCluster=1;
  3957     TInt count=0;
  3958 
  3959     TFatDirEntry startEntry;
  3960     TFileName dummyLongName;
  3961 
  3962     FOREVER
  3963         {
  3964 #ifdef _DEBUG
  3965         const TInt e= GetDirEntry(aDosEntryPos, aDosEntry, startEntry, dummyLongName);
  3966         __PRINT1(_L("CFatMountCB::FindVolumeLabelFileL: GetDir %d"), e);
  3967         User::LeaveIfError(e);
  3968 #else
  3969         User::LeaveIfError(GetDirEntry(aDosEntryPos, aDosEntry, startEntry, dummyLongName));
  3970 #endif
  3971         if(aDosEntry.IsEndOfDirectory())
  3972             {
  3973             __PRINT(_L("-CFatMountCB::FindVolumeLabelFileL: end of dir"));
  3974             User::Leave(KErrNotFound);
  3975             }
  3976         if(IsRootDir(aDosEntryPos) && (aDosEntryPos.iPos+StartOfRootDirInBytes()==(RootDirEnd()-KSizeOfFatDirEntry)))
  3977             {
  3978             if(aDosEntry.IsErased())
  3979                 {
  3980                 __PRINT(_L("-CFatMountCB::FindVolumeLabelFileL: erased end of root"));
  3981                 User::Leave(KErrNotFound); //Allows maximum number of entries in root directory
  3982                 }
  3983             }
  3984         if(!aDosEntry.IsCurrentDirectory() && !aDosEntry.IsParentDirectory() && !aDosEntry.IsErased() && !aDosEntry.IsGarbage())
  3985             {
  3986             if(aDosEntry.Attributes() & KEntryAttVolume)
  3987                 {
  3988                 aLabel = aDosEntry.Name();
  3989 #ifdef _DEBUG
  3990                 dummyLongName.Copy(aLabel);
  3991                 __PRINT1(_L("-CFatMountCB::FindVolumeLabelFileL: found [%S]"), &dummyLongName);
  3992 #endif
  3993                 break;
  3994                 }
  3995             }
  3996         MoveToNextEntryL(aDosEntryPos);
  3997         if(IsRootDir(aDosEntryPos) && (aDosEntryPos.iPos+StartOfRootDirInBytes()>=RootDirEnd()))
  3998             {
  3999             __PRINT(_L("-CFatMountCB::FindVolumeLabelFileL: passed end of root"));
  4000             User::Leave(KErrNotFound); //Allows maximum number of entries in root directory
  4001             }
  4002         if(aDosEntryPos.iCluster && (aDosEntryPos.iPos <= previousPosition))
  4003             {
  4004             DoCheckFatForLoopsL(aDosEntryPos.iCluster, previousCluster, changePreviousCluster, count);
  4005             }
  4006         previousPosition=aDosEntryPos.iPos;
  4007         }
  4008     }
  4009 
  4010 //-----------------------------------------------------------------------------------------
  4011 
  4012 /**
  4013 Read volume label from disk and trim trailing 0x20 & 0x00 characters
  4014 */
  4015 void CFatMountCB::GetVolumeLabelFromDiskL(const TFatBootSector& aBootSector)
  4016     {
  4017     // Read volume label as 8 bit descriptor
  4018     TBuf8<KVolumeLabelSize> volName8;
  4019     TInt r = ReadVolumeLabelFile(volName8);
  4020     if(r != KErrNone)   // No volume label file in root directory
  4021         volName8 = aBootSector.VolumeLabel();
  4022     TrimVolumeLabel(volName8);
  4023 
  4024     TBuf16<KVolumeLabelSize> volName16;
  4025     LocaleUtils::ConvertToUnicodeL(volName16, volName8);
  4026     SetVolumeName(volName16.AllocL());
  4027     }
  4028 
  4029 
  4030 //-----------------------------------------------------------------------------------------
  4031 
  4032 /**
  4033 Populates iMap member of aInfo with contiguous block group maps.
  4034 
  4035 @param aPos     Start position for a desired section of the file.
  4036 @param sLength  Length of the desired data to produce the block map for.
  4037 @param aInfo    A structure describing a group of block maps.
  4038 */
  4039 void CFatMountCB::BlockMapReadFromClusterListL(TEntryPos& aPos, TInt aLength, SBlockMapInfo& aInfo)
  4040     {
  4041     __PRINT(_L("CFatMountCB::BlockMapReadFromClusterListL"));
  4042     __ASSERT_ALWAYS(aPos.Cluster()>=KFatFirstSearchCluster,User::Leave(KErrCorrupt));
  4043     TBlockMapEntry blockMapEntry;
  4044 
  4045     TUint i = 0;
  4046     TInt clusterRelativePos;
  4047     TInt maxClusters;
  4048     TInt endCluster;
  4049     TInt clusterListLen;
  4050     TInt readLength;
  4051     TInt temp;
  4052     TInt currentPos;
  4053     TLocalDriveCapsBuf caps;
  4054     TInt r;
  4055     TInt64 realPosition = 0;
  4056 
  4057     do
  4058         {
  4059         currentPos = aPos.iPos;
  4060         temp = currentPos>>ClusterSizeLog2();
  4061         if ( (currentPos) && ( (currentPos) == (temp<<ClusterSizeLog2()) ) )
  4062             {
  4063             if (!FAT().GetNextClusterL(aPos.iCluster))
  4064                 {
  4065 				__PRINT(_L("CFatMountCB::BlockMapReadFromClusterListL corrupt#1"))
  4066                 User::Leave(KErrCorrupt);
  4067                 }
  4068             }
  4069         clusterRelativePos = ClusterRelativePos( aPos.iPos );
  4070         maxClusters = ((aLength + clusterRelativePos - 1)>>ClusterSizeLog2())+1;
  4071         clusterListLen = FAT().CountContiguousClustersL(aPos.iCluster, endCluster, maxClusters);
  4072         readLength = Min( aLength, (clusterListLen<<ClusterSizeLog2()) - clusterRelativePos);
  4073 
  4074         blockMapEntry.SetNumberOfBlocks( clusterListLen );
  4075         if (aPos.iCluster < 2)
  4076             User::Leave(KErrCorrupt);
  4077         r = LocalDrive()->Caps(caps);
  4078         if ( r != KErrNone )
  4079             User::LeaveIfError(r);
  4080         if ( caps().iType&EMediaRam )
  4081             {
  4082             realPosition = FAT().DataPositionInBytes( aPos.iCluster );
  4083             aPos.iCluster = I64LOW((realPosition - aInfo.iStartBlockAddress)>>ClusterSizeLog2());
  4084             blockMapEntry.SetStartBlock( aPos.iCluster );
  4085             }
  4086         else
  4087             blockMapEntry.SetStartBlock( aPos.iCluster - 2);
  4088         aInfo.iMap.Append(TPckgC<TBlockMapEntry>(blockMapEntry));
  4089         aPos.iPos += readLength;
  4090         aPos.iCluster = endCluster;
  4091         aLength -= readLength;
  4092         }
  4093     while( ( aLength > 0 ) && ( ++i < KMaxMapsPerCall ) );
  4094     }
  4095 
  4096 
  4097 //-----------------------------------------------------------------------------------------
  4098 
  4099 TInt CFatMountCB::GetDosEntryFromNameL(const TDesC& aName, TEntryPos& aDosEntryPos, TFatDirEntry& aDosEntry)
  4100     {
  4101     TFatDirEntry firstEntry;
  4102     TEntryPos firstEntryPos(RootIndicator(),0); // Already checked entry is a directory
  4103     FindEntryStartL(aName,KEntryAttMaskSupported,firstEntry,firstEntryPos);
  4104 
  4105     aDosEntryPos=firstEntryPos;
  4106     aDosEntry=firstEntry;
  4107     MoveToDosEntryL(aDosEntryPos,aDosEntry);
  4108 
  4109     return KErrNone;
  4110     }
  4111 
  4112 //-----------------------------------------------------------------------------------------
  4113 
  4114 TInt CFatMountCB::GetFileUniqueId(const TDesC& aName, TInt64& aUniqueId)
  4115     {
  4116     // Get first cluster of file
  4117     TEntryPos dosEntryPos(RootIndicator(),0);
  4118     TFatDirEntry dosEntry;
  4119     InitializeRootEntry(dosEntry);  // Nugatory initialisation to placate warnings
  4120     TRAPD(err,GetDosEntryFromNameL(aName,dosEntryPos,dosEntry));
  4121     if(err!=KErrNone)
  4122         return err;
  4123 
  4124     TInt startCluster=StartCluster(dosEntry);
  4125     // Empty files will return a cluster of zero
  4126     if(startCluster==0)
  4127         return KErrEof;
  4128 
  4129     aUniqueId=MAKE_TINT64(0,startCluster);
  4130     return KErrNone;
  4131     }
  4132 //-----------------------------------------------------------------------------------------
  4133 
  4134 
  4135 TInt CFatMountCB::Spare3(TInt /*aVal*/, TAny* /*aPtr1*/, TAny* /*aPtr2*/)
  4136     {
  4137     return KErrNotSupported;
  4138     }
  4139 
  4140 TInt CFatMountCB::Spare2(TInt /*aVal*/, TAny* /*aPtr1*/, TAny* /*aPtr2*/)
  4141     {
  4142     return KErrNotSupported;
  4143     }
  4144 
  4145 TInt CFatMountCB::Spare1(TInt /*aVal*/, TAny* /*aPtr1*/, TAny* /*aPtr2*/)
  4146     {
  4147     return KErrNotSupported;
  4148     }
  4149 
  4150 //-----------------------------------------------------------------------------------------
  4151 
  4152 /** 
  4153     Check file system for errors. 
  4154     @return KErrNone if no errors found, otherwise a error code hopefully describing the problem found.
  4155 */
  4156 TInt CFatMountCB::CheckDisk()
  4157 	{
  4158 
  4159     __PRINT1(_L("CFatMountCB::CheckDisk() drv:%d"), DriveNumber());
  4160 
  4161     if(!ConsistentState())
  4162         return KErrCorrupt;
  4163 
  4164     //-- create a bit representation of the FAT
  4165     const TUint32 MaxClusters = UsableClusters()+KFatFirstSearchCluster; //-- UsableClusters() doesn't count first 2 unused clusers
  4166     if (MaxClusters == 0)
  4167         return KErrCorrupt;
  4168 
  4169     //-- used for measuring time
  4170     TTime   timeStart;
  4171     TTime   timeEnd;
  4172     timeStart.UniversalTime(); //-- take start time
  4173 
  4174     TInt nRes;
  4175  
  4176     CScanDrive* pScnDrv = NULL;
  4177     TRAP(nRes, pScnDrv=CScanDrive::NewL(this));
  4178     if(nRes != KErrNone)
  4179         return nRes;
  4180 
  4181     //-- start ScanDrive in "checkdisk" mode
  4182     TRAPD(nScnDrvRes, pScnDrv->StartL(CScanDrive::ECheckDisk));
  4183     
  4184     timeEnd.UniversalTime(); //-- take end time
  4185     const TInt msScanTime = (TInt)( (timeEnd.MicroSecondsFrom(timeStart)).Int64() / K1mSec);
  4186     (void)msScanTime;
  4187     __PRINT1(_L("#@@@ CheckDisk() time taken:%d ms"), msScanTime);
  4188  
  4189     CScanDrive::TGenericError chkDskRes = pScnDrv->ProblemsDiscovered();
  4190     const TBool bProblemsFound = (nScnDrvRes!=KErrNone) || pScnDrv->ProblemsDiscovered();
  4191     
  4192     if(bProblemsFound && chkDskRes == CScanDrive::ENoErrors)
  4193         {//-- ScanDrive in this mode can leave unexpectedly without setting an error code that is returned by ProblemsDiscovered();
  4194          //-- leave itself means a problem
  4195         chkDskRes = CScanDrive::EUnknownError;
  4196         }
  4197 
  4198     delete pScnDrv;
  4199 
  4200     if(chkDskRes != KErrNone)
  4201         {
  4202         __PRINT2(_L("CFatMountCB::CheckDisk() drv:%d, result:%d"), DriveNumber(), chkDskRes);
  4203         }
  4204     
  4205     return chkDskRes;
  4206 
  4207     }
  4208 
  4209 
  4210 //-------------------------------------------------------------------------------------------------------------------
  4211 
  4212 /**
  4213     Creates a scan drive object and starts the scan.
  4214 */
  4215 TInt CFatMountCB::DoRunScanDrive()
  4216 {
  4217     TInt nRes;
  4218 
  4219     CScanDrive* pScnDrv = NULL;
  4220     TRAP(nRes, pScnDrv=CScanDrive::NewL(this));
  4221     if(nRes != KErrNone)
  4222         return nRes;
  4223 
  4224     TRAPD(nScnDrvRes, pScnDrv->StartL(CScanDrive::EScanAndFix));
  4225 
  4226     const TBool bNeedFatRemount = (nScnDrvRes!=KErrNone) || pScnDrv->ProblemsDiscovered();
  4227     delete pScnDrv;
  4228 
  4229 
  4230     if(bNeedFatRemount)
  4231         {//-- ScanDrive found and probably fixed some errors.
  4232         // ensure cached fat and free cluster count are updated
  4233         DoDismount(); //-- dismount
  4234         TRAP(nRes, MountL(EFalse)); //-- mount again
  4235         }
  4236 
  4237     if(nScnDrvRes != KErrNone)
  4238         return nScnDrvRes;
  4239 
  4240 
  4241     //-- if ScanDrive hasn't found anything wrong or has fixed recoverable errors, mark the volume clean
  4242     if(VolCleanFlagSupported())
  4243         {
  4244         //-- if there is a background FAT scanning thread, we need to wait until it finishes its work.
  4245         //-- otherwise it's possible to have incorrect amount of free space on the volume until next remounting.
  4246         (void)FAT().NumberOfFreeClusters(ETrue);
  4247         TRAP(nRes, FinaliseMountL());
  4248         ASSERT(nRes == KErrNone);
  4249         }
  4250 
  4251     return KErrNone;
  4252 }
  4253 
  4254 //-------------------------------------------------------------------------------------------------------------------
  4255 
  4256 /**
  4257     Run scan drive on the given volume.
  4258     The ScanDrive may be skipped on the finalised volumes, i.e. those, that had been shut down properly.
  4259 
  4260 
  4261     @return Either  KErrCorrupt if an error was found that is not caused by write failure due to power removal.
  4262                     KErrNone if no error was found. One of four positive codes explaining what type of error was rectified
  4263 */
  4264 TInt CFatMountCB::ScanDrive()
  4265 {
  4266     __PRINT1(_L("CFatMountCB::ScanDrive() starting on drive %d"), DriveNumber());
  4267 
  4268     if(!ConsistentState())
  4269         return KErrCorrupt;
  4270 
  4271     TInt nRes;
  4272 
  4273     if(LockStatus()!=0)
  4274         {
  4275 		__PRINT(_L("CFatMountCB::ScanDrive() locked!\n"));
  4276         return KErrInUse;
  4277         }
  4278 
  4279     if(iRamDrive)
  4280         {//-- Do not check internal RAM drive
  4281         __PRINT(_L("CFatMountCB::ScanDrive() Skipping Internal RAM drive."));
  4282         return KErrNone;
  4283         }
  4284 
  4285     //-- check if the volume is finalised and skip running ScanDrive if this option is enabled in estart.txt
  4286     if(VolCleanFlagSupported() && FatConfig().ScanDrvSkipFinalisedVolume())
  4287         {
  4288         TBool bVolClean = EFalse;
  4289         TRAP(nRes, bVolClean = VolumeCleanL());
  4290 
  4291         if(nRes == KErrNone && bVolClean)
  4292             {
  4293             __PRINT(_L("Skipping ScanDrive on finalised volume!"));
  4294             return KErrNone; //-- skip ScanDrive on a clean volume
  4295             }
  4296         }
  4297 
  4298     //-- run ScanDrive
  4299     nRes = Open();
  4300     if(nRes != KErrNone)
  4301         return nRes;
  4302 
  4303     nRes = DoRunScanDrive();
  4304 
  4305     Close();
  4306 
  4307     __PRINT2(_L("~ CFatMountCB::ScanDrive() finished for drive %d with the code %d"),DriveNumber(), nRes);
  4308 
  4309     return nRes;
  4310 
  4311 }
  4312 
  4313 //-----------------------------------------------------------------------------------------
  4314 /**
  4315 Returns the offset between UTC time and timestamps on the filesystem. This will return User::UTCOffset
  4316 if the flag iUseLocalTime has been set in CFatFileSystem and this mount is on a removable drive. If not
  4317 a null offset is returned.
  4318 
  4319 @return The offset in seconds that timestamps on the filesystem have, relative to UTC.
  4320 */
  4321 TTimeIntervalSeconds CFatMountCB::TimeOffset() const
  4322 	{
  4323     if((Drive().Att() & KDriveAttRemovable) && FatFileSystem().GetUseLocalTime() )
  4324 	    {
  4325         return User::UTCOffset();
  4326         }
  4327 	else
  4328         {
  4329         return TTimeIntervalSeconds(0);
  4330         }
  4331 	}
  4332 
  4333 
  4334 
  4335 
  4336 //-----------------------------------------------------------------------------------------
  4337 /** 
  4338     Check is this file system can be mounted on the drive at all.
  4339     Just read and validate boot region, no real mounting overhead. 
  4340     
  4341     @return KErrNone    boot region is OK, the file system can be mounted.
  4342             KErrLocked  the media is locked on a physical level.
  4343             other error codes depending on the implementation
  4344 
  4345 */
  4346 TInt CFatMountCB::MntCtl_DoCheckFileSystemMountable()
  4347     {
  4348     TInt nRes;
  4349     
  4350     const TInt driveNo = Drive().DriveNumber();
  4351     __PRINT1(_L("CFatMountCB::MntCtl_DoCheckFileSystemMountable() drv:%d"),driveNo);
  4352 
  4353     nRes = CreateDrive(driveNo);
  4354     if(nRes != KErrNone)
  4355         {
  4356         __PRINT1(_L(" ..CreateDrive() err:%d \n"), nRes);    
  4357         return nRes;
  4358         }
  4359 
  4360     //-- try reading boot sector. This doesn't require iDriverInterface setup, it uses LocalDrive()
  4361     TFatBootSector bootSector;
  4362     nRes = ReadBootSector(bootSector);
  4363 
  4364     DismountedLocalDrive();
  4365 
  4366     return nRes;
  4367     }
  4368 
  4369 //-----------------------------------------------------------------------------------------
  4370 /** 
  4371     Internal helper method.
  4372     @param      aFatType FAT type
  4373     @return     End Of Cluster Chain code that depend on FAT type, 0xff8 for FAT12, 0xfff8 for FAT16, and 0xffffff8 for FAT32 
  4374 */
  4375 TUint32 EocCodeByFatType(TFatType aFatType)
  4376     {
  4377     switch(aFatType)
  4378         {
  4379         case EFat32: 
  4380         return EOF_32Bit-7; //-- 0xffffff8
  4381         
  4382         case EFat16: 
  4383         return  EOF_16Bit-7; //-- 0xfff8
  4384         
  4385         case EFat12: 
  4386         return  EOF_12Bit-7; //-- 0xff8
  4387         
  4388         default: 
  4389         ASSERT(aFatType == EInvalid); 
  4390         return 0;
  4391         }
  4392 
  4393     }
  4394 
  4395 //-----------------------------------------------------------------------------------------
  4396 /**
  4397     Set FAT type that this object of CFatMountCB will be dealing with.
  4398 */
  4399 void CFatMountCB::SetFatType(TFatType aFatType)
  4400     {
  4401     ASSERT(State() == ENotMounted || State() == EDismounted || State() == EMounting) ;
  4402     
  4403     iFatType = aFatType;
  4404     iFatEocCode = EocCodeByFatType(aFatType);
  4405     }
  4406 
  4407 
  4408 
  4409 
  4410 
  4411 
  4412 
  4413 
  4414 
  4415 
  4416 
  4417 
  4418 
  4419 
  4420 
  4421 
  4422