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