userlibandfileserver/fileserver/sfat/fat_table.cpp
changeset 9 96e5fb8b040d
equal deleted inserted replaced
-1:000000000000 9:96e5fb8b040d
       
     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