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