os/kernelhwsrv/userlibandfileserver/fileserver/sfat/fat_table.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 // f32\sfat\fat_table.cpp
    15 // FAT12/16 File Allocation Table classes implementation
    16 // 
    17 //
    18 
    19 /**
    20  @file
    21  @internalTechnology
    22 */
    23 
    24 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    25 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    26 //!!
    27 //!! WARNING!! DO NOT edit this file !! '\sfat' component is obsolete and is not being used. '\sfat32'replaces it
    28 //!!
    29 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    30 //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    31 
    32 
    33 #include "sl_std.h"
    34 #include "sl_fatcache.h"
    35 #include "fat_table.h"
    36 
    37 
    38 //#######################################################################################################################################
    39 //#     CFatTable class implementation 
    40 //#######################################################################################################################################
    41 
    42 /**
    43     FAT object factory method.
    44     Constructs either CAtaFatTable or CRamFatTable depending on the media type parameter
    45 
    46     @param aOwner Pointer to the owning mount
    47     @param aLocDrvCaps local drive attributes
    48     @leave KErrNoMemory
    49     @return Pointer to the Fat table
    50 */
    51 CFatTable* CFatTable::NewL(CFatMountCB& aOwner, const TLocalDriveCaps& aLocDrvCaps)
    52     {
    53     CFatTable* pFatTable=NULL;
    54 
    55     
    56     switch(aLocDrvCaps.iType)
    57         {
    58         case EMediaRam:
    59             {//-- this is RAM media, try to create CRamFatTable instance.
    60             const TFatType fatType = aOwner.FatType();
    61             
    62             if(fatType != EFat16 )
    63                 {//-- CRamFatTable doesn't support FAT12; FAT16 only.
    64                 __PRINT1(_L("CFatTable::NewL() CRamFatTable doesn't support this FAT type:%d"), fatType);
    65                 ASSERT(0);
    66                 return NULL;
    67                 }
    68             
    69                 pFatTable = CRamFatTable::NewL(aOwner);            
    70             }
    71         break;
    72 
    73         default:
    74             //-- other media
    75             pFatTable = CAtaFatTable::NewL(aOwner);
    76         break;
    77         };
    78 
    79     return pFatTable;
    80     }
    81 
    82 CFatTable::CFatTable(CFatMountCB& aOwner)
    83 {
    84     iOwner = &aOwner;
    85     ASSERT(iOwner);
    86 }
    87 
    88 CFatTable::~CFatTable()
    89 {
    90     //-- destroy cache ignoring dirty data in cache
    91     //-- the destructor isn't an appropriate place to flush the data.
    92     Dismount(ETrue); 
    93 }
    94 
    95 //-----------------------------------------------------------------------------
    96 
    97 /**
    98     Initialise the object, get data from the owning CFatMountCB
    99 */
   100 void CFatTable::InitializeL()
   101     {
   102     ASSERT(iOwner);
   103 
   104     //-- get FAT type from the owner
   105     iFatType = iOwner->FatType();
   106     ASSERT(IsFat12() || IsFat16());
   107 
   108     iFreeClusterHint = KFatFirstSearchCluster;
   109 
   110     //-- cache the media attributes
   111     TLocalDriveCapsV2 caps;
   112     TPckg<TLocalDriveCapsV2> capsPckg(caps);
   113     User::LeaveIfError(iOwner->LocalDrive()->Caps(capsPckg));
   114     iMediaAtt = caps.iMediaAtt;
   115     
   116     //-- obtain maximal number of entries in the table
   117     iMaxEntries = iOwner->UsableClusters()+KFatFirstSearchCluster; //-- FAT[0] & FAT[1] are not in use
   118 
   119     __PRINT3(_L("CFatTable::InitializeL(), drv:%d, iMediaAtt = %08X, max Entries:%d"), iOwner->DriveNumber(), iMediaAtt, iMaxEntries);
   120     }
   121 
   122 //-----------------------------------------------------------------------------
   123 
   124 /** 
   125     Decrements the free cluster count.
   126     Note that can be quite expensive operation (especially for overrides with synchronisation), if it is called for every 
   127     cluster of a large file. Use more than one cluster granularity.
   128      
   129     @param  aCount a number of clusters 
   130 */
   131 void CFatTable::DecrementFreeClusterCount(TUint32 aCount)
   132 {
   133     __ASSERT_DEBUG(iFreeClusters >= aCount, Fault(EFatCorrupt));
   134     iFreeClusters -= aCount;
   135 }
   136 
   137 /** 
   138     Increments the free cluster count.
   139     Note that can be quite expensive operation (especially for overrides with synchronisation), if it is called for every 
   140     cluster of a large file. Use more than one cluster granularity.
   141 
   142     @param  aCount a number of clusters 
   143 */
   144 void CFatTable::IncrementFreeClusterCount(TUint32 aCount)
   145 {
   146     const TUint32 newVal = iFreeClusters+aCount;
   147     __ASSERT_DEBUG(newVal<=MaxEntries(), Fault(EFatCorrupt));
   148     
   149     iFreeClusters = newVal;
   150 }
   151 
   152 /** @return number of free clusters in the FAT */
   153 TUint32 CFatTable::NumberOfFreeClusters(TBool /*aSyncOperation=EFalse*/) const
   154 {
   155     return FreeClusters();
   156 }
   157 
   158 void CFatTable::SetFreeClusters(TUint32 aFreeClusters)
   159 {   
   160     iFreeClusters=aFreeClusters;
   161 }
   162 
   163 /**
   164     Get the hint about the last known free cluster number.
   165     Note that can be quite expensive operation (especially for overrides with synchronisation), if it is called for every 
   166     cluster of a large file.
   167 
   168     @return cluster number supposedly close to the free one.
   169 */
   170 TUint32 CFatTable::FreeClusterHint() const 
   171 {
   172     ASSERT(ClusterNumberValid(iFreeClusterHint));
   173     return iFreeClusterHint;
   174 } 
   175 
   176 /**
   177     Set a free cluster hint. The next search fro the free cluster can start from this value.
   178     aCluster doesn't have to be a precise number of free FAT entry; it just needs to be as close as possible to the 
   179     free entries chain.
   180     Note that can be quite expensive operation (especially for overrides with synchronisation), if it is called for every 
   181     cluster of a large file.
   182 
   183     @param aCluster cluster number hint.
   184 */
   185 void CFatTable::SetFreeClusterHint(TUint32 aCluster) 
   186 {
   187     ASSERT(ClusterNumberValid(aCluster));
   188     iFreeClusterHint=aCluster;
   189 } 
   190 
   191 //-----------------------------------------------------------------------------
   192 
   193 /**
   194     Find out the number of free clusters on the volume.
   195     Reads whole FAT and counts free clusters.
   196 */
   197 void CFatTable::CountFreeClustersL()
   198 {
   199     __PRINT1(_L("#- CFatTable::CountFreeClustersL(), drv:%d"), iOwner->DriveNumber());
   200 
   201     const TUint32 KUsableClusters = iOwner->UsableClusters();
   202     (void)KUsableClusters;
   203     
   204     TUint32 freeClusters = 0;
   205     TUint32 firstFreeCluster = 0;
   206 
   207     TTime   timeStart;
   208     TTime   timeEnd;
   209     timeStart.UniversalTime(); //-- take start time
   210 
   211     //-- walk through whole FAT table looking for free clusters
   212     for(TUint i=KFatFirstSearchCluster; i<MaxEntries(); ++i)
   213     {
   214         if(ReadL(i) == KSpareCluster)
   215         {//-- found a free cluster
   216             ++freeClusters;
   217             
   218             if(!firstFreeCluster)
   219                 firstFreeCluster = i;
   220         }
   221     }
   222 
   223     timeEnd.UniversalTime(); //-- take end time
   224     const TInt msScanTime = (TInt)( (timeEnd.MicroSecondsFrom(timeStart)).Int64() / K1mSec);
   225     __PRINT1(_L("#- CFatTable::CountFreeClustersL() finished. Taken:%d ms"), msScanTime);
   226     (void)msScanTime;
   227 
   228     if(!firstFreeCluster) //-- haven't found free clusters on the volume
   229         firstFreeCluster = KFatFirstSearchCluster;
   230 
   231     ASSERT(freeClusters <= KUsableClusters);
   232 
   233     SetFreeClusters(freeClusters);
   234     SetFreeClusterHint(firstFreeCluster);
   235 }
   236 
   237 //-----------------------------------------------------------------------------
   238 
   239 /**
   240 Count the number of contiguous cluster from a start cluster
   241 
   242 @param aStartCluster cluster to start counting from
   243 @param anEndCluster contains the end cluster number upon return
   244 @param aMaxCount Maximum cluster required
   245 @leave System wide error values
   246 @return Number of contiguous clusters from aStartCluster.
   247 */
   248 TInt CFatTable::CountContiguousClustersL(TUint32 aStartCluster,TInt& anEndCluster,TUint32 aMaxCount) const
   249     {
   250     __PRINT2(_L("CFatTable::CountContiguousClustersL() start:%d, max:%d"),aStartCluster, aMaxCount);
   251     TUint32 clusterListLen=1;
   252     TInt endCluster=aStartCluster;
   253     TInt64 endClusterPos=DataPositionInBytes(endCluster);
   254     while (clusterListLen<aMaxCount)
   255         {
   256         TInt oldCluster=endCluster;
   257         TInt64 oldClusterPos=endClusterPos;
   258         if (GetNextClusterL(endCluster)==EFalse || (endClusterPos=DataPositionInBytes(endCluster))!=(oldClusterPos+(1<<iOwner->ClusterSizeLog2())))
   259             {
   260             endCluster=oldCluster;
   261             break;
   262             }
   263         clusterListLen++;
   264         }
   265     anEndCluster=endCluster;
   266     return(clusterListLen);
   267     }   
   268 
   269 //-----------------------------------------------------------------------------
   270 
   271 /**
   272     Extend a file or directory cluster chain, leaves if there are no free clusters (the disk is full).
   273 
   274     @param aNumber  amount of clusters to allocate
   275     @param aCluster FAT entry index to start with.
   276 
   277     @leave KErrDiskFull + system wide error codes
   278 */
   279 void CFatTable::ExtendClusterListL(TUint32 aNumber,TInt& aCluster)
   280     {
   281     __PRINT2(_L("CFatTable::ExtendClusterListL() num:%d, clust:%d"), aNumber, aCluster);
   282     __ASSERT_DEBUG(aNumber>0,Fault(EFatBadParameter));
   283     
   284     while(aNumber && GetNextClusterL(aCluster))
   285         aNumber--;
   286 
   287     if(!aNumber)
   288         return;
   289 
   290     if (iFreeClusters<aNumber)
   291         {
   292         __PRINT(_L("CFatTable::ExtendClusterListL - leaving KErrDirFull"));
   293         User::Leave(KErrDiskFull);
   294         }
   295 
   296 
   297     TUint32 freeCluster = 0;
   298     
   299     //-- note: this can be impoved by trying to fing as long chain of free clusters as possible in FindClosestFreeClusterL()
   300     for(TUint i=0; i<aNumber; ++i)
   301         {
   302         freeCluster = FindClosestFreeClusterL(aCluster);
   303         WriteFatEntryEofL(freeCluster); //  Must write EOF for FindClosestFreeCluster to work again
   304         WriteL(aCluster,freeCluster);
   305         aCluster=freeCluster;
   306         }
   307     
   308     //-- decrement number of available clusters
   309     DecrementFreeClusterCount(aNumber);
   310 
   311     //-- update free cluster hint, it isn't required to be a precise value, just a hint where to start the from from
   312     SetFreeClusterHint(aCluster); 
   313     
   314     }
   315 
   316 //-----------------------------------------------------------------------------
   317 
   318 /**
   319 Allocate and mark as EOF a single cluster as close as possible to aNearestCluster
   320 
   321 @param aNearestCluster Cluster the new cluster should be nearest to
   322 @leave System wide error codes
   323 @return The cluster number allocated
   324 */
   325 TUint32 CFatTable::AllocateSingleClusterL(TUint32 aNearestCluster)
   326     {
   327     __PRINT1(_L("CFatTable::AllocateSingleCluster() nearest:%d"), aNearestCluster);
   328     if (iFreeClusters==0)
   329         User::Leave(KErrDiskFull);
   330     const TInt freeCluster=FindClosestFreeClusterL(aNearestCluster);
   331     WriteFatEntryEofL(freeCluster);
   332     DecrementFreeClusterCount(1);
   333 
   334     //-- update free cluster hint, it isn't required to be a precise value, just a hint where to start the from from.
   335     SetFreeClusterHint(freeCluster); 
   336 
   337     return(freeCluster);
   338     }   
   339 
   340 //-----------------------------------------------------------------------------
   341 
   342 /**
   343 Allocate and link a cluster chain, leaves if there are not enough free clusters.
   344 Chain starts as close as possible to aNearestCluster, last cluster will be marked as EOF.
   345 
   346 @param aNumber Number of clusters to allocate
   347 @param aNearestCluster Cluster the new chain should be nearest to
   348 @leave System wide error codes
   349 @return The first cluster number allocated
   350 */
   351 TUint32 CFatTable::AllocateClusterListL(TUint32 aNumber, TUint32 aNearestCluster)
   352     {
   353     __PRINT2(_L("#>> CFatTable::AllocateClusterList() N:%d,NearestCL:%d"),aNumber,aNearestCluster);
   354     __ASSERT_DEBUG(aNumber>0,Fault(EFatBadParameter));
   355 
   356     if (iFreeClusters<aNumber)
   357         {
   358         __PRINT(_L("CFatTable::AllocateClusterListL - leaving KErrDirFull"));
   359         User::Leave(KErrDiskFull);
   360         }
   361 
   362     TInt firstCluster = aNearestCluster = AllocateSingleClusterL(aNearestCluster);
   363     if (aNumber>1)
   364         ExtendClusterListL(aNumber-1, (TInt&)aNearestCluster);
   365 
   366     return(firstCluster);
   367     }   
   368 
   369 //-----------------------------------------------------------------------------
   370 
   371 /**
   372     Notify the media drive about media areas that shall be treated as "deleted" if this feature is supported.
   373     @param aFreedClusters array with FAT numbers of clusters that shall be marked as "deleted"
   374 */
   375 void CFatTable::DoFreedClustersNotify(RClusterArray &aFreedClusters)
   376 {
   377     ASSERT(iMediaAtt & KMediaAttDeleteNotify);
   378 
   379     const TUint clusterCount = aFreedClusters.Count();
   380 
   381     if(!clusterCount)
   382         return;
   383     
   384     FlushL(); //-- Commit the FAT changes to disk first to be safe
   385 
   386     const TUint bytesPerCluster = 1 << iOwner->ClusterSizeLog2();
   387 
   388     TInt64  byteAddress = 0;    
   389     TUint   deleteLen = 0;  // zero indicates no clusters accumulated yet
   390 
   391     for (TUint i=0; i<clusterCount; ++i)
   392     {
   393         const TUint currCluster = aFreedClusters[i];
   394         
   395         if (deleteLen == 0)
   396             byteAddress = DataPositionInBytes(currCluster); //-- start of the media range
   397         
   398         deleteLen += bytesPerCluster;
   399 
   400         //-- if this is the last entry in the array or the net cluster number is not consecutive, notify the driver
   401         if ((i+1) == clusterCount || aFreedClusters[i+1] != (currCluster+1))
   402         {
   403             //__PRINT3(_L("DeleteNotify(%08X:%08X, %u), first cluster %u last cluster #%u"), I64HIGH(byteAddress), I64LOW(byteAddress), deleteLen);
   404             //__PRINT2(_L("   first cluster %u last cluster #%u"), I64LOW((byteAddress - iOwner->ClusterBasePosition()) >> iOwner->ClusterSizeLog2()) + 2, cluster);
   405             const TInt r = iOwner->LocalDrive()->DeleteNotify(byteAddress, deleteLen);
   406             if(r != KErrNone)
   407                 {//-- if DeleteNotify() failed, it means that something terribly wrong happened to the NAND media; 
   408                  //-- in normal circumstances it can not happen. One of the reasons: totally worn out media.
   409                 const TBool platSecEnabled = PlatSec::ConfigSetting(PlatSec::EPlatSecEnforcement);
   410                 __PRINT3(_L("CFatTable::DoFreedClustersNotify() DeleteNotify failure! drv:%d err:%d, PlatSec:%d"),iOwner->DriveNumber(), r, platSecEnabled);
   411 
   412                 if(platSecEnabled)
   413                     {
   414                     //-- if PlatSec is enabled, we can't afford jeopardize the security; without DeleteNotify()
   415                     //-- it's possible to pick up data from deleted files, so, panic the file server.
   416                     Fault(EFatBadLocalDrive);
   417                     }
   418                 else
   419                     {
   420                     //-- if PlatSec is disabled, it's OK to ignore the NAND fault in release mode.
   421                     __ASSERT_DEBUG(0, Fault(EFatBadLocalDrive));
   422                     }        
   423                 }
   424             
   425             
   426             deleteLen = 0;
   427         }
   428 
   429     }
   430 
   431     //-- empty the array.
   432     aFreedClusters.Reset();
   433 }
   434 
   435 //-----------------------------------------------------------------------------
   436 /**
   437 Mark a chain of clusters as free in the FAT. 
   438 
   439 @param aCluster Start cluster of cluster chain to free
   440 @leave System wide error codes
   441 */
   442 void CFatTable::FreeClusterListL(TUint32 aCluster)
   443     {
   444     __PRINT1(_L("CFatTable::FreeClusterListL startCluster=%d"),aCluster);
   445     if (aCluster == KSpareCluster)
   446         return; 
   447 
   448     //-- here we can store array of freed cluster numbers in order to 
   449     //-- notify media drive about the media addresses marked as "invalid"
   450     RClusterArray deletedClusters;      
   451     CleanupClosePushL(deletedClusters);
   452 
   453     //-- if ETrue, we need to notify media driver about invalidated media addressses
   454     const TBool bFreeClustersNotify = iMediaAtt & KMediaAttDeleteNotify;
   455 
   456     //-- this is a maximal number of FAT entries in the deletedClusters array.
   457     //-- as soon as we collect this number of entries in the array, FAT cache will be flushed
   458     //-- and driver notified. The array will be emptied. Used to avoid huge array when deleting
   459     //--  large files on NAND media 
   460     const TUint KSubListLen = 4096;
   461     ASSERT(IsPowerOf2(KSubListLen));
   462 
   463     TUint32 lastKnownFreeCluster = FreeClusterHint();
   464     TUint32 cntFreedClusters = 0;
   465 
   466     TUint32 currCluster = aCluster;
   467     TInt    nextCluster = aCluster;
   468 
   469     for(;;)
   470         {
   471         const TBool bEOF = !GetNextClusterL(nextCluster);    
   472         WriteL(currCluster, KSpareCluster);
   473 
   474         lastKnownFreeCluster = Min(currCluster, lastKnownFreeCluster);
   475 
   476         // Keep a record of the deleted clusters so that we can subsequently notify the media driver. This is only safe 
   477         // to do once the FAT changes have been written to disk.
   478         if(bFreeClustersNotify)
   479             deletedClusters.Append(currCluster);
   480 
   481         ++cntFreedClusters;
   482         currCluster = nextCluster;
   483 
   484         if (bEOF || aCluster == KSpareCluster)
   485             break;
   486 
   487         if(bFreeClustersNotify && cntFreedClusters && (cntFreedClusters & (KSubListLen-1))==0)
   488         {//-- reached a limit of the entries in the array. Flush FAT cache, notify the driver and empty the array.
   489             IncrementFreeClusterCount(cntFreedClusters);
   490             cntFreedClusters = 0;
   491 
   492             SetFreeClusterHint(lastKnownFreeCluster);
   493             DoFreedClustersNotify(deletedClusters);
   494         }
   495 
   496     }
   497 
   498     //-- increase the number of free clusters and notify the driver if required.
   499     IncrementFreeClusterCount(cntFreedClusters);
   500     SetFreeClusterHint(lastKnownFreeCluster);
   501     
   502     if(bFreeClustersNotify)
   503     DoFreedClustersNotify(deletedClusters);
   504 
   505     CleanupStack::PopAndDestroy(&deletedClusters);
   506     }
   507 
   508 //-----------------------------------------------------------------------------
   509 
   510 /**
   511 Find a free cluster nearest to aCluster, Always checks to the right of aCluster first 
   512 but checks in both directions in the Fat.
   513 
   514 @param aCluster Cluster to find nearest free cluster to.
   515 @leave KErrDiskFull + system wide error codes
   516 @return cluster number found
   517 */
   518 TUint32 CFatTable::FindClosestFreeClusterL(TUint32 aCluster)
   519     {
   520     __PRINT2(_L("CFatTable::FindClosestFreeClusterL() drv:%d cl:%d"),iOwner->DriveNumber(),aCluster);
   521 
   522     if(!ClusterNumberValid(aCluster))
   523         {
   524         ASSERT(0);
   525         User::Leave(KErrCorrupt);
   526         }
   527 
   528 
   529     if(iFreeClusters==0)
   530         {//-- there is no at least 1 free cluster available
   531         __PRINT(_L("CFatTable::FindClosestFreeClusterL() leaving KErrDiskFull #1"));
   532         User::Leave(KErrDiskFull);
   533         }
   534     
   535     //-- 1. look if the given index contains a free entry 
   536     if(ReadL(aCluster) != KSpareCluster)
   537         {//-- no, it doesn't...
   538         
   539         //-- 2. look in both directions starting from the aCluster, looking in the right direction first
   540         
   541         const TUint32 maxEntries = MaxEntries();
   542         const TUint32 MinIdx = KFatFirstSearchCluster;
   543         const TUint32 MaxIdx = maxEntries-1; 
   544 
   545         TBool canGoRight = ETrue;
   546         TBool canGoLeft = ETrue;
   547     
   548         TUint32 rightIdx = aCluster;
   549         TUint32 leftIdx  = aCluster;
   550         
   551         for(TUint i=0; i<maxEntries; ++i)
   552             {
   553             if(canGoRight)
   554                 {
   555                 if(rightIdx < MaxIdx)
   556                     ++rightIdx;
   557                 else
   558                     canGoRight = EFalse;
   559                 }
   560 
   561             if(canGoLeft)
   562                 {
   563                 if(leftIdx > MinIdx)
   564                     --leftIdx;
   565                 else        
   566                     canGoLeft = EFalse;
   567                 }
   568 
   569         if(!canGoRight && !canGoLeft)
   570             {
   571             __PRINT(_L("CFatTable::FindClosestFreeClusterL() leaving KErrDiskFull #2"));
   572             User::Leave(KErrDiskFull);
   573             }
   574 
   575         if (canGoRight && ReadL(rightIdx) == KSpareCluster)
   576             {
   577             aCluster = rightIdx;
   578             break;
   579             }
   580 
   581         if (canGoLeft && ReadL(leftIdx) == KSpareCluster)
   582             {
   583             aCluster = leftIdx;
   584             break;
   585             }
   586             }//for(..)
   587 
   588         }//if(ReadL(aCluster) != KSpareCluster)
   589 
   590 
   591     //-- note: do not update free cluster hint here by calling SetFreeClusterHint(). This is going to be 
   592     //-- expensive especially if overridden methods with synchronisation are called. Instead, set the number of 
   593     //-- the last known free cluster in the caller of this internal method.
   594 
   595 //    __PRINT1(_L("CFatTable::FindClosestFreeClusterL found:%d"),aCluster);
   596 
   597     return aCluster;
   598     }
   599 
   600 //-----------------------------------------------------------------------------
   601 
   602 /**
   603     Converts a cluster number to byte offset in the FAT
   604 
   605 @param aFatIndex Cluster number
   606     @return Number of bytes from the beginning of the FAT
   607 */
   608 TUint32 CFatTable::PosInBytes(TUint32 aFatIndex) const
   609     {
   610     switch(FatType())
   611         {
   612         case EFat12:
   613             return (((aFatIndex>>1)<<1) + (aFatIndex>>1)); //-- 1.5 bytes per FAT entry
   614 
   615         case EFat16:
   616             return aFatIndex<<1; //-- 2 bytes per FAT entry
   617 
   618         default:
   619             ASSERT(0);
   620             return 0;//-- get rid of warning
   621         };
   622 
   623     }
   624 
   625 //-----------------------------------------------------------------------------
   626 
   627 /**
   628     Checks if we have at least aClustersRequired clusters free in the FAT.
   629     This is, actually a dummy implementation.
   630 
   631     @param  aClustersRequired number of free clusters required
   632     @return ETrue if there is at least aClustersRequired free clusters available, EFalse otherwise.
   633 */
   634 TBool CFatTable::RequestFreeClusters(TUint32 aClustersRequired) const
   635 {
   636     //ASSERT(aClustersRequired >0 && aClustersRequired <= iOwner->UsableClusters());
   637     ASSERT(aClustersRequired >0);
   638     return (NumberOfFreeClusters() >= aClustersRequired);
   639 }
   640 
   641 //-----------------------------------------------------------------------------
   642 /**
   643     @return ETrue if the cluster number aClusterNo is valid, i.e. belongs to the FAT table
   644 */
   645 TBool CFatTable::ClusterNumberValid(TUint32 aClusterNo) const 
   646     {
   647     return (aClusterNo >= KFatFirstSearchCluster) && (aClusterNo < iMaxEntries); 
   648     }
   649 
   650 
   651 
   652 //#######################################################################################################################################
   653 //#     CAtaFatTable class implementation 
   654 //#######################################################################################################################################
   655 
   656 /**
   657 Constructor
   658 */
   659 CAtaFatTable::CAtaFatTable(CFatMountCB& aOwner)
   660              :CFatTable(aOwner)
   661     {
   662     }
   663 
   664 
   665 /** factory method */
   666 CAtaFatTable* CAtaFatTable::NewL(CFatMountCB& aOwner)
   667 {
   668     __PRINT1(_L("CAtaFatTable::NewL() drv:%d"),aOwner.DriveNumber());
   669     CAtaFatTable* pSelf = new (ELeave) CAtaFatTable(aOwner);
   670 
   671     CleanupStack::PushL(pSelf);
   672     pSelf->InitializeL();
   673     CleanupStack::Pop();
   674 
   675     return pSelf;
   676 }
   677 
   678 
   679 //---------------------------------------------------------------------------------------------------------------------------------------
   680 
   681 /**
   682     CAtaFatTable's FAT cache factory method.
   683     Creates fixed cache for FAT12 or FAT16
   684 */
   685 void CAtaFatTable::CreateCacheL()
   686 {
   687     ASSERT(iOwner);
   688     const TUint32 fatSize=iOwner->FatSizeInBytes();
   689     __PRINT3(_L("CAtaFatTable::CreateCacheL drv:%d, FAT:%d, FAT Size:%d"), iOwner->DriveNumber(), FatType(), fatSize);
   690     
   691 
   692     //-- according to FAT specs:
   693     //-- FAT12 max size is 4084 entries or 6126 bytes                                               => create fixed cache for whole FAT
   694     //-- FAT16 min size is 4085 entries or 8170 bytes, max size is 65525 entries or 131048 bytes    => create fixed cache for whole FAT
   695 
   696     ASSERT(!iCache);
   697 
   698     //-- this is used for chaches granularity sanity check 
   699     const TUint32 KMaxGranularityLog2 = 18; //-- 256K is a maximal allowed granularity
   700     const TUint32 KMinGranularityLog2 = KDefSectorSzLog2;  //-- 512 bytes is a minimal allowed granularity
   701 
   702     switch(FatType())
   703     {
   704         case EFat12: //-- create fixed FAT12 cache
   705             iCache = CFat12Cache::NewL(iOwner, fatSize); 
   706         break;
   707     
   708         case EFat16: //-- create fixed FAT16 cache
   709         {
   710             TUint32 fat16_ReadGranularity_Log2; //-- FAT16 cache read granularity Log2
   711             TUint32 fat16_WriteGranularity_Log2;//-- FAT16 cache write granularity Log2
   712             
   713             iOwner->FatConfig().Fat16FixedCacheParams(fat16_ReadGranularity_Log2, fat16_WriteGranularity_Log2);
   714             
   715             //-- check if granularity values look sensible
   716             const TBool bParamsValid = fat16_ReadGranularity_Log2  >= KMinGranularityLog2 && fat16_ReadGranularity_Log2  <= KMaxGranularityLog2 &&
   717                                        fat16_WriteGranularity_Log2 >= KMinGranularityLog2 && fat16_WriteGranularity_Log2 <= KMaxGranularityLog2;
   718             
   719             __ASSERT_ALWAYS(bParamsValid, Fault(EFatCache_BadGranularity)); 
   720 
   721         
   722             iCache = CFat16FixedCache::NewL(iOwner, fatSize, fat16_ReadGranularity_Log2, fat16_WriteGranularity_Log2); 
   723         }
   724         break;
   725 
   726         default:
   727         ASSERT(0);
   728         User::Leave(KErrCorrupt);
   729         break;
   730     };
   731 
   732     ASSERT(iCache);
   733 }
   734 
   735 //---------------------------------------------------------------------------------------------------------------------------------------
   736 
   737 
   738 /**
   739     Flush the FAT cache on disk
   740 @leave System wide error codes
   741 */
   742 void CAtaFatTable::FlushL()
   743     {
   744     //-- the data can't be written if the mount is inconsistent
   745     iOwner->CheckStateConsistentL();
   746 
   747     if (iCache)
   748         iCache->FlushL();
   749     }
   750 
   751 /**
   752 Clear any cached data
   753     @param aDiscardDirtyData if ETrue, non-flushed data in the cache will be discarded.
   754 */
   755 void CAtaFatTable::Dismount(TBool aDiscardDirtyData)
   756     {
   757     if (iCache)
   758         {
   759         //-- cache's Close() can check if the cache is clean. 
   760         //-- ignore dirty data in cache if the mount is not in consistent state (it's impossible to flush cache data)
   761         //-- or if we are asked to do so.
   762         const TBool bIgnoreDirtyData = aDiscardDirtyData || !iOwner->ConsistentState();
   763         iCache->Close(bIgnoreDirtyData);
   764 
   765         delete iCache;
   766         iCache=NULL;
   767         }
   768 
   769     }
   770 
   771 //---------------------------------------------------------------------------------------------------------------------------------------
   772 
   773 /**
   774     Invalidate whole FAT cache.
   775     Depending of cache type this may just mark cache invalid with reading on demand or re-read whole cache from the media
   776 */
   777 void CAtaFatTable::InvalidateCacheL()
   778 {
   779     __PRINT1(_L("CAtaFatTable::InvalidateCache(), drv:%d"), iOwner->DriveNumber());
   780 
   781     //-- if we have a cache, invalidate it entirely
   782     if(iCache)
   783     {
   784         User::LeaveIfError(iCache->Invalidate());
   785     }
   786 }
   787 
   788 
   789 //---------------------------------------------------------------------------------------------------------------------------------------
   790 
   791 /**
   792     Invalidate specified region of the FAT cache
   793     Depending of cache type this may just mark part of the cache invalid with reading on demand later
   794     or re-read whole cache from the media.
   795 
   796     @param aPos absolute media position where the region being invalidated starts.
   797     @param aLength length in bytes of region to invalidate / refresh
   798 */
   799 void CAtaFatTable::InvalidateCacheL(TInt64 aPos, TUint32 aLength)
   800     {
   801     __PRINT3(_L("CAtaFatTable::InvalidateCacheL() drv:%d, pos:%LU, len:%u,"), iOwner->DriveNumber(), aPos, aLength);
   802 
   803     if(I64HIGH(aPos) || !aLength || I64HIGH(aPos+aLength))
   804         return; //-- FAT tables can't span over 4G 
   805 
   806     const TUint32 mediaPos32 = I64LOW(aPos);
   807 
   808     //-- we do not use other copies of FAT, so trach changes only in FAT1
   809     const TUint32 fat1StartPos = iOwner->StartOfFatInBytes();
   810     const TUint32 fat1EndPos   = fat1StartPos + iOwner->FatSizeInBytes();
   811 
   812     TUint32 invRegionPosStart = 0; //-- media pos where the invalidated region starts
   813     TUint32 invRegionLen = 0;      //-- size of the invalidated region, bytes
   814     
   815     //-- calculate the FAT1 region being invalidated
   816     if(mediaPos32 < fat1StartPos)
   817     {
   818         if((mediaPos32 + aLength) <= fat1StartPos)
   819             return;
   820 
   821         invRegionPosStart = fat1StartPos;
   822         invRegionLen = aLength - (fat1StartPos-mediaPos32);
   823     }
   824     else //if(mediaPos32 < fat1StartPos)
   825     {//-- mediaPos32 >= fat1StartPos)
   826         if(mediaPos32 >= fat1EndPos)
   827             return;
   828     
   829         invRegionPosStart = mediaPos32;
   830         
   831         if((mediaPos32 + aLength) <= fat1EndPos)
   832         {
   833             invRegionLen = aLength;
   834         }
   835         else 
   836         {
   837             invRegionLen = mediaPos32+aLength-fat1EndPos;
   838         }
   839     }
   840 
   841     //-- convert the media pos of the region into FAT entries basis, depending on the FAT type
   842     ASSERT(invRegionPosStart >= fat1StartPos && invRegionLen <= (TUint)iOwner->FatSizeInBytes());
   843     
   844     TUint32 startFatEntry=0;
   845     TUint32 numEntries = 0;
   846 
   847     switch(FatType())
   848     {
   849         case EFat12:
   850         //-- invalidate whole cache; it is not worth making calculations for such small memory region.
   851         User::LeaveIfError(iCache->Invalidate());
   852         return;
   853 
   854         case EFat16:
   855         startFatEntry = (invRegionPosStart-fat1StartPos) >> KFat16EntrySzLog2;
   856         numEntries = (invRegionLen + (sizeof(TFat16Entry)-1)) >> KFat16EntrySzLog2;
   857         break;
   858 
   859         default:
   860         ASSERT(0);
   861         return;
   862     };
   863 
   864     if(startFatEntry < KFatFirstSearchCluster)
   865     {//-- FAT[0] and FAT[1] can't be legally accessed, they are reserved entries. We need to adjust region being refreshed.
   866         if(numEntries <= KFatFirstSearchCluster)
   867             return; //-- nothing to refresh
   868                     
   869         startFatEntry += KFatFirstSearchCluster;
   870         numEntries -= KFatFirstSearchCluster;
   871     }
   872 
   873     User::LeaveIfError(iCache->InvalidateRegion(startFatEntry, numEntries));
   874     }
   875 
   876 
   877 //-----------------------------------------------------------------------------
   878 /**
   879     Initialize the object, create FAT cache if required
   880 @leave KErrNoMemory
   881 */
   882 void CAtaFatTable::InitializeL()
   883     {
   884     __PRINT1(_L("CAtaFatTable::InitializeL() drv:%d"), iOwner->DriveNumber());
   885     CFatTable::InitializeL();
   886 
   887     //-- create the FAT cache.
   888     ASSERT(!iCache);
   889     CreateCacheL();
   890     }
   891 
   892 
   893 //-----------------------------------------------------------------------------
   894 /**
   895     Remount the FAT table. This method call means that the media parameters wasn't changed, 
   896     otherwise CFatMountCB::DoReMountL() would reject it. 
   897     Just do some re-initialisation work.
   898 */
   899 void CAtaFatTable::ReMountL()
   900 {
   901     __PRINT1(_L("CAtaFatTable::ReMountL() drv:%d"), iOwner->DriveNumber());
   902 
   903     if(iCache)
   904         {
   905         iCache->Invalidate();
   906         }
   907     else
   908         {
   909         //-- this situation can happen when someone called CAtaFatTable::Dismount() that deletes the cache object
   910         //-- and then ReMount happens. We need to re-initialise this object.
   911         InitializeL();
   912         }
   913 }
   914 
   915 
   916 //-----------------------------------------------------------------------------
   917 /**
   918     Read an entry from the FAT table
   919 
   920     @param aFatIndex FAT entry number to read
   921     @return FAT entry value
   922 */
   923 TUint32 CAtaFatTable::ReadL(TUint32 aFatIndex) const
   924     {
   925     if(!ClusterNumberValid(aFatIndex))
   926         {
   927         //ASSERT(0); //-- for some silly reason some callers pass 0 here and expect it to leave
   928         User::Leave(KErrCorrupt);
   929         }
   930 
   931 
   932     const TUint entry = iCache->ReadEntryL(aFatIndex);
   933     return entry;
   934     }
   935 
   936 
   937 //-----------------------------------------------------------------------------
   938 /**
   939     Write an entry to the FAT table
   940 
   941     @param aFatIndex    aFatIndex FAT entry number to write
   942     @param aValue       FAT entry to write
   943 @leave
   944 */
   945 void CAtaFatTable::WriteL(TUint32 aFatIndex, TUint32 aValue)
   946     {
   947     const TUint32 KFat16EntryMask = 0x0FFFF;
   948     
   949     __PRINT2(_L("CAtaFatTable::WriteL() entry:%d, val:0x%x"), aFatIndex, aValue);
   950     
   951     if(!ClusterNumberValid(aFatIndex))
   952         {
   953         ASSERT(0); 
   954         User::Leave(KErrCorrupt);
   955         }
   956     
   957     if(aValue != KSpareCluster && (aValue < KFatFirstSearchCluster || aValue > KFat16EntryMask))
   958         {
   959         ASSERT(0);
   960         User::Leave(KErrCorrupt);
   961         }
   962     iCache->WriteEntryL(aFatIndex, aValue);
   963     }
   964 
   965 
   966 /**
   967 Get the next cluster in the chain from the FAT
   968 
   969 @param aCluster number to read, contains next cluster upon return
   970 @leave
   971 @return False if end of cluster chain
   972 */
   973 TBool CFatTable::GetNextClusterL(TInt& aCluster) const
   974     {
   975     __PRINT1(_L("CAtaFatTable::GetNextClusterL(%d)"), aCluster);
   976     
   977     const TInt nextCluster = ReadL(aCluster);
   978     TBool ret = EFalse; 
   979 
   980     switch(FatType())
   981         {
   982         case EFat12:
   983             ret=!IsEof12Bit(nextCluster);
   984         break;
   985 
   986         case EFat16:
   987             ret=!IsEof16Bit(nextCluster);
   988         break;
   989 
   990         default:
   991             ASSERT(0);
   992             return EFalse;//-- get rid of warning
   993         };
   994     
   995     if (ret)
   996         {
   997         aCluster=nextCluster;
   998         }
   999     
  1000     return ret;
  1001 
  1002     }
  1003 
  1004 /**
  1005 Write EOF to aFatIndex
  1006     @param aFatIndex index in FAT (cluster number) to be written
  1007 */
  1008 void CFatTable::WriteFatEntryEofL(TUint32 aFatIndex)
  1009     {
  1010     __PRINT1(_L("CAtaFatTable::WriteFatEntryEofL(%d)"), aFatIndex);
  1011 
  1012     //-- use EOF_16Bit (0x0ffff) for all types of FAT, FAT cache will mask it appropriately
  1013     WriteL(aFatIndex, EOF_16Bit);
  1014     }
  1015 
  1016 
  1017 
  1018 /** 
  1019     Mark cluster number aFatIndex in FAT as bad 
  1020     @param aFatIndex index in FAT (cluster number) to be written
  1021 */
  1022 void CFatTable::MarkAsBadClusterL(TUint32 aFatIndex)
  1023     {
  1024     __PRINT1(_L("CAtaFatTable::MarkAsBadClusterL(%d)"),aFatIndex);
  1025 
  1026     //-- use KBad_16Bit (0x0fff7) for all types of FAT, FAT cache will mask it appropriately
  1027     WriteL(aFatIndex, KBad_16Bit);
  1028     
  1029     FlushL();
  1030     }
  1031 
  1032 
  1033 /**
  1034 Return the location of a Cluster in the data section of the media
  1035 
  1036 @param aCluster to find location of
  1037 @return Byte offset of the cluster data 
  1038 */
  1039 TInt64 CAtaFatTable::DataPositionInBytes(TUint32 aCluster) const
  1040     {
  1041     __ASSERT_DEBUG(ClusterNumberValid(aCluster), Fault(EFatTable_InvalidIndex));
  1042 
  1043     const TInt clusterBasePosition=iOwner->ClusterBasePosition();
  1044     return(((TInt64(aCluster)-KFatFirstSearchCluster) << iOwner->ClusterSizeLog2()) + clusterBasePosition);
  1045     }
  1046 
  1047 
  1048 
  1049 
  1050 
  1051