1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/kernelhwsrv/userlibandfileserver/fileserver/sfat/fat_table.cpp Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,1051 @@
1.4 +// Copyright (c) 1996-2009 Nokia Corporation and/or its subsidiary(-ies).
1.5 +// All rights reserved.
1.6 +// This component and the accompanying materials are made available
1.7 +// under the terms of the License "Eclipse Public License v1.0"
1.8 +// which accompanies this distribution, and is available
1.9 +// at the URL "http://www.eclipse.org/legal/epl-v10.html".
1.10 +//
1.11 +// Initial Contributors:
1.12 +// Nokia Corporation - initial contribution.
1.13 +//
1.14 +// Contributors:
1.15 +//
1.16 +// Description:
1.17 +// f32\sfat\fat_table.cpp
1.18 +// FAT12/16 File Allocation Table classes implementation
1.19 +//
1.20 +//
1.21 +
1.22 +/**
1.23 + @file
1.24 + @internalTechnology
1.25 +*/
1.26 +
1.27 +//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1.28 +//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1.29 +//!!
1.30 +//!! WARNING!! DO NOT edit this file !! '\sfat' component is obsolete and is not being used. '\sfat32'replaces it
1.31 +//!!
1.32 +//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1.33 +//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
1.34 +
1.35 +
1.36 +#include "sl_std.h"
1.37 +#include "sl_fatcache.h"
1.38 +#include "fat_table.h"
1.39 +
1.40 +
1.41 +//#######################################################################################################################################
1.42 +//# CFatTable class implementation
1.43 +//#######################################################################################################################################
1.44 +
1.45 +/**
1.46 + FAT object factory method.
1.47 + Constructs either CAtaFatTable or CRamFatTable depending on the media type parameter
1.48 +
1.49 + @param aOwner Pointer to the owning mount
1.50 + @param aLocDrvCaps local drive attributes
1.51 + @leave KErrNoMemory
1.52 + @return Pointer to the Fat table
1.53 +*/
1.54 +CFatTable* CFatTable::NewL(CFatMountCB& aOwner, const TLocalDriveCaps& aLocDrvCaps)
1.55 + {
1.56 + CFatTable* pFatTable=NULL;
1.57 +
1.58 +
1.59 + switch(aLocDrvCaps.iType)
1.60 + {
1.61 + case EMediaRam:
1.62 + {//-- this is RAM media, try to create CRamFatTable instance.
1.63 + const TFatType fatType = aOwner.FatType();
1.64 +
1.65 + if(fatType != EFat16 )
1.66 + {//-- CRamFatTable doesn't support FAT12; FAT16 only.
1.67 + __PRINT1(_L("CFatTable::NewL() CRamFatTable doesn't support this FAT type:%d"), fatType);
1.68 + ASSERT(0);
1.69 + return NULL;
1.70 + }
1.71 +
1.72 + pFatTable = CRamFatTable::NewL(aOwner);
1.73 + }
1.74 + break;
1.75 +
1.76 + default:
1.77 + //-- other media
1.78 + pFatTable = CAtaFatTable::NewL(aOwner);
1.79 + break;
1.80 + };
1.81 +
1.82 + return pFatTable;
1.83 + }
1.84 +
1.85 +CFatTable::CFatTable(CFatMountCB& aOwner)
1.86 +{
1.87 + iOwner = &aOwner;
1.88 + ASSERT(iOwner);
1.89 +}
1.90 +
1.91 +CFatTable::~CFatTable()
1.92 +{
1.93 + //-- destroy cache ignoring dirty data in cache
1.94 + //-- the destructor isn't an appropriate place to flush the data.
1.95 + Dismount(ETrue);
1.96 +}
1.97 +
1.98 +//-----------------------------------------------------------------------------
1.99 +
1.100 +/**
1.101 + Initialise the object, get data from the owning CFatMountCB
1.102 +*/
1.103 +void CFatTable::InitializeL()
1.104 + {
1.105 + ASSERT(iOwner);
1.106 +
1.107 + //-- get FAT type from the owner
1.108 + iFatType = iOwner->FatType();
1.109 + ASSERT(IsFat12() || IsFat16());
1.110 +
1.111 + iFreeClusterHint = KFatFirstSearchCluster;
1.112 +
1.113 + //-- cache the media attributes
1.114 + TLocalDriveCapsV2 caps;
1.115 + TPckg<TLocalDriveCapsV2> capsPckg(caps);
1.116 + User::LeaveIfError(iOwner->LocalDrive()->Caps(capsPckg));
1.117 + iMediaAtt = caps.iMediaAtt;
1.118 +
1.119 + //-- obtain maximal number of entries in the table
1.120 + iMaxEntries = iOwner->UsableClusters()+KFatFirstSearchCluster; //-- FAT[0] & FAT[1] are not in use
1.121 +
1.122 + __PRINT3(_L("CFatTable::InitializeL(), drv:%d, iMediaAtt = %08X, max Entries:%d"), iOwner->DriveNumber(), iMediaAtt, iMaxEntries);
1.123 + }
1.124 +
1.125 +//-----------------------------------------------------------------------------
1.126 +
1.127 +/**
1.128 + Decrements the free cluster count.
1.129 + Note that can be quite expensive operation (especially for overrides with synchronisation), if it is called for every
1.130 + cluster of a large file. Use more than one cluster granularity.
1.131 +
1.132 + @param aCount a number of clusters
1.133 +*/
1.134 +void CFatTable::DecrementFreeClusterCount(TUint32 aCount)
1.135 +{
1.136 + __ASSERT_DEBUG(iFreeClusters >= aCount, Fault(EFatCorrupt));
1.137 + iFreeClusters -= aCount;
1.138 +}
1.139 +
1.140 +/**
1.141 + Increments the free cluster count.
1.142 + Note that can be quite expensive operation (especially for overrides with synchronisation), if it is called for every
1.143 + cluster of a large file. Use more than one cluster granularity.
1.144 +
1.145 + @param aCount a number of clusters
1.146 +*/
1.147 +void CFatTable::IncrementFreeClusterCount(TUint32 aCount)
1.148 +{
1.149 + const TUint32 newVal = iFreeClusters+aCount;
1.150 + __ASSERT_DEBUG(newVal<=MaxEntries(), Fault(EFatCorrupt));
1.151 +
1.152 + iFreeClusters = newVal;
1.153 +}
1.154 +
1.155 +/** @return number of free clusters in the FAT */
1.156 +TUint32 CFatTable::NumberOfFreeClusters(TBool /*aSyncOperation=EFalse*/) const
1.157 +{
1.158 + return FreeClusters();
1.159 +}
1.160 +
1.161 +void CFatTable::SetFreeClusters(TUint32 aFreeClusters)
1.162 +{
1.163 + iFreeClusters=aFreeClusters;
1.164 +}
1.165 +
1.166 +/**
1.167 + Get the hint about the last known free cluster number.
1.168 + Note that can be quite expensive operation (especially for overrides with synchronisation), if it is called for every
1.169 + cluster of a large file.
1.170 +
1.171 + @return cluster number supposedly close to the free one.
1.172 +*/
1.173 +TUint32 CFatTable::FreeClusterHint() const
1.174 +{
1.175 + ASSERT(ClusterNumberValid(iFreeClusterHint));
1.176 + return iFreeClusterHint;
1.177 +}
1.178 +
1.179 +/**
1.180 + Set a free cluster hint. The next search fro the free cluster can start from this value.
1.181 + aCluster doesn't have to be a precise number of free FAT entry; it just needs to be as close as possible to the
1.182 + free entries chain.
1.183 + Note that can be quite expensive operation (especially for overrides with synchronisation), if it is called for every
1.184 + cluster of a large file.
1.185 +
1.186 + @param aCluster cluster number hint.
1.187 +*/
1.188 +void CFatTable::SetFreeClusterHint(TUint32 aCluster)
1.189 +{
1.190 + ASSERT(ClusterNumberValid(aCluster));
1.191 + iFreeClusterHint=aCluster;
1.192 +}
1.193 +
1.194 +//-----------------------------------------------------------------------------
1.195 +
1.196 +/**
1.197 + Find out the number of free clusters on the volume.
1.198 + Reads whole FAT and counts free clusters.
1.199 +*/
1.200 +void CFatTable::CountFreeClustersL()
1.201 +{
1.202 + __PRINT1(_L("#- CFatTable::CountFreeClustersL(), drv:%d"), iOwner->DriveNumber());
1.203 +
1.204 + const TUint32 KUsableClusters = iOwner->UsableClusters();
1.205 + (void)KUsableClusters;
1.206 +
1.207 + TUint32 freeClusters = 0;
1.208 + TUint32 firstFreeCluster = 0;
1.209 +
1.210 + TTime timeStart;
1.211 + TTime timeEnd;
1.212 + timeStart.UniversalTime(); //-- take start time
1.213 +
1.214 + //-- walk through whole FAT table looking for free clusters
1.215 + for(TUint i=KFatFirstSearchCluster; i<MaxEntries(); ++i)
1.216 + {
1.217 + if(ReadL(i) == KSpareCluster)
1.218 + {//-- found a free cluster
1.219 + ++freeClusters;
1.220 +
1.221 + if(!firstFreeCluster)
1.222 + firstFreeCluster = i;
1.223 + }
1.224 + }
1.225 +
1.226 + timeEnd.UniversalTime(); //-- take end time
1.227 + const TInt msScanTime = (TInt)( (timeEnd.MicroSecondsFrom(timeStart)).Int64() / K1mSec);
1.228 + __PRINT1(_L("#- CFatTable::CountFreeClustersL() finished. Taken:%d ms"), msScanTime);
1.229 + (void)msScanTime;
1.230 +
1.231 + if(!firstFreeCluster) //-- haven't found free clusters on the volume
1.232 + firstFreeCluster = KFatFirstSearchCluster;
1.233 +
1.234 + ASSERT(freeClusters <= KUsableClusters);
1.235 +
1.236 + SetFreeClusters(freeClusters);
1.237 + SetFreeClusterHint(firstFreeCluster);
1.238 +}
1.239 +
1.240 +//-----------------------------------------------------------------------------
1.241 +
1.242 +/**
1.243 +Count the number of contiguous cluster from a start cluster
1.244 +
1.245 +@param aStartCluster cluster to start counting from
1.246 +@param anEndCluster contains the end cluster number upon return
1.247 +@param aMaxCount Maximum cluster required
1.248 +@leave System wide error values
1.249 +@return Number of contiguous clusters from aStartCluster.
1.250 +*/
1.251 +TInt CFatTable::CountContiguousClustersL(TUint32 aStartCluster,TInt& anEndCluster,TUint32 aMaxCount) const
1.252 + {
1.253 + __PRINT2(_L("CFatTable::CountContiguousClustersL() start:%d, max:%d"),aStartCluster, aMaxCount);
1.254 + TUint32 clusterListLen=1;
1.255 + TInt endCluster=aStartCluster;
1.256 + TInt64 endClusterPos=DataPositionInBytes(endCluster);
1.257 + while (clusterListLen<aMaxCount)
1.258 + {
1.259 + TInt oldCluster=endCluster;
1.260 + TInt64 oldClusterPos=endClusterPos;
1.261 + if (GetNextClusterL(endCluster)==EFalse || (endClusterPos=DataPositionInBytes(endCluster))!=(oldClusterPos+(1<<iOwner->ClusterSizeLog2())))
1.262 + {
1.263 + endCluster=oldCluster;
1.264 + break;
1.265 + }
1.266 + clusterListLen++;
1.267 + }
1.268 + anEndCluster=endCluster;
1.269 + return(clusterListLen);
1.270 + }
1.271 +
1.272 +//-----------------------------------------------------------------------------
1.273 +
1.274 +/**
1.275 + Extend a file or directory cluster chain, leaves if there are no free clusters (the disk is full).
1.276 +
1.277 + @param aNumber amount of clusters to allocate
1.278 + @param aCluster FAT entry index to start with.
1.279 +
1.280 + @leave KErrDiskFull + system wide error codes
1.281 +*/
1.282 +void CFatTable::ExtendClusterListL(TUint32 aNumber,TInt& aCluster)
1.283 + {
1.284 + __PRINT2(_L("CFatTable::ExtendClusterListL() num:%d, clust:%d"), aNumber, aCluster);
1.285 + __ASSERT_DEBUG(aNumber>0,Fault(EFatBadParameter));
1.286 +
1.287 + while(aNumber && GetNextClusterL(aCluster))
1.288 + aNumber--;
1.289 +
1.290 + if(!aNumber)
1.291 + return;
1.292 +
1.293 + if (iFreeClusters<aNumber)
1.294 + {
1.295 + __PRINT(_L("CFatTable::ExtendClusterListL - leaving KErrDirFull"));
1.296 + User::Leave(KErrDiskFull);
1.297 + }
1.298 +
1.299 +
1.300 + TUint32 freeCluster = 0;
1.301 +
1.302 + //-- note: this can be impoved by trying to fing as long chain of free clusters as possible in FindClosestFreeClusterL()
1.303 + for(TUint i=0; i<aNumber; ++i)
1.304 + {
1.305 + freeCluster = FindClosestFreeClusterL(aCluster);
1.306 + WriteFatEntryEofL(freeCluster); // Must write EOF for FindClosestFreeCluster to work again
1.307 + WriteL(aCluster,freeCluster);
1.308 + aCluster=freeCluster;
1.309 + }
1.310 +
1.311 + //-- decrement number of available clusters
1.312 + DecrementFreeClusterCount(aNumber);
1.313 +
1.314 + //-- update free cluster hint, it isn't required to be a precise value, just a hint where to start the from from
1.315 + SetFreeClusterHint(aCluster);
1.316 +
1.317 + }
1.318 +
1.319 +//-----------------------------------------------------------------------------
1.320 +
1.321 +/**
1.322 +Allocate and mark as EOF a single cluster as close as possible to aNearestCluster
1.323 +
1.324 +@param aNearestCluster Cluster the new cluster should be nearest to
1.325 +@leave System wide error codes
1.326 +@return The cluster number allocated
1.327 +*/
1.328 +TUint32 CFatTable::AllocateSingleClusterL(TUint32 aNearestCluster)
1.329 + {
1.330 + __PRINT1(_L("CFatTable::AllocateSingleCluster() nearest:%d"), aNearestCluster);
1.331 + if (iFreeClusters==0)
1.332 + User::Leave(KErrDiskFull);
1.333 + const TInt freeCluster=FindClosestFreeClusterL(aNearestCluster);
1.334 + WriteFatEntryEofL(freeCluster);
1.335 + DecrementFreeClusterCount(1);
1.336 +
1.337 + //-- update free cluster hint, it isn't required to be a precise value, just a hint where to start the from from.
1.338 + SetFreeClusterHint(freeCluster);
1.339 +
1.340 + return(freeCluster);
1.341 + }
1.342 +
1.343 +//-----------------------------------------------------------------------------
1.344 +
1.345 +/**
1.346 +Allocate and link a cluster chain, leaves if there are not enough free clusters.
1.347 +Chain starts as close as possible to aNearestCluster, last cluster will be marked as EOF.
1.348 +
1.349 +@param aNumber Number of clusters to allocate
1.350 +@param aNearestCluster Cluster the new chain should be nearest to
1.351 +@leave System wide error codes
1.352 +@return The first cluster number allocated
1.353 +*/
1.354 +TUint32 CFatTable::AllocateClusterListL(TUint32 aNumber, TUint32 aNearestCluster)
1.355 + {
1.356 + __PRINT2(_L("#>> CFatTable::AllocateClusterList() N:%d,NearestCL:%d"),aNumber,aNearestCluster);
1.357 + __ASSERT_DEBUG(aNumber>0,Fault(EFatBadParameter));
1.358 +
1.359 + if (iFreeClusters<aNumber)
1.360 + {
1.361 + __PRINT(_L("CFatTable::AllocateClusterListL - leaving KErrDirFull"));
1.362 + User::Leave(KErrDiskFull);
1.363 + }
1.364 +
1.365 + TInt firstCluster = aNearestCluster = AllocateSingleClusterL(aNearestCluster);
1.366 + if (aNumber>1)
1.367 + ExtendClusterListL(aNumber-1, (TInt&)aNearestCluster);
1.368 +
1.369 + return(firstCluster);
1.370 + }
1.371 +
1.372 +//-----------------------------------------------------------------------------
1.373 +
1.374 +/**
1.375 + Notify the media drive about media areas that shall be treated as "deleted" if this feature is supported.
1.376 + @param aFreedClusters array with FAT numbers of clusters that shall be marked as "deleted"
1.377 +*/
1.378 +void CFatTable::DoFreedClustersNotify(RClusterArray &aFreedClusters)
1.379 +{
1.380 + ASSERT(iMediaAtt & KMediaAttDeleteNotify);
1.381 +
1.382 + const TUint clusterCount = aFreedClusters.Count();
1.383 +
1.384 + if(!clusterCount)
1.385 + return;
1.386 +
1.387 + FlushL(); //-- Commit the FAT changes to disk first to be safe
1.388 +
1.389 + const TUint bytesPerCluster = 1 << iOwner->ClusterSizeLog2();
1.390 +
1.391 + TInt64 byteAddress = 0;
1.392 + TUint deleteLen = 0; // zero indicates no clusters accumulated yet
1.393 +
1.394 + for (TUint i=0; i<clusterCount; ++i)
1.395 + {
1.396 + const TUint currCluster = aFreedClusters[i];
1.397 +
1.398 + if (deleteLen == 0)
1.399 + byteAddress = DataPositionInBytes(currCluster); //-- start of the media range
1.400 +
1.401 + deleteLen += bytesPerCluster;
1.402 +
1.403 + //-- if this is the last entry in the array or the net cluster number is not consecutive, notify the driver
1.404 + if ((i+1) == clusterCount || aFreedClusters[i+1] != (currCluster+1))
1.405 + {
1.406 + //__PRINT3(_L("DeleteNotify(%08X:%08X, %u), first cluster %u last cluster #%u"), I64HIGH(byteAddress), I64LOW(byteAddress), deleteLen);
1.407 + //__PRINT2(_L(" first cluster %u last cluster #%u"), I64LOW((byteAddress - iOwner->ClusterBasePosition()) >> iOwner->ClusterSizeLog2()) + 2, cluster);
1.408 + const TInt r = iOwner->LocalDrive()->DeleteNotify(byteAddress, deleteLen);
1.409 + if(r != KErrNone)
1.410 + {//-- if DeleteNotify() failed, it means that something terribly wrong happened to the NAND media;
1.411 + //-- in normal circumstances it can not happen. One of the reasons: totally worn out media.
1.412 + const TBool platSecEnabled = PlatSec::ConfigSetting(PlatSec::EPlatSecEnforcement);
1.413 + __PRINT3(_L("CFatTable::DoFreedClustersNotify() DeleteNotify failure! drv:%d err:%d, PlatSec:%d"),iOwner->DriveNumber(), r, platSecEnabled);
1.414 +
1.415 + if(platSecEnabled)
1.416 + {
1.417 + //-- if PlatSec is enabled, we can't afford jeopardize the security; without DeleteNotify()
1.418 + //-- it's possible to pick up data from deleted files, so, panic the file server.
1.419 + Fault(EFatBadLocalDrive);
1.420 + }
1.421 + else
1.422 + {
1.423 + //-- if PlatSec is disabled, it's OK to ignore the NAND fault in release mode.
1.424 + __ASSERT_DEBUG(0, Fault(EFatBadLocalDrive));
1.425 + }
1.426 + }
1.427 +
1.428 +
1.429 + deleteLen = 0;
1.430 + }
1.431 +
1.432 + }
1.433 +
1.434 + //-- empty the array.
1.435 + aFreedClusters.Reset();
1.436 +}
1.437 +
1.438 +//-----------------------------------------------------------------------------
1.439 +/**
1.440 +Mark a chain of clusters as free in the FAT.
1.441 +
1.442 +@param aCluster Start cluster of cluster chain to free
1.443 +@leave System wide error codes
1.444 +*/
1.445 +void CFatTable::FreeClusterListL(TUint32 aCluster)
1.446 + {
1.447 + __PRINT1(_L("CFatTable::FreeClusterListL startCluster=%d"),aCluster);
1.448 + if (aCluster == KSpareCluster)
1.449 + return;
1.450 +
1.451 + //-- here we can store array of freed cluster numbers in order to
1.452 + //-- notify media drive about the media addresses marked as "invalid"
1.453 + RClusterArray deletedClusters;
1.454 + CleanupClosePushL(deletedClusters);
1.455 +
1.456 + //-- if ETrue, we need to notify media driver about invalidated media addressses
1.457 + const TBool bFreeClustersNotify = iMediaAtt & KMediaAttDeleteNotify;
1.458 +
1.459 + //-- this is a maximal number of FAT entries in the deletedClusters array.
1.460 + //-- as soon as we collect this number of entries in the array, FAT cache will be flushed
1.461 + //-- and driver notified. The array will be emptied. Used to avoid huge array when deleting
1.462 + //-- large files on NAND media
1.463 + const TUint KSubListLen = 4096;
1.464 + ASSERT(IsPowerOf2(KSubListLen));
1.465 +
1.466 + TUint32 lastKnownFreeCluster = FreeClusterHint();
1.467 + TUint32 cntFreedClusters = 0;
1.468 +
1.469 + TUint32 currCluster = aCluster;
1.470 + TInt nextCluster = aCluster;
1.471 +
1.472 + for(;;)
1.473 + {
1.474 + const TBool bEOF = !GetNextClusterL(nextCluster);
1.475 + WriteL(currCluster, KSpareCluster);
1.476 +
1.477 + lastKnownFreeCluster = Min(currCluster, lastKnownFreeCluster);
1.478 +
1.479 + // Keep a record of the deleted clusters so that we can subsequently notify the media driver. This is only safe
1.480 + // to do once the FAT changes have been written to disk.
1.481 + if(bFreeClustersNotify)
1.482 + deletedClusters.Append(currCluster);
1.483 +
1.484 + ++cntFreedClusters;
1.485 + currCluster = nextCluster;
1.486 +
1.487 + if (bEOF || aCluster == KSpareCluster)
1.488 + break;
1.489 +
1.490 + if(bFreeClustersNotify && cntFreedClusters && (cntFreedClusters & (KSubListLen-1))==0)
1.491 + {//-- reached a limit of the entries in the array. Flush FAT cache, notify the driver and empty the array.
1.492 + IncrementFreeClusterCount(cntFreedClusters);
1.493 + cntFreedClusters = 0;
1.494 +
1.495 + SetFreeClusterHint(lastKnownFreeCluster);
1.496 + DoFreedClustersNotify(deletedClusters);
1.497 + }
1.498 +
1.499 + }
1.500 +
1.501 + //-- increase the number of free clusters and notify the driver if required.
1.502 + IncrementFreeClusterCount(cntFreedClusters);
1.503 + SetFreeClusterHint(lastKnownFreeCluster);
1.504 +
1.505 + if(bFreeClustersNotify)
1.506 + DoFreedClustersNotify(deletedClusters);
1.507 +
1.508 + CleanupStack::PopAndDestroy(&deletedClusters);
1.509 + }
1.510 +
1.511 +//-----------------------------------------------------------------------------
1.512 +
1.513 +/**
1.514 +Find a free cluster nearest to aCluster, Always checks to the right of aCluster first
1.515 +but checks in both directions in the Fat.
1.516 +
1.517 +@param aCluster Cluster to find nearest free cluster to.
1.518 +@leave KErrDiskFull + system wide error codes
1.519 +@return cluster number found
1.520 +*/
1.521 +TUint32 CFatTable::FindClosestFreeClusterL(TUint32 aCluster)
1.522 + {
1.523 + __PRINT2(_L("CFatTable::FindClosestFreeClusterL() drv:%d cl:%d"),iOwner->DriveNumber(),aCluster);
1.524 +
1.525 + if(!ClusterNumberValid(aCluster))
1.526 + {
1.527 + ASSERT(0);
1.528 + User::Leave(KErrCorrupt);
1.529 + }
1.530 +
1.531 +
1.532 + if(iFreeClusters==0)
1.533 + {//-- there is no at least 1 free cluster available
1.534 + __PRINT(_L("CFatTable::FindClosestFreeClusterL() leaving KErrDiskFull #1"));
1.535 + User::Leave(KErrDiskFull);
1.536 + }
1.537 +
1.538 + //-- 1. look if the given index contains a free entry
1.539 + if(ReadL(aCluster) != KSpareCluster)
1.540 + {//-- no, it doesn't...
1.541 +
1.542 + //-- 2. look in both directions starting from the aCluster, looking in the right direction first
1.543 +
1.544 + const TUint32 maxEntries = MaxEntries();
1.545 + const TUint32 MinIdx = KFatFirstSearchCluster;
1.546 + const TUint32 MaxIdx = maxEntries-1;
1.547 +
1.548 + TBool canGoRight = ETrue;
1.549 + TBool canGoLeft = ETrue;
1.550 +
1.551 + TUint32 rightIdx = aCluster;
1.552 + TUint32 leftIdx = aCluster;
1.553 +
1.554 + for(TUint i=0; i<maxEntries; ++i)
1.555 + {
1.556 + if(canGoRight)
1.557 + {
1.558 + if(rightIdx < MaxIdx)
1.559 + ++rightIdx;
1.560 + else
1.561 + canGoRight = EFalse;
1.562 + }
1.563 +
1.564 + if(canGoLeft)
1.565 + {
1.566 + if(leftIdx > MinIdx)
1.567 + --leftIdx;
1.568 + else
1.569 + canGoLeft = EFalse;
1.570 + }
1.571 +
1.572 + if(!canGoRight && !canGoLeft)
1.573 + {
1.574 + __PRINT(_L("CFatTable::FindClosestFreeClusterL() leaving KErrDiskFull #2"));
1.575 + User::Leave(KErrDiskFull);
1.576 + }
1.577 +
1.578 + if (canGoRight && ReadL(rightIdx) == KSpareCluster)
1.579 + {
1.580 + aCluster = rightIdx;
1.581 + break;
1.582 + }
1.583 +
1.584 + if (canGoLeft && ReadL(leftIdx) == KSpareCluster)
1.585 + {
1.586 + aCluster = leftIdx;
1.587 + break;
1.588 + }
1.589 + }//for(..)
1.590 +
1.591 + }//if(ReadL(aCluster) != KSpareCluster)
1.592 +
1.593 +
1.594 + //-- note: do not update free cluster hint here by calling SetFreeClusterHint(). This is going to be
1.595 + //-- expensive especially if overridden methods with synchronisation are called. Instead, set the number of
1.596 + //-- the last known free cluster in the caller of this internal method.
1.597 +
1.598 +// __PRINT1(_L("CFatTable::FindClosestFreeClusterL found:%d"),aCluster);
1.599 +
1.600 + return aCluster;
1.601 + }
1.602 +
1.603 +//-----------------------------------------------------------------------------
1.604 +
1.605 +/**
1.606 + Converts a cluster number to byte offset in the FAT
1.607 +
1.608 +@param aFatIndex Cluster number
1.609 + @return Number of bytes from the beginning of the FAT
1.610 +*/
1.611 +TUint32 CFatTable::PosInBytes(TUint32 aFatIndex) const
1.612 + {
1.613 + switch(FatType())
1.614 + {
1.615 + case EFat12:
1.616 + return (((aFatIndex>>1)<<1) + (aFatIndex>>1)); //-- 1.5 bytes per FAT entry
1.617 +
1.618 + case EFat16:
1.619 + return aFatIndex<<1; //-- 2 bytes per FAT entry
1.620 +
1.621 + default:
1.622 + ASSERT(0);
1.623 + return 0;//-- get rid of warning
1.624 + };
1.625 +
1.626 + }
1.627 +
1.628 +//-----------------------------------------------------------------------------
1.629 +
1.630 +/**
1.631 + Checks if we have at least aClustersRequired clusters free in the FAT.
1.632 + This is, actually a dummy implementation.
1.633 +
1.634 + @param aClustersRequired number of free clusters required
1.635 + @return ETrue if there is at least aClustersRequired free clusters available, EFalse otherwise.
1.636 +*/
1.637 +TBool CFatTable::RequestFreeClusters(TUint32 aClustersRequired) const
1.638 +{
1.639 + //ASSERT(aClustersRequired >0 && aClustersRequired <= iOwner->UsableClusters());
1.640 + ASSERT(aClustersRequired >0);
1.641 + return (NumberOfFreeClusters() >= aClustersRequired);
1.642 +}
1.643 +
1.644 +//-----------------------------------------------------------------------------
1.645 +/**
1.646 + @return ETrue if the cluster number aClusterNo is valid, i.e. belongs to the FAT table
1.647 +*/
1.648 +TBool CFatTable::ClusterNumberValid(TUint32 aClusterNo) const
1.649 + {
1.650 + return (aClusterNo >= KFatFirstSearchCluster) && (aClusterNo < iMaxEntries);
1.651 + }
1.652 +
1.653 +
1.654 +
1.655 +//#######################################################################################################################################
1.656 +//# CAtaFatTable class implementation
1.657 +//#######################################################################################################################################
1.658 +
1.659 +/**
1.660 +Constructor
1.661 +*/
1.662 +CAtaFatTable::CAtaFatTable(CFatMountCB& aOwner)
1.663 + :CFatTable(aOwner)
1.664 + {
1.665 + }
1.666 +
1.667 +
1.668 +/** factory method */
1.669 +CAtaFatTable* CAtaFatTable::NewL(CFatMountCB& aOwner)
1.670 +{
1.671 + __PRINT1(_L("CAtaFatTable::NewL() drv:%d"),aOwner.DriveNumber());
1.672 + CAtaFatTable* pSelf = new (ELeave) CAtaFatTable(aOwner);
1.673 +
1.674 + CleanupStack::PushL(pSelf);
1.675 + pSelf->InitializeL();
1.676 + CleanupStack::Pop();
1.677 +
1.678 + return pSelf;
1.679 +}
1.680 +
1.681 +
1.682 +//---------------------------------------------------------------------------------------------------------------------------------------
1.683 +
1.684 +/**
1.685 + CAtaFatTable's FAT cache factory method.
1.686 + Creates fixed cache for FAT12 or FAT16
1.687 +*/
1.688 +void CAtaFatTable::CreateCacheL()
1.689 +{
1.690 + ASSERT(iOwner);
1.691 + const TUint32 fatSize=iOwner->FatSizeInBytes();
1.692 + __PRINT3(_L("CAtaFatTable::CreateCacheL drv:%d, FAT:%d, FAT Size:%d"), iOwner->DriveNumber(), FatType(), fatSize);
1.693 +
1.694 +
1.695 + //-- according to FAT specs:
1.696 + //-- FAT12 max size is 4084 entries or 6126 bytes => create fixed cache for whole FAT
1.697 + //-- FAT16 min size is 4085 entries or 8170 bytes, max size is 65525 entries or 131048 bytes => create fixed cache for whole FAT
1.698 +
1.699 + ASSERT(!iCache);
1.700 +
1.701 + //-- this is used for chaches granularity sanity check
1.702 + const TUint32 KMaxGranularityLog2 = 18; //-- 256K is a maximal allowed granularity
1.703 + const TUint32 KMinGranularityLog2 = KDefSectorSzLog2; //-- 512 bytes is a minimal allowed granularity
1.704 +
1.705 + switch(FatType())
1.706 + {
1.707 + case EFat12: //-- create fixed FAT12 cache
1.708 + iCache = CFat12Cache::NewL(iOwner, fatSize);
1.709 + break;
1.710 +
1.711 + case EFat16: //-- create fixed FAT16 cache
1.712 + {
1.713 + TUint32 fat16_ReadGranularity_Log2; //-- FAT16 cache read granularity Log2
1.714 + TUint32 fat16_WriteGranularity_Log2;//-- FAT16 cache write granularity Log2
1.715 +
1.716 + iOwner->FatConfig().Fat16FixedCacheParams(fat16_ReadGranularity_Log2, fat16_WriteGranularity_Log2);
1.717 +
1.718 + //-- check if granularity values look sensible
1.719 + const TBool bParamsValid = fat16_ReadGranularity_Log2 >= KMinGranularityLog2 && fat16_ReadGranularity_Log2 <= KMaxGranularityLog2 &&
1.720 + fat16_WriteGranularity_Log2 >= KMinGranularityLog2 && fat16_WriteGranularity_Log2 <= KMaxGranularityLog2;
1.721 +
1.722 + __ASSERT_ALWAYS(bParamsValid, Fault(EFatCache_BadGranularity));
1.723 +
1.724 +
1.725 + iCache = CFat16FixedCache::NewL(iOwner, fatSize, fat16_ReadGranularity_Log2, fat16_WriteGranularity_Log2);
1.726 + }
1.727 + break;
1.728 +
1.729 + default:
1.730 + ASSERT(0);
1.731 + User::Leave(KErrCorrupt);
1.732 + break;
1.733 + };
1.734 +
1.735 + ASSERT(iCache);
1.736 +}
1.737 +
1.738 +//---------------------------------------------------------------------------------------------------------------------------------------
1.739 +
1.740 +
1.741 +/**
1.742 + Flush the FAT cache on disk
1.743 +@leave System wide error codes
1.744 +*/
1.745 +void CAtaFatTable::FlushL()
1.746 + {
1.747 + //-- the data can't be written if the mount is inconsistent
1.748 + iOwner->CheckStateConsistentL();
1.749 +
1.750 + if (iCache)
1.751 + iCache->FlushL();
1.752 + }
1.753 +
1.754 +/**
1.755 +Clear any cached data
1.756 + @param aDiscardDirtyData if ETrue, non-flushed data in the cache will be discarded.
1.757 +*/
1.758 +void CAtaFatTable::Dismount(TBool aDiscardDirtyData)
1.759 + {
1.760 + if (iCache)
1.761 + {
1.762 + //-- cache's Close() can check if the cache is clean.
1.763 + //-- ignore dirty data in cache if the mount is not in consistent state (it's impossible to flush cache data)
1.764 + //-- or if we are asked to do so.
1.765 + const TBool bIgnoreDirtyData = aDiscardDirtyData || !iOwner->ConsistentState();
1.766 + iCache->Close(bIgnoreDirtyData);
1.767 +
1.768 + delete iCache;
1.769 + iCache=NULL;
1.770 + }
1.771 +
1.772 + }
1.773 +
1.774 +//---------------------------------------------------------------------------------------------------------------------------------------
1.775 +
1.776 +/**
1.777 + Invalidate whole FAT cache.
1.778 + Depending of cache type this may just mark cache invalid with reading on demand or re-read whole cache from the media
1.779 +*/
1.780 +void CAtaFatTable::InvalidateCacheL()
1.781 +{
1.782 + __PRINT1(_L("CAtaFatTable::InvalidateCache(), drv:%d"), iOwner->DriveNumber());
1.783 +
1.784 + //-- if we have a cache, invalidate it entirely
1.785 + if(iCache)
1.786 + {
1.787 + User::LeaveIfError(iCache->Invalidate());
1.788 + }
1.789 +}
1.790 +
1.791 +
1.792 +//---------------------------------------------------------------------------------------------------------------------------------------
1.793 +
1.794 +/**
1.795 + Invalidate specified region of the FAT cache
1.796 + Depending of cache type this may just mark part of the cache invalid with reading on demand later
1.797 + or re-read whole cache from the media.
1.798 +
1.799 + @param aPos absolute media position where the region being invalidated starts.
1.800 + @param aLength length in bytes of region to invalidate / refresh
1.801 +*/
1.802 +void CAtaFatTable::InvalidateCacheL(TInt64 aPos, TUint32 aLength)
1.803 + {
1.804 + __PRINT3(_L("CAtaFatTable::InvalidateCacheL() drv:%d, pos:%LU, len:%u,"), iOwner->DriveNumber(), aPos, aLength);
1.805 +
1.806 + if(I64HIGH(aPos) || !aLength || I64HIGH(aPos+aLength))
1.807 + return; //-- FAT tables can't span over 4G
1.808 +
1.809 + const TUint32 mediaPos32 = I64LOW(aPos);
1.810 +
1.811 + //-- we do not use other copies of FAT, so trach changes only in FAT1
1.812 + const TUint32 fat1StartPos = iOwner->StartOfFatInBytes();
1.813 + const TUint32 fat1EndPos = fat1StartPos + iOwner->FatSizeInBytes();
1.814 +
1.815 + TUint32 invRegionPosStart = 0; //-- media pos where the invalidated region starts
1.816 + TUint32 invRegionLen = 0; //-- size of the invalidated region, bytes
1.817 +
1.818 + //-- calculate the FAT1 region being invalidated
1.819 + if(mediaPos32 < fat1StartPos)
1.820 + {
1.821 + if((mediaPos32 + aLength) <= fat1StartPos)
1.822 + return;
1.823 +
1.824 + invRegionPosStart = fat1StartPos;
1.825 + invRegionLen = aLength - (fat1StartPos-mediaPos32);
1.826 + }
1.827 + else //if(mediaPos32 < fat1StartPos)
1.828 + {//-- mediaPos32 >= fat1StartPos)
1.829 + if(mediaPos32 >= fat1EndPos)
1.830 + return;
1.831 +
1.832 + invRegionPosStart = mediaPos32;
1.833 +
1.834 + if((mediaPos32 + aLength) <= fat1EndPos)
1.835 + {
1.836 + invRegionLen = aLength;
1.837 + }
1.838 + else
1.839 + {
1.840 + invRegionLen = mediaPos32+aLength-fat1EndPos;
1.841 + }
1.842 + }
1.843 +
1.844 + //-- convert the media pos of the region into FAT entries basis, depending on the FAT type
1.845 + ASSERT(invRegionPosStart >= fat1StartPos && invRegionLen <= (TUint)iOwner->FatSizeInBytes());
1.846 +
1.847 + TUint32 startFatEntry=0;
1.848 + TUint32 numEntries = 0;
1.849 +
1.850 + switch(FatType())
1.851 + {
1.852 + case EFat12:
1.853 + //-- invalidate whole cache; it is not worth making calculations for such small memory region.
1.854 + User::LeaveIfError(iCache->Invalidate());
1.855 + return;
1.856 +
1.857 + case EFat16:
1.858 + startFatEntry = (invRegionPosStart-fat1StartPos) >> KFat16EntrySzLog2;
1.859 + numEntries = (invRegionLen + (sizeof(TFat16Entry)-1)) >> KFat16EntrySzLog2;
1.860 + break;
1.861 +
1.862 + default:
1.863 + ASSERT(0);
1.864 + return;
1.865 + };
1.866 +
1.867 + if(startFatEntry < KFatFirstSearchCluster)
1.868 + {//-- FAT[0] and FAT[1] can't be legally accessed, they are reserved entries. We need to adjust region being refreshed.
1.869 + if(numEntries <= KFatFirstSearchCluster)
1.870 + return; //-- nothing to refresh
1.871 +
1.872 + startFatEntry += KFatFirstSearchCluster;
1.873 + numEntries -= KFatFirstSearchCluster;
1.874 + }
1.875 +
1.876 + User::LeaveIfError(iCache->InvalidateRegion(startFatEntry, numEntries));
1.877 + }
1.878 +
1.879 +
1.880 +//-----------------------------------------------------------------------------
1.881 +/**
1.882 + Initialize the object, create FAT cache if required
1.883 +@leave KErrNoMemory
1.884 +*/
1.885 +void CAtaFatTable::InitializeL()
1.886 + {
1.887 + __PRINT1(_L("CAtaFatTable::InitializeL() drv:%d"), iOwner->DriveNumber());
1.888 + CFatTable::InitializeL();
1.889 +
1.890 + //-- create the FAT cache.
1.891 + ASSERT(!iCache);
1.892 + CreateCacheL();
1.893 + }
1.894 +
1.895 +
1.896 +//-----------------------------------------------------------------------------
1.897 +/**
1.898 + Remount the FAT table. This method call means that the media parameters wasn't changed,
1.899 + otherwise CFatMountCB::DoReMountL() would reject it.
1.900 + Just do some re-initialisation work.
1.901 +*/
1.902 +void CAtaFatTable::ReMountL()
1.903 +{
1.904 + __PRINT1(_L("CAtaFatTable::ReMountL() drv:%d"), iOwner->DriveNumber());
1.905 +
1.906 + if(iCache)
1.907 + {
1.908 + iCache->Invalidate();
1.909 + }
1.910 + else
1.911 + {
1.912 + //-- this situation can happen when someone called CAtaFatTable::Dismount() that deletes the cache object
1.913 + //-- and then ReMount happens. We need to re-initialise this object.
1.914 + InitializeL();
1.915 + }
1.916 +}
1.917 +
1.918 +
1.919 +//-----------------------------------------------------------------------------
1.920 +/**
1.921 + Read an entry from the FAT table
1.922 +
1.923 + @param aFatIndex FAT entry number to read
1.924 + @return FAT entry value
1.925 +*/
1.926 +TUint32 CAtaFatTable::ReadL(TUint32 aFatIndex) const
1.927 + {
1.928 + if(!ClusterNumberValid(aFatIndex))
1.929 + {
1.930 + //ASSERT(0); //-- for some silly reason some callers pass 0 here and expect it to leave
1.931 + User::Leave(KErrCorrupt);
1.932 + }
1.933 +
1.934 +
1.935 + const TUint entry = iCache->ReadEntryL(aFatIndex);
1.936 + return entry;
1.937 + }
1.938 +
1.939 +
1.940 +//-----------------------------------------------------------------------------
1.941 +/**
1.942 + Write an entry to the FAT table
1.943 +
1.944 + @param aFatIndex aFatIndex FAT entry number to write
1.945 + @param aValue FAT entry to write
1.946 +@leave
1.947 +*/
1.948 +void CAtaFatTable::WriteL(TUint32 aFatIndex, TUint32 aValue)
1.949 + {
1.950 + const TUint32 KFat16EntryMask = 0x0FFFF;
1.951 +
1.952 + __PRINT2(_L("CAtaFatTable::WriteL() entry:%d, val:0x%x"), aFatIndex, aValue);
1.953 +
1.954 + if(!ClusterNumberValid(aFatIndex))
1.955 + {
1.956 + ASSERT(0);
1.957 + User::Leave(KErrCorrupt);
1.958 + }
1.959 +
1.960 + if(aValue != KSpareCluster && (aValue < KFatFirstSearchCluster || aValue > KFat16EntryMask))
1.961 + {
1.962 + ASSERT(0);
1.963 + User::Leave(KErrCorrupt);
1.964 + }
1.965 + iCache->WriteEntryL(aFatIndex, aValue);
1.966 + }
1.967 +
1.968 +
1.969 +/**
1.970 +Get the next cluster in the chain from the FAT
1.971 +
1.972 +@param aCluster number to read, contains next cluster upon return
1.973 +@leave
1.974 +@return False if end of cluster chain
1.975 +*/
1.976 +TBool CFatTable::GetNextClusterL(TInt& aCluster) const
1.977 + {
1.978 + __PRINT1(_L("CAtaFatTable::GetNextClusterL(%d)"), aCluster);
1.979 +
1.980 + const TInt nextCluster = ReadL(aCluster);
1.981 + TBool ret = EFalse;
1.982 +
1.983 + switch(FatType())
1.984 + {
1.985 + case EFat12:
1.986 + ret=!IsEof12Bit(nextCluster);
1.987 + break;
1.988 +
1.989 + case EFat16:
1.990 + ret=!IsEof16Bit(nextCluster);
1.991 + break;
1.992 +
1.993 + default:
1.994 + ASSERT(0);
1.995 + return EFalse;//-- get rid of warning
1.996 + };
1.997 +
1.998 + if (ret)
1.999 + {
1.1000 + aCluster=nextCluster;
1.1001 + }
1.1002 +
1.1003 + return ret;
1.1004 +
1.1005 + }
1.1006 +
1.1007 +/**
1.1008 +Write EOF to aFatIndex
1.1009 + @param aFatIndex index in FAT (cluster number) to be written
1.1010 +*/
1.1011 +void CFatTable::WriteFatEntryEofL(TUint32 aFatIndex)
1.1012 + {
1.1013 + __PRINT1(_L("CAtaFatTable::WriteFatEntryEofL(%d)"), aFatIndex);
1.1014 +
1.1015 + //-- use EOF_16Bit (0x0ffff) for all types of FAT, FAT cache will mask it appropriately
1.1016 + WriteL(aFatIndex, EOF_16Bit);
1.1017 + }
1.1018 +
1.1019 +
1.1020 +
1.1021 +/**
1.1022 + Mark cluster number aFatIndex in FAT as bad
1.1023 + @param aFatIndex index in FAT (cluster number) to be written
1.1024 +*/
1.1025 +void CFatTable::MarkAsBadClusterL(TUint32 aFatIndex)
1.1026 + {
1.1027 + __PRINT1(_L("CAtaFatTable::MarkAsBadClusterL(%d)"),aFatIndex);
1.1028 +
1.1029 + //-- use KBad_16Bit (0x0fff7) for all types of FAT, FAT cache will mask it appropriately
1.1030 + WriteL(aFatIndex, KBad_16Bit);
1.1031 +
1.1032 + FlushL();
1.1033 + }
1.1034 +
1.1035 +
1.1036 +/**
1.1037 +Return the location of a Cluster in the data section of the media
1.1038 +
1.1039 +@param aCluster to find location of
1.1040 +@return Byte offset of the cluster data
1.1041 +*/
1.1042 +TInt64 CAtaFatTable::DataPositionInBytes(TUint32 aCluster) const
1.1043 + {
1.1044 + __ASSERT_DEBUG(ClusterNumberValid(aCluster), Fault(EFatTable_InvalidIndex));
1.1045 +
1.1046 + const TInt clusterBasePosition=iOwner->ClusterBasePosition();
1.1047 + return(((TInt64(aCluster)-KFatFirstSearchCluster) << iOwner->ClusterSizeLog2()) + clusterBasePosition);
1.1048 + }
1.1049 +
1.1050 +
1.1051 +
1.1052 +
1.1053 +
1.1054 +