1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/kernelhwsrv/userlibandfileserver/fileserver/sfat32/fat_table32.cpp Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,2594 @@
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\sfat32\fat_table32.cpp
1.18 +// FAT32 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 +#include "sl_std.h"
1.30 +#include "sl_fatcache32.h"
1.31 +#include "fat_table32.h"
1.32 +
1.33 +
1.34 +
1.35 +
1.36 +//---------------------------------------------------------------------------------------------------------------------------------------
1.37 +/**
1.38 + Implements automatic locking object.
1.39 + Calls TDriveInterface::AcquireLock() on construction and TDriveInterface::ReleaseLock() on destruction.
1.40 + Can be constructed on the stack only.
1.41 +*/
1.42 +class XAutoLock
1.43 + {
1.44 + public:
1.45 + inline XAutoLock(CFatMountCB* apOwner) : iDrv(apOwner->DriveInterface()) {iDrv.AcquireLock();}
1.46 + inline XAutoLock(TDriveInterface& aDrv) : iDrv(aDrv) {iDrv.AcquireLock();}
1.47 + inline ~XAutoLock() {iDrv.ReleaseLock();}
1.48 +
1.49 + private:
1.50 + void* operator new(TUint); //-- disable creating objects on heap.
1.51 + void* operator new(TUint, void*);
1.52 +
1.53 + private:
1.54 + TDriveInterface &iDrv; ///< reference to the drive interface
1.55 + };
1.56 +
1.57 +
1.58 +//---------------------------------------------------------------------------------------------------------------------------------------
1.59 +
1.60 +
1.61 +
1.62 +//#######################################################################################################################################
1.63 +//# CFatTable class implementation
1.64 +//#######################################################################################################################################
1.65 +
1.66 +/**
1.67 + FAT object factory method.
1.68 + Constructs either CAtaFatTable or CRamFatTable depending on the media type parameter
1.69 +
1.70 + @param aOwner Pointer to the owning mount
1.71 + @param aLocDrvCaps local drive attributes
1.72 + @leave KErrNoMemory
1.73 + @return Pointer to the Fat table
1.74 +*/
1.75 +CFatTable* CFatTable::NewL(CFatMountCB& aOwner, const TLocalDriveCaps& aLocDrvCaps)
1.76 + {
1.77 + CFatTable* pFatTable=NULL;
1.78 +
1.79 + switch(aLocDrvCaps.iType)
1.80 + {
1.81 + case EMediaRam:
1.82 + {//-- this is RAM media, try to create CRamFatTable instance.
1.83 + const TFatType fatType = aOwner.FatType();
1.84 +
1.85 + if(fatType != EFat16 && fatType != EFat32)
1.86 + {//-- CRamFatTable doesn't support FAT12, FAT16 & FAT32 only.
1.87 + __PRINT1(_L("CFatTable::NewL() CRamFatTable doesn't support this FAT type:%d"), fatType);
1.88 + ASSERT(0);
1.89 + return NULL;
1.90 + }
1.91 +
1.92 + pFatTable = CRamFatTable::NewL(aOwner);
1.93 + }
1.94 + break;
1.95 +
1.96 + default:
1.97 + //-- other media
1.98 + pFatTable = CAtaFatTable::NewL(aOwner);
1.99 + break;
1.100 + };
1.101 +
1.102 + return pFatTable;
1.103 + }
1.104 +
1.105 +
1.106 +CFatTable::CFatTable(CFatMountCB& aOwner)
1.107 +{
1.108 + iOwner = &aOwner;
1.109 + ASSERT(iOwner);
1.110 +}
1.111 +
1.112 +CFatTable::~CFatTable()
1.113 +{
1.114 + //-- destroy cache ignoring dirty data in cache
1.115 + //-- the destructor isn't an appropriate place to flush the data.
1.116 + Dismount(ETrue);
1.117 +}
1.118 +
1.119 +//-----------------------------------------------------------------------------
1.120 +
1.121 +/**
1.122 + Initialise the object, get data from the owning CFatMountCB
1.123 +*/
1.124 +void CFatTable::InitializeL()
1.125 + {
1.126 + ASSERT(iOwner);
1.127 +
1.128 + //-- get FAT type from the owner
1.129 + iFatType = iOwner->FatType();
1.130 + ASSERT(IsFat12() || IsFat16() || IsFat32());
1.131 +
1.132 + //-- set the EOC code
1.133 + iFatEocCode = EocCodeByFatType(iFatType);
1.134 +
1.135 +
1.136 +
1.137 +
1.138 + iFreeClusterHint = KFatFirstSearchCluster;
1.139 +
1.140 + //-- cache the media attributes
1.141 + TLocalDriveCapsV2 caps;
1.142 + TPckg<TLocalDriveCapsV2> capsPckg(caps);
1.143 + User::LeaveIfError(iOwner->LocalDrive()->Caps(capsPckg));
1.144 + iMediaAtt = caps.iMediaAtt;
1.145 +
1.146 + //-- obtain maximal number of entries in the table
1.147 + iMaxEntries = iOwner->UsableClusters()+KFatFirstSearchCluster; //-- FAT[0] & FAT[1] are not in use
1.148 +
1.149 + __PRINT3(_L("CFatTable::InitializeL(), drv:%d, iMediaAtt = %08X, max Entries:%d"), iOwner->DriveNumber(), iMediaAtt, iMaxEntries);
1.150 + }
1.151 +
1.152 +//-----------------------------------------------------------------------------
1.153 +
1.154 +/**
1.155 + Decrements the free cluster count.
1.156 + Note that can be quite expensive operation (especially for overrides with synchronisation), if it is called for every
1.157 + cluster of a large file. Use more than one cluster granularity.
1.158 +
1.159 + @param aCount a number of clusters
1.160 +*/
1.161 +void CFatTable::DecrementFreeClusterCount(TUint32 aCount)
1.162 + {
1.163 + __ASSERT_DEBUG(iFreeClusters >= aCount, Fault(EFatCorrupt));
1.164 + iFreeClusters -= aCount;
1.165 + }
1.166 +
1.167 +/**
1.168 + Increments the free cluster count.
1.169 + Note that can be quite expensive operation (especially for overrides with synchronisation), if it is called for every
1.170 + cluster of a large file. Use more than one cluster granularity.
1.171 +
1.172 + @param aCount a number of clusters
1.173 +*/
1.174 +void CFatTable::IncrementFreeClusterCount(TUint32 aCount)
1.175 + {
1.176 + const TUint32 newVal = iFreeClusters+aCount;
1.177 + __ASSERT_DEBUG(newVal<=MaxEntries(), Fault(EFatCorrupt));
1.178 +
1.179 + iFreeClusters = newVal;
1.180 + }
1.181 +
1.182 +/** @return number of free clusters in the FAT */
1.183 +TUint32 CFatTable::NumberOfFreeClusters(TBool /*aSyncOperation=EFalse*/) const
1.184 + {
1.185 + return FreeClusters();
1.186 + }
1.187 +
1.188 +void CFatTable::SetFreeClusters(TUint32 aFreeClusters)
1.189 + {
1.190 + iFreeClusters=aFreeClusters;
1.191 + }
1.192 +
1.193 +/**
1.194 + Get the hint about the last known free cluster number.
1.195 + Note that can be quite expensive operation (especially for overrides with synchronisation), if it is called for every
1.196 + cluster of a large file.
1.197 +
1.198 + @return cluster number supposedly close to the free one.
1.199 +*/
1.200 +TUint32 CFatTable::FreeClusterHint() const
1.201 + {
1.202 + ASSERT(ClusterNumberValid(iFreeClusterHint));
1.203 + return iFreeClusterHint;
1.204 + }
1.205 +
1.206 +/**
1.207 + Set a free cluster hint. The next search fro the free cluster can start from this value.
1.208 + 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.209 + free entries chain.
1.210 + Note that can be quite expensive operation (especially for overrides with synchronisation), if it is called for every
1.211 + cluster of a large file.
1.212 +
1.213 + @param aCluster cluster number hint.
1.214 +*/
1.215 +void CFatTable::SetFreeClusterHint(TUint32 aCluster)
1.216 + {
1.217 + ASSERT(ClusterNumberValid(aCluster));
1.218 + iFreeClusterHint=aCluster;
1.219 + }
1.220 +
1.221 +//-----------------------------------------------------------------------------
1.222 +
1.223 +/**
1.224 + Find out the number of free clusters on the volume.
1.225 + Reads whole FAT and counts free clusters.
1.226 +*/
1.227 +void CFatTable::CountFreeClustersL()
1.228 + {
1.229 + __PRINT1(_L("#- CFatTable::CountFreeClustersL(), drv:%d"), iOwner->DriveNumber());
1.230 +
1.231 + const TUint32 KUsableClusters = iOwner->UsableClusters();
1.232 + (void)KUsableClusters;
1.233 +
1.234 + TUint32 freeClusters = 0;
1.235 + TUint32 firstFreeCluster = 0;
1.236 +
1.237 + TTime timeStart;
1.238 + TTime timeEnd;
1.239 + timeStart.UniversalTime(); //-- take start time
1.240 +
1.241 + //-- walk through whole FAT table looking for free clusters
1.242 + for(TUint i=KFatFirstSearchCluster; i<MaxEntries(); ++i)
1.243 + {
1.244 + if(ReadL(i) == KSpareCluster)
1.245 + {//-- found a free cluster
1.246 + ++freeClusters;
1.247 +
1.248 + if(!firstFreeCluster)
1.249 + firstFreeCluster = i;
1.250 + }
1.251 + }
1.252 +
1.253 + timeEnd.UniversalTime(); //-- take end time
1.254 + const TInt msScanTime = (TInt)( (timeEnd.MicroSecondsFrom(timeStart)).Int64() / K1mSec);
1.255 + __PRINT1(_L("#- CFatTable::CountFreeClustersL() finished. Taken:%d ms"), msScanTime);
1.256 + (void)msScanTime;
1.257 +
1.258 + if(!firstFreeCluster) //-- haven't found free clusters on the volume
1.259 + firstFreeCluster = KFatFirstSearchCluster;
1.260 +
1.261 + ASSERT(freeClusters <= KUsableClusters);
1.262 +
1.263 + SetFreeClusters(freeClusters);
1.264 + SetFreeClusterHint(firstFreeCluster);
1.265 + }
1.266 +
1.267 +//-----------------------------------------------------------------------------
1.268 +
1.269 +/**
1.270 +Count the number of contiguous cluster from a start cluster
1.271 +
1.272 +@param aStartCluster cluster to start counting from
1.273 +@param anEndCluster contains the end cluster number upon return
1.274 +@param aMaxCount Maximum cluster required
1.275 +@leave System wide error values
1.276 +@return Number of contiguous clusters from aStartCluster.
1.277 +*/
1.278 +TInt CFatTable::CountContiguousClustersL(TUint32 aStartCluster,TInt& anEndCluster,TUint32 aMaxCount) const
1.279 + {
1.280 + __PRINT2(_L("CFatTable::CountContiguousClustersL() start:%d, max:%d"),aStartCluster, aMaxCount);
1.281 + TUint32 clusterListLen=1;
1.282 + TInt endCluster=aStartCluster;
1.283 + TInt64 endClusterPos=DataPositionInBytes(endCluster);
1.284 + while (clusterListLen<aMaxCount)
1.285 + {
1.286 + TInt oldCluster=endCluster;
1.287 + TInt64 oldClusterPos=endClusterPos;
1.288 + if (GetNextClusterL(endCluster)==EFalse || (endClusterPos=DataPositionInBytes(endCluster))!=(oldClusterPos+(1<<iOwner->ClusterSizeLog2())))
1.289 + {
1.290 + endCluster=oldCluster;
1.291 + break;
1.292 + }
1.293 + clusterListLen++;
1.294 + }
1.295 + anEndCluster=endCluster;
1.296 + return(clusterListLen);
1.297 + }
1.298 +
1.299 +//-----------------------------------------------------------------------------
1.300 +
1.301 +/**
1.302 + Extend a file or directory cluster chain, leaves if there are no free clusters (the disk is full).
1.303 +
1.304 + @param aNumber amount of clusters to allocate
1.305 + @param aCluster FAT entry index to start with.
1.306 +
1.307 + @leave KErrDiskFull + system wide error codes
1.308 +*/
1.309 +void CFatTable::ExtendClusterListL(TUint32 aNumber,TInt& aCluster)
1.310 + {
1.311 + __PRINT2(_L("CFatTable::ExtendClusterListL() num:%d, clust:%d"), aNumber, aCluster);
1.312 + __ASSERT_DEBUG(aNumber>0,Fault(EFatBadParameter));
1.313 +
1.314 + while(aNumber && GetNextClusterL(aCluster))
1.315 + aNumber--;
1.316 +
1.317 + if(!aNumber)
1.318 + return;
1.319 +
1.320 + if(!RequestFreeClusters(aNumber))
1.321 + {
1.322 + __PRINT(_L("CFatTable::ExtendClusterListL - leaving KErrDirFull"));
1.323 + User::Leave(KErrDiskFull);
1.324 + }
1.325 +
1.326 +
1.327 + TUint32 freeCluster = 0;
1.328 +
1.329 + //-- note: this can be impoved by trying to fing as long chain of free clusters as possible in FindClosestFreeClusterL()
1.330 + for(TUint i=0; i<aNumber; ++i)
1.331 + {
1.332 + freeCluster = FindClosestFreeClusterL(aCluster);
1.333 + WriteFatEntryEofL(freeCluster); // Must write EOF for FindClosestFreeCluster to work again
1.334 + WriteL(aCluster,freeCluster);
1.335 + aCluster=freeCluster;
1.336 + }
1.337 +
1.338 + //-- decrement number of available clusters
1.339 + DecrementFreeClusterCount(aNumber);
1.340 +
1.341 + //-- update free cluster hint, it isn't required to be a precise value, just a hint where to start the from from
1.342 + SetFreeClusterHint(aCluster);
1.343 +
1.344 + }
1.345 +
1.346 +//-----------------------------------------------------------------------------
1.347 +
1.348 +/**
1.349 + Allocate and mark as EOF a single cluster as close as possible to aNearestCluster
1.350 +
1.351 + @param aNearestCluster Cluster the new cluster should be nearest to
1.352 + @leave System wide error codes
1.353 + @return The cluster number allocated
1.354 +*/
1.355 +TUint32 CFatTable::AllocateSingleClusterL(TUint32 aNearestCluster)
1.356 + {
1.357 + __PRINT1(_L("CFatTable::AllocateSingleCluster() nearest:%d"), aNearestCluster);
1.358 +
1.359 + const TInt freeCluster=FindClosestFreeClusterL(aNearestCluster);
1.360 + WriteFatEntryEofL(freeCluster);
1.361 + DecrementFreeClusterCount(1);
1.362 +
1.363 + //-- update free cluster hint, it isn't required to be a precise value, just a hint where to start the from from.
1.364 + SetFreeClusterHint(freeCluster);
1.365 +
1.366 + return(freeCluster);
1.367 + }
1.368 +
1.369 +//-----------------------------------------------------------------------------
1.370 +
1.371 +/**
1.372 + Allocate and link a cluster chain, leaves if there are not enough free clusters.
1.373 + Chain starts as close as possible to aNearestCluster, last cluster will be marked as EOF.
1.374 +
1.375 + @param aNumber Number of clusters to allocate
1.376 + @param aNearestCluster Cluster the new chain should be nearest to
1.377 + @leave System wide error codes
1.378 + @return The first cluster number allocated
1.379 +*/
1.380 +TUint32 CFatTable::AllocateClusterListL(TUint32 aNumber, TUint32 aNearestCluster)
1.381 + {
1.382 + __PRINT2(_L("CFatTable::AllocateClusterList() N:%d,NearestCL:%d"),aNumber,aNearestCluster);
1.383 + __ASSERT_DEBUG(aNumber>0, Fault(EFatBadParameter));
1.384 +
1.385 + if(!RequestFreeClusters(aNumber))
1.386 + {
1.387 + __PRINT(_L("CFatTable::AllocateClusterListL - leaving KErrDirFull"));
1.388 + User::Leave(KErrDiskFull);
1.389 + }
1.390 +
1.391 + TInt firstCluster = aNearestCluster = AllocateSingleClusterL(aNearestCluster);
1.392 + if (aNumber>1)
1.393 + ExtendClusterListL(aNumber-1, (TInt&)aNearestCluster);
1.394 +
1.395 + return(firstCluster);
1.396 + }
1.397 +
1.398 +//-----------------------------------------------------------------------------
1.399 +
1.400 +/**
1.401 + Notify the media drive about media areas that shall be treated as "deleted" if this feature is supported.
1.402 + @param aFreedClusters array with FAT numbers of clusters that shall be marked as "deleted"
1.403 +*/
1.404 +void CFatTable::DoFreedClustersNotify(RClusterArray &aFreedClusters)
1.405 +{
1.406 + ASSERT(iMediaAtt & KMediaAttDeleteNotify);
1.407 +
1.408 + const TUint clusterCount = aFreedClusters.Count();
1.409 +
1.410 + if(!clusterCount)
1.411 + return;
1.412 +
1.413 + FlushL(); //-- Commit the FAT changes to disk first to be safe
1.414 +
1.415 + const TUint bytesPerCluster = 1 << iOwner->ClusterSizeLog2();
1.416 +
1.417 + TInt64 byteAddress = 0;
1.418 + TUint deleteLen = 0; // zero indicates no clusters accumulated yet
1.419 +
1.420 + for (TUint i=0; i<clusterCount; ++i)
1.421 + {
1.422 + const TUint currCluster = aFreedClusters[i];
1.423 +
1.424 + if (deleteLen == 0)
1.425 + byteAddress = DataPositionInBytes(currCluster); //-- start of the media range
1.426 +
1.427 + deleteLen += bytesPerCluster;
1.428 +
1.429 + //-- if this is the last entry in the array or the net cluster number is not consecutive, notify the driver
1.430 + if ((i+1) == clusterCount || aFreedClusters[i+1] != (currCluster+1))
1.431 + {
1.432 + //__PRINT3(_L("DeleteNotify(%08X:%08X, %u), first cluster %u last cluster #%u"), I64HIGH(byteAddress), I64LOW(byteAddress), deleteLen);
1.433 + //__PRINT2(_L(" first cluster %u last cluster #%u"), I64LOW((byteAddress - iOwner->ClusterBasePosition()) >> iOwner->ClusterSizeLog2()) + 2, cluster);
1.434 +
1.435 + const TInt r = iOwner->LocalDrive()->DeleteNotify(byteAddress, deleteLen);
1.436 + if(r != KErrNone)
1.437 + {//-- if DeleteNotify() failed, it means that something terribly wrong happened to the NAND media;
1.438 + //-- in normal circumstances it can not happen. One of the reasons: totally worn out media.
1.439 + const TBool platSecEnabled = PlatSec::ConfigSetting(PlatSec::EPlatSecEnforcement);
1.440 + __PRINT3(_L("CFatTable::DoFreedClustersNotify() DeleteNotify failure! drv:%d err:%d, PlatSec:%d"),iOwner->DriveNumber(), r, platSecEnabled);
1.441 +
1.442 + if(platSecEnabled)
1.443 + {
1.444 + //-- if PlatSec is enabled, we can't afford jeopardize the security; without DeleteNotify()
1.445 + //-- it's possible to pick up data from deleted files, so, panic the file server.
1.446 + Fault(EFatBadLocalDrive);
1.447 + }
1.448 + else
1.449 + {
1.450 + //-- if PlatSec is disabled, it's OK to ignore the NAND fault in release mode.
1.451 + __ASSERT_DEBUG(0, Fault(EFatBadLocalDrive));
1.452 + }
1.453 + }
1.454 +
1.455 +
1.456 + deleteLen = 0;
1.457 + }
1.458 +
1.459 + }
1.460 +
1.461 + //-- empty the array.
1.462 + aFreedClusters.Reset();
1.463 +}
1.464 +
1.465 +//-----------------------------------------------------------------------------
1.466 +/**
1.467 + Mark a chain of clusters as free in the FAT.
1.468 +
1.469 + @param aCluster Start cluster of cluster chain to free
1.470 + @leave System wide error codes
1.471 +*/
1.472 +void CFatTable::FreeClusterListL(TUint32 aCluster)
1.473 + {
1.474 + __PRINT1(_L("CFatTable::FreeClusterListL startCluster=%d"),aCluster);
1.475 + if (aCluster == KSpareCluster)
1.476 + return;
1.477 +
1.478 + //-- here we can store array of freed cluster numbers in order to
1.479 + //-- notify media drive about the media addresses marked as "invalid"
1.480 + RClusterArray deletedClusters;
1.481 + CleanupClosePushL(deletedClusters);
1.482 +
1.483 + //-- if ETrue, we need to notify media driver about invalidated media addressses
1.484 + const TBool bFreeClustersNotify = iMediaAtt & KMediaAttDeleteNotify;
1.485 +
1.486 + //-- this is a maximal number of FAT entries in the deletedClusters array.
1.487 + //-- as soon as we collect this number of entries in the array, FAT cache will be flushed
1.488 + //-- and driver notified. The array will be emptied. Used to avoid huge array when deleting
1.489 + //-- large files on NAND media
1.490 + const TUint KSubListLen = 4096;
1.491 + ASSERT(IsPowerOf2(KSubListLen));
1.492 +
1.493 + TUint32 lastKnownFreeCluster = FreeClusterHint();
1.494 + TUint32 cntFreedClusters = 0;
1.495 +
1.496 + TUint32 currCluster = aCluster;
1.497 + TInt nextCluster = aCluster;
1.498 +
1.499 + for(;;)
1.500 + {
1.501 + const TBool bEOF = !GetNextClusterL(nextCluster);
1.502 + WriteL(currCluster, KSpareCluster);
1.503 +
1.504 + lastKnownFreeCluster = Min(currCluster, lastKnownFreeCluster);
1.505 +
1.506 + // Keep a record of the deleted clusters so that we can subsequently notify the media driver. This is only safe
1.507 + // to do once the FAT changes have been written to disk.
1.508 + if(bFreeClustersNotify)
1.509 + deletedClusters.Append(currCluster);
1.510 +
1.511 + ++cntFreedClusters;
1.512 + currCluster = nextCluster;
1.513 +
1.514 + if (bEOF || aCluster == KSpareCluster)
1.515 + break;
1.516 +
1.517 + if(bFreeClustersNotify && cntFreedClusters && (cntFreedClusters & (KSubListLen-1))==0)
1.518 + {//-- reached a limit of the entries in the array. Flush FAT cache, notify the driver and empty the array.
1.519 + IncrementFreeClusterCount(cntFreedClusters);
1.520 + cntFreedClusters = 0;
1.521 +
1.522 + SetFreeClusterHint(lastKnownFreeCluster);
1.523 + DoFreedClustersNotify(deletedClusters);
1.524 + }
1.525 +
1.526 + }
1.527 +
1.528 + //-- increase the number of free clusters and notify the driver if required.
1.529 + IncrementFreeClusterCount(cntFreedClusters);
1.530 + SetFreeClusterHint(lastKnownFreeCluster);
1.531 +
1.532 + if(bFreeClustersNotify)
1.533 + DoFreedClustersNotify(deletedClusters);
1.534 +
1.535 + CleanupStack::PopAndDestroy(&deletedClusters);
1.536 + }
1.537 +
1.538 +//-----------------------------------------------------------------------------
1.539 +
1.540 +/**
1.541 + Find a free cluster nearest to aCluster, Always checks to the right of aCluster first
1.542 + but checks in both directions in the Fat.
1.543 +
1.544 + @param aCluster Cluster to find nearest free cluster to.
1.545 + @leave KErrDiskFull + system wide error codes
1.546 + @return cluster number found
1.547 +*/
1.548 +TUint32 CFatTable::FindClosestFreeClusterL(TUint32 aCluster)
1.549 + {
1.550 + __PRINT2(_L("CFatTable::FindClosestFreeClusterL() drv:%d cl:%d"),iOwner->DriveNumber(),aCluster);
1.551 +
1.552 + if(!ClusterNumberValid(aCluster))
1.553 + {
1.554 + ASSERT(0);
1.555 + User::Leave(KErrCorrupt);
1.556 + }
1.557 +
1.558 + if(!RequestFreeClusters(1))
1.559 + {//-- there is no at least 1 free cluster available
1.560 + __PRINT(_L("CFatTable::FindClosestFreeClusterL() leaving KErrDiskFull #1"));
1.561 + User::Leave(KErrDiskFull);
1.562 + }
1.563 +
1.564 + //-- 1. look if the given index contains a free entry
1.565 + if(ReadL(aCluster) != KSpareCluster)
1.566 + {//-- no, it doesn't...
1.567 +
1.568 + //-- 2. look in both directions starting from the aCluster, looking in the right direction first
1.569 +
1.570 + const TUint32 maxEntries = MaxEntries();
1.571 + const TUint32 MinIdx = KFatFirstSearchCluster;
1.572 + const TUint32 MaxIdx = maxEntries-1;
1.573 +
1.574 + TBool canGoRight = ETrue;
1.575 + TBool canGoLeft = ETrue;
1.576 +
1.577 + TUint32 rightIdx = aCluster;
1.578 + TUint32 leftIdx = aCluster;
1.579 +
1.580 + for(TUint i=0; i<maxEntries; ++i)
1.581 + {
1.582 + if(canGoRight)
1.583 + {
1.584 + if(rightIdx < MaxIdx)
1.585 + ++rightIdx;
1.586 + else
1.587 + canGoRight = EFalse;
1.588 + }
1.589 +
1.590 + if(canGoLeft)
1.591 + {
1.592 + if(leftIdx > MinIdx)
1.593 + --leftIdx;
1.594 + else
1.595 + canGoLeft = EFalse;
1.596 + }
1.597 +
1.598 + if(!canGoRight && !canGoLeft)
1.599 + {
1.600 + __PRINT(_L("CFatTable::FindClosestFreeClusterL() leaving KErrDiskFull #2"));
1.601 + User::Leave(KErrDiskFull);
1.602 + }
1.603 +
1.604 + if(canGoRight && ReadL(rightIdx) == KSpareCluster)
1.605 + {
1.606 + aCluster = rightIdx;
1.607 + break;
1.608 + }
1.609 +
1.610 + if (canGoLeft && ReadL(leftIdx) == KSpareCluster)
1.611 + {
1.612 + aCluster = leftIdx;
1.613 + break;
1.614 + }
1.615 + }//for(..)
1.616 +
1.617 + }//if(ReadL(aCluster) != KSpareCluster)
1.618 +
1.619 +
1.620 + //-- note: do not update free cluster hint here by calling SetFreeClusterHint(). This is going to be
1.621 + //-- expensive especially if overridden methods with synchronisation are called. Instead, set the number of
1.622 + //-- the last known free cluster in the caller of this internal method.
1.623 +
1.624 + //__PRINT1(_L("CFatTable::FindClosestFreeClusterL found:%d"),aCluster);
1.625 +
1.626 + return aCluster;
1.627 + }
1.628 +
1.629 +//-----------------------------------------------------------------------------
1.630 +
1.631 +/**
1.632 + Converts a cluster number to byte offset in the FAT
1.633 +
1.634 + @param aFatIndex Cluster number
1.635 + @return Number of bytes from the beginning of the FAT
1.636 +*/
1.637 +TUint32 CFatTable::PosInBytes(TUint32 aFatIndex) const
1.638 + {
1.639 + switch(FatType())
1.640 + {
1.641 + case EFat12:
1.642 + return (((aFatIndex>>1)<<1) + (aFatIndex>>1)); //-- 1.5 bytes per FAT entry
1.643 +
1.644 + case EFat16:
1.645 + return aFatIndex<<1; //-- 2 bytes per FAT entry
1.646 +
1.647 + case EFat32:
1.648 + return aFatIndex<<2; //-- 4 bytes per FAT entry
1.649 +
1.650 + default:
1.651 + ASSERT(0);
1.652 + return 0;//-- get rid of warning
1.653 + };
1.654 +
1.655 + }
1.656 +
1.657 +//-----------------------------------------------------------------------------
1.658 +
1.659 +/**
1.660 + Checks if we have at least aClustersRequired clusters free in the FAT.
1.661 + This is, actually a dummy implementation.
1.662 +
1.663 + @param aClustersRequired number of free clusters required
1.664 + @return ETrue if there is at least aClustersRequired free clusters available, EFalse otherwise.
1.665 +*/
1.666 +TBool CFatTable::RequestFreeClusters(TUint32 aClustersRequired) const
1.667 + {
1.668 + //__PRINT1(_L("#- CFatTable::RequestFreeClusters(%d)"),aClustersRequired);
1.669 + ASSERT(aClustersRequired >0);
1.670 + return (NumberOfFreeClusters() >= aClustersRequired);
1.671 + }
1.672 +
1.673 +//-----------------------------------------------------------------------------
1.674 +/**
1.675 + @return ETrue if the cluster number aClusterNo is valid, i.e. belongs to the FAT table
1.676 +*/
1.677 +TBool CFatTable::ClusterNumberValid(TUint32 aClusterNo) const
1.678 + {
1.679 + return (aClusterNo >= KFatFirstSearchCluster) && (aClusterNo < iMaxEntries);
1.680 + }
1.681 +
1.682 +
1.683 +
1.684 +//#######################################################################################################################################
1.685 +//# CAtaFatTable class implementation
1.686 +//#######################################################################################################################################
1.687 +
1.688 +/**
1.689 + Constructor
1.690 +*/
1.691 +CAtaFatTable::CAtaFatTable(CFatMountCB& aOwner)
1.692 + :CFatTable(aOwner), iDriveInteface(aOwner.DriveInterface())
1.693 + {
1.694 + iState = ENotInitialised;
1.695 + }
1.696 +
1.697 +
1.698 +CAtaFatTable::~CAtaFatTable()
1.699 + {
1.700 + DestroyHelperThread();
1.701 + }
1.702 +
1.703 +
1.704 +/** factory method */
1.705 +CAtaFatTable* CAtaFatTable::NewL(CFatMountCB& aOwner)
1.706 +{
1.707 + __PRINT1(_L("CAtaFatTable::NewL() drv:%d"),aOwner.DriveNumber());
1.708 + CAtaFatTable* pSelf = new (ELeave) CAtaFatTable(aOwner);
1.709 +
1.710 + CleanupStack::PushL(pSelf);
1.711 + pSelf->InitializeL();
1.712 + CleanupStack::Pop();
1.713 +
1.714 + return pSelf;
1.715 +}
1.716 +
1.717 +
1.718 +//---------------------------------------------------------------------------------------------------------------------------------------
1.719 +
1.720 +/**
1.721 + CAtaFatTable's FAT cache factory method.
1.722 + Creates fixed cache for FAT12/FAT16 or LRU cache for FAT32
1.723 +*/
1.724 +void CAtaFatTable::CreateCacheL()
1.725 +{
1.726 + ASSERT(iOwner);
1.727 + const TUint32 fatSize=iOwner->FatSizeInBytes();
1.728 + __PRINT3(_L("CAtaFatTable::CreateCacheL drv:%d, FAT:%d, FAT Size:%d"), iOwner->DriveNumber(), FatType(), fatSize);
1.729 +
1.730 +
1.731 + //-- according to FAT specs:
1.732 + //-- FAT12 max size is 4084 entries or 6126 bytes => create fixed cache for whole FAT
1.733 + //-- FAT16 min size is 4085 entries or 8170 bytes, max size is 65525 entries or 131048 bytes => create fixed cache for whole FAT
1.734 + //-- FAT32 min size is 65526 entries or 262104 bytes => create LRU paged cache of max size: KFat32LRUCacheSize
1.735 +
1.736 + ASSERT(!iCache);
1.737 +
1.738 + //-- this is used for chaches granularity sanity check
1.739 + const TUint32 KMinGranularityLog2 = KDefSectorSzLog2; //-- 512 bytes is a minimal allowed granularity
1.740 + const TUint32 KMaxGranularityLog2 = 18; //-- 256K is a maximal allowed granularity
1.741 +
1.742 + switch(FatType())
1.743 + {
1.744 + //-- create fixed FAT12 cache
1.745 + case EFat12:
1.746 + iCache = CFat12Cache::NewL(iOwner, fatSize);
1.747 + break;
1.748 +
1.749 + //-- create fixed FAT16 cache
1.750 + case EFat16:
1.751 + {
1.752 + TUint32 fat16_ReadGranularity_Log2; //-- FAT16 cache read granularity Log2
1.753 + TUint32 fat16_WriteGranularity_Log2;//-- FAT16 cache write granularity Log2
1.754 +
1.755 + iOwner->FatConfig().Fat16FixedCacheParams(fat16_ReadGranularity_Log2, fat16_WriteGranularity_Log2);
1.756 +
1.757 + //-- check if granularity values look sensible
1.758 + const TBool bParamsValid = fat16_ReadGranularity_Log2 >= KMinGranularityLog2 && fat16_ReadGranularity_Log2 <= KMaxGranularityLog2 &&
1.759 + fat16_WriteGranularity_Log2 >= KMinGranularityLog2 && fat16_WriteGranularity_Log2 <= KMaxGranularityLog2;
1.760 +
1.761 + __ASSERT_ALWAYS(bParamsValid, Fault(EFatCache_BadGranularity));
1.762 +
1.763 +
1.764 + iCache = CFat16FixedCache::NewL(iOwner, fatSize, fat16_ReadGranularity_Log2, fat16_WriteGranularity_Log2);
1.765 + }
1.766 + break;
1.767 +
1.768 + //-- create FAT32 LRU paged cache
1.769 + case EFat32:
1.770 + {
1.771 + TUint32 fat32_LRUCache_MaxMemSize; //-- Maximum memory for the LRU FAT32 cache
1.772 + TUint32 fat32_ReadGranularity_Log2; //-- FAT32 cache read granularity Log2
1.773 + TUint32 fat32_WriteGranularity_Log2;//-- FAT32 cache write granularity Log2
1.774 +
1.775 + iOwner->FatConfig().Fat32LruCacheParams(fat32_ReadGranularity_Log2, fat32_WriteGranularity_Log2, fat32_LRUCache_MaxMemSize);
1.776 +
1.777 +
1.778 + //-- check if granularity and required cache size values look sensible
1.779 + const TBool bParamsValid = fat32_ReadGranularity_Log2 >= KMinGranularityLog2 && fat32_ReadGranularity_Log2 <= KMaxGranularityLog2 &&
1.780 + fat32_WriteGranularity_Log2 >= KMinGranularityLog2 && fat32_WriteGranularity_Log2 <= KMaxGranularityLog2 &&
1.781 + fat32_LRUCache_MaxMemSize >= 8*K1KiloByte && fat32_LRUCache_MaxMemSize < 4*K1MegaByte;
1.782 +
1.783 + __ASSERT_ALWAYS(bParamsValid, Fault(EFatCache_BadGranularity));
1.784 +
1.785 + iCache = CFat32LruCache::NewL(iOwner, fat32_LRUCache_MaxMemSize, fat32_ReadGranularity_Log2, fat32_WriteGranularity_Log2);
1.786 + }
1.787 + break;
1.788 +
1.789 + default:
1.790 + ASSERT(0);
1.791 + User::Leave(KErrCorrupt);
1.792 + break;
1.793 + };
1.794 +
1.795 + ASSERT(iCache);
1.796 +}
1.797 +
1.798 +//---------------------------------------------------------------------------------------------------------------------------------------
1.799 +
1.800 +/**
1.801 + Destroys a helper thread object.
1.802 + If the thread is running, stops it first. than deletes the ipHelperThread and sets it to NULL
1.803 +*/
1.804 +void CAtaFatTable::DestroyHelperThread()
1.805 +{
1.806 +
1.807 + if(!ipHelperThread)
1.808 + return;
1.809 +
1.810 + __PRINT1(_L("CAtaFatTable::DestroyHelperThread(), drv:%d"), iOwner->DriveNumber());
1.811 + ipHelperThread->ForceStop();
1.812 + delete ipHelperThread;
1.813 + ipHelperThread = NULL;
1.814 +}
1.815 +
1.816 +//---------------------------------------------------------------------------------------------------------------------------------------
1.817 +
1.818 +/**
1.819 + Flush the FAT cache on disk
1.820 + @leave System wide error codes
1.821 +*/
1.822 +void CAtaFatTable::FlushL()
1.823 + {
1.824 + __PRINT1(_L("CAtaFatTable::FlushL(), drv:%d"), iOwner->DriveNumber());
1.825 +
1.826 + //-- the data can't be written if the mount is inconsistent
1.827 + iOwner->CheckStateConsistentL();
1.828 +
1.829 + if (iCache)
1.830 + iCache->FlushL();
1.831 + }
1.832 +
1.833 +
1.834 +//---------------------------------------------------------------------------------------------------------------------------------------
1.835 +
1.836 +/**
1.837 + Dismount the cache. Stops any activity, deallocates caches etc.
1.838 + @param aDiscardDirtyData if ETrue, non-flushed data in the cache will be discarded.
1.839 +*/
1.840 +void CAtaFatTable::Dismount(TBool aDiscardDirtyData)
1.841 + {
1.842 + __PRINT3(_L("#=-= CAtaFatTable::Dismount(%d), drv:%d, state:%d"), aDiscardDirtyData, iOwner->DriveNumber(), State());
1.843 +
1.844 + //-- if there is a helper thread, stop it and delete its object
1.845 + DestroyHelperThread();
1.846 +
1.847 + //-- if there is the cache, close it (it may lead to deallocating its memory)
1.848 + if(iCache)
1.849 + {
1.850 + //-- cache's Close() can check if the cache is clean.
1.851 + //-- ignore dirty data in cache if the mount is not in consistent state (it's impossible to flush cache data)
1.852 + //-- or if we are asked to do so.
1.853 + const TBool bIgnoreDirtyData = aDiscardDirtyData || !iOwner->ConsistentState();
1.854 + iCache->Close(bIgnoreDirtyData);
1.855 +
1.856 + delete iCache;
1.857 + iCache=NULL;
1.858 + }
1.859 +
1.860 + SetState(EDismounted);
1.861 + }
1.862 +
1.863 +//---------------------------------------------------------------------------------------------------------------------------------------
1.864 +
1.865 +/**
1.866 + Invalidate whole FAT cache.
1.867 + Depending of cache type this may just mark cache invalid with reading on demand or re-read whole cache from the media
1.868 +*/
1.869 +void CAtaFatTable::InvalidateCacheL()
1.870 +{
1.871 + __PRINT1(_L("CAtaFatTable::InvalidateCache(), drv:%d"), iOwner->DriveNumber());
1.872 +
1.873 + //-- if we have a cache, invalidate it entirely
1.874 + if(iCache)
1.875 + {
1.876 + User::LeaveIfError(iCache->Invalidate());
1.877 + }
1.878 +
1.879 + //-- invalidating whole FAT cache means that something very serious happened.
1.880 + //-- if we have a helper thread running, abort it.
1.881 + if(ipHelperThread)
1.882 + ipHelperThread->ForceStop();
1.883 +
1.884 +}
1.885 +
1.886 +
1.887 +//---------------------------------------------------------------------------------------------------------------------------------------
1.888 +
1.889 +/**
1.890 + Invalidate specified region of the FAT cache
1.891 + Depending of cache type this may just mark part of the cache invalid with reading on demand later
1.892 + or re-read whole cache from the media.
1.893 +
1.894 + @param aPos absolute media position where the region being invalidated starts.
1.895 + @param aLength length in bytes of region to invalidate / refresh
1.896 +*/
1.897 +void CAtaFatTable::InvalidateCacheL(TInt64 aPos, TUint32 aLength)
1.898 + {
1.899 + __PRINT3(_L("CAtaFatTable::InvalidateCacheL() drv:%d, pos:%LU, len:%u,"), iOwner->DriveNumber(), aPos, aLength);
1.900 +
1.901 + if(I64HIGH(aPos) || !aLength || I64HIGH(aPos+aLength))
1.902 + return; //-- FAT tables can't span over 4G
1.903 +
1.904 + const TUint32 mediaPos32 = I64LOW(aPos);
1.905 +
1.906 + //-- we do not use other copies of FAT, so trach changes only in FAT1
1.907 + const TUint32 fat1StartPos = iOwner->StartOfFatInBytes();
1.908 + const TUint32 fat1EndPos = fat1StartPos + iOwner->FatSizeInBytes();
1.909 +
1.910 + TUint32 invRegionPosStart = 0; //-- media pos where the invalidated region starts
1.911 + TUint32 invRegionLen = 0; //-- size of the invalidated region, bytes
1.912 +
1.913 + //-- calculate the FAT1 region being invalidated
1.914 + if(mediaPos32 < fat1StartPos)
1.915 + {
1.916 + if((mediaPos32 + aLength) <= fat1StartPos)
1.917 + return;
1.918 +
1.919 + invRegionPosStart = fat1StartPos;
1.920 + invRegionLen = aLength - (fat1StartPos-mediaPos32);
1.921 + }
1.922 + else //if(mediaPos32 < fat1StartPos)
1.923 + {//-- mediaPos32 >= fat1StartPos)
1.924 + if(mediaPos32 >= fat1EndPos)
1.925 + return;
1.926 +
1.927 + invRegionPosStart = mediaPos32;
1.928 +
1.929 + if((mediaPos32 + aLength) <= fat1EndPos)
1.930 + {
1.931 + invRegionLen = aLength;
1.932 + }
1.933 + else
1.934 + {
1.935 + invRegionLen = mediaPos32+aLength-fat1EndPos;
1.936 + }
1.937 + }
1.938 +
1.939 + //-- convert the media pos of the region into FAT entries basis, depending on the FAT type
1.940 + ASSERT(invRegionPosStart >= fat1StartPos && invRegionLen <= (TUint)iOwner->FatSizeInBytes());
1.941 +
1.942 + TUint32 startFatEntry=0;
1.943 + TUint32 numEntries = 0;
1.944 +
1.945 + switch(FatType())
1.946 + {
1.947 + case EFat12:
1.948 + //-- invalidate whole cache; it is not worth making calculations for such small memory region.
1.949 + User::LeaveIfError(iCache->Invalidate());
1.950 + return;
1.951 +
1.952 + case EFat16:
1.953 + startFatEntry = (invRegionPosStart-fat1StartPos) >> KFat16EntrySzLog2;
1.954 + numEntries = (invRegionLen + (sizeof(TFat16Entry)-1)) >> KFat16EntrySzLog2;
1.955 + break;
1.956 +
1.957 + case EFat32:
1.958 + startFatEntry = (invRegionPosStart-fat1StartPos) >> KFat32EntrySzLog2;
1.959 + numEntries = (invRegionLen + (sizeof(TFat32Entry)-1)) >> KFat32EntrySzLog2;
1.960 + break;
1.961 +
1.962 + default:
1.963 + ASSERT(0);
1.964 + return;
1.965 + };
1.966 +
1.967 + if(startFatEntry < KFatFirstSearchCluster)
1.968 + {//-- FAT[0] and FAT[1] can't be legally accessed, they are reserved entries. We need to adjust region being refreshed.
1.969 + if(numEntries <= KFatFirstSearchCluster)
1.970 + return; //-- nothing to refresh
1.971 +
1.972 + startFatEntry += KFatFirstSearchCluster;
1.973 + numEntries -= KFatFirstSearchCluster;
1.974 + }
1.975 +
1.976 + User::LeaveIfError(iCache->InvalidateRegion(startFatEntry, numEntries));
1.977 + }
1.978 +
1.979 +
1.980 +//-----------------------------------------------------------------------------
1.981 +/**
1.982 + Initialize the object, create FAT cache if required
1.983 + @leave KErrNoMemory
1.984 +*/
1.985 +void CAtaFatTable::InitializeL()
1.986 + {
1.987 + __PRINT2(_L("CAtaFatTable::InitializeL() drv:%d, state%d"), iOwner->DriveNumber(), State());
1.988 + CFatTable::InitializeL();
1.989 +
1.990 + ASSERT(!iCache);
1.991 + ASSERT(State() == ENotInitialised);
1.992 +
1.993 + //-- create the FAT cache.
1.994 + CreateCacheL();
1.995 +
1.996 + SetState(EInitialised);
1.997 +
1.998 + }
1.999 +
1.1000 +//-----------------------------------------------------------------------------
1.1001 +/**
1.1002 + Mount the FAT table to the CFatMountCB. Depending on mount parameters and configuration this method
1.1003 + can do various things, like counting free clusters synchronously if data from FSInfo isn't valid,
1.1004 + or setting up a FAT backround thread and return immediately etc.
1.1005 +
1.1006 + @param aMountParam mounting parameters, like some data from FSInfo
1.1007 +
1.1008 +*/
1.1009 +void CAtaFatTable::MountL(const TMountParams& aMountParam)
1.1010 + {
1.1011 + __PRINT2(_L("CAtaFatTable::MountL() drv:%d, state:%d"), iOwner->DriveNumber(), State());
1.1012 +
1.1013 + ASSERT(State() == EInitialised);
1.1014 + SetState(EMounting);
1.1015 +
1.1016 + if(ipHelperThread)
1.1017 + {
1.1018 + __PRINT(_L("CAtaFatTable::MountL() Helper thread is present!"));
1.1019 + ASSERT(0);
1.1020 + DestroyHelperThread();
1.1021 + }
1.1022 +
1.1023 +
1.1024 + //-- Check if we have valid data from FSInfo. In this case we don't need to count free clusters
1.1025 + if(aMountParam.iFsInfoValid)
1.1026 + {
1.1027 + ASSERT(IsFat32());
1.1028 + ASSERT(aMountParam.iFreeClusters <= MaxEntries());
1.1029 +
1.1030 + ASSERT(ClusterNumberValid(aMountParam.iFirstFreeCluster));
1.1031 +
1.1032 + SetFreeClusters(aMountParam.iFreeClusters);
1.1033 + SetFreeClusterHint(aMountParam.iFirstFreeCluster);
1.1034 +
1.1035 + __PRINT2(_L("CAtaFatTable::MountL() Using data from FSInfo sector. free clusters:%d, 1st free:%d"), FreeClusters(), FreeClusterHint());
1.1036 +
1.1037 + //-- We don't need to scan entire FAT to find out the number of free entries, because the data are taken from FSInfo.
1.1038 + //-- But if we are going to use the FAT32 bit supercache, we need to populate it. So, try to start up a special
1.1039 + //-- populating thread.
1.1040 + CFatBitCache *pFatBitCache = iCache->BitCacheInterface();
1.1041 + if(pFatBitCache)
1.1042 + {//-- bit cache is present, we need to populate (or repopulate it)
1.1043 + //-- create helper thread object and start the thread
1.1044 + ipHelperThread = CFat32BitCachePopulator::NewL(*this);
1.1045 +
1.1046 + ipHelperThread->Launch();
1.1047 + //-- background FAT bit cache populating thread is running now.
1.1048 + //-- the result of thread start up and completion isn't very interesting: If it fails to
1.1049 + //-- properly populate the cache, nothing fatal will happen.
1.1050 + }
1.1051 +
1.1052 + //-- CFat32BitCachePopulator doesn't affect FAT table state.
1.1053 + SetState(EMounted);
1.1054 + return;
1.1055 + }
1.1056 +
1.1057 + //-- FSInfo data are invalid; we need to count free clusters by reading whole FAT table
1.1058 + //-- This method can optionally create a background thread (that will count free clusters) and return immideately.
1.1059 + CountFreeClustersL();
1.1060 + }
1.1061 +
1.1062 +//-----------------------------------------------------------------------------
1.1063 +
1.1064 +/**
1.1065 + Decrements the free cluster count. This is an overridden method with synchronisation.
1.1066 + @param aCount a number of clusters
1.1067 +*/
1.1068 +void CAtaFatTable::DecrementFreeClusterCount(TUint32 aCount)
1.1069 + {
1.1070 + XAutoLock lock(iOwner); //-- enter critical section
1.1071 + CFatTable::DecrementFreeClusterCount(aCount);
1.1072 + }
1.1073 +
1.1074 +/**
1.1075 + Increments the free cluster count. This is an overridden method with synchronisation.
1.1076 + @param aCount a number of clusters
1.1077 +*/
1.1078 +void CAtaFatTable::IncrementFreeClusterCount(TUint32 aCount)
1.1079 + {
1.1080 + XAutoLock lock(iOwner); //-- enter critical section
1.1081 + CFatTable::IncrementFreeClusterCount(aCount);
1.1082 + }
1.1083 +
1.1084 +//-----------------------------------------------------------------------------
1.1085 +
1.1086 +/**
1.1087 + Obtain number of free clusters on the volume. This is an overridden method.
1.1088 + Depending on the "aSyncOperation" parameter this operation can be fully synhronous (exact number of free clusters ) or asynchronous
1.1089 + (current number of free clusters) if the FAT scanning thread is still running.
1.1090 +
1.1091 + @param aSyncOperation if ETrue, this method will wait until FAT scan thread finishes and return exact number of free clusters
1.1092 + if false, it will return current number of free clusters counted by FAT scan thread if it hasn't finished yet.
1.1093 +
1.1094 + @return Number of free clusters. See also CAtaFatTable::RequestFreeClusters()
1.1095 +*/
1.1096 +TUint32 CAtaFatTable::NumberOfFreeClusters(TBool aSyncOperation/*=EFalse*/) const
1.1097 + {
1.1098 + if(ipHelperThread && ipHelperThread->ThreadWorking() && ipHelperThread->Type() == CFatHelperThreadBase::EFreeSpaceScanner)
1.1099 + {//-- here we have running helper thread that counts free entries in FAT.
1.1100 + //-- if this operation is synchronous, we need to wait until it finish its job in order to get _exact_ number of free cluster,
1.1101 + //-- not currently counted
1.1102 +
1.1103 + //__PRINT2(_L("#- CAtaFatTable::NumberOfFreeClusters(), drv:%d enter, sync:%d"), iOwner->DriveNumber(), aSyncOperation);
1.1104 +
1.1105 + if(aSyncOperation)
1.1106 + {//-- wait for background scanning thread to finish counting free clusters if this operation is synchronous
1.1107 + ipHelperThread->BoostPriority(ETrue);
1.1108 + ipHelperThread->WaitToFinish();
1.1109 + }
1.1110 +
1.1111 + XAutoLock lock(iOwner); //-- enter critical section
1.1112 +
1.1113 + const TUint32 freeClusters = FreeClusters();
1.1114 + //__PRINT2(_L("#- CAtaFatTable::NumberOfFreeClusters(), drv:%d Exit, clusters:%d"), iOwner->DriveNumber(), freeClusters);
1.1115 + return freeClusters;
1.1116 + }
1.1117 +
1.1118 + return FreeClusters();
1.1119 +
1.1120 + }
1.1121 +
1.1122 +//-----------------------------------------------------------------------------
1.1123 +
1.1124 +/**
1.1125 + Set free cluster count. This is an overridden method with synchronisation.
1.1126 + @param aFreeClusters new value of free clusters
1.1127 +*/
1.1128 +void CAtaFatTable::SetFreeClusters(TUint32 aFreeClusters)
1.1129 + {
1.1130 + XAutoLock lock(iOwner); //-- enter critical section
1.1131 + CFatTable::SetFreeClusters(aFreeClusters);
1.1132 + }
1.1133 +
1.1134 +/**
1.1135 + This is an overridden method with synchronisation.
1.1136 + @return the last known free cluster number.
1.1137 +*/
1.1138 +TUint32 CAtaFatTable::FreeClusterHint() const
1.1139 + {
1.1140 + XAutoLock lock(iOwner); //-- enter critical section
1.1141 + return CFatTable::FreeClusterHint();
1.1142 + }
1.1143 +
1.1144 +/** Set next free cluster number. This is an overridden method with synchronisation. */
1.1145 +void CAtaFatTable::SetFreeClusterHint(TUint32 aCluster)
1.1146 + {
1.1147 + XAutoLock lock(iOwner); //-- enter critical section
1.1148 + CFatTable::SetFreeClusterHint(aCluster);
1.1149 + }
1.1150 +
1.1151 +/**
1.1152 + @return ETrue if the state of the object is consistent; i.e. it is
1.1153 + fully constructed, valid and the amount of free entries is known.
1.1154 + Used in the case of asynchronous mounting.
1.1155 +*/
1.1156 +TBool CAtaFatTable::ConsistentState() const
1.1157 + {
1.1158 + return State() == EMounted;
1.1159 + }
1.1160 +
1.1161 +//-----------------------------------------------------------------------------
1.1162 +
1.1163 +/**
1.1164 + Request for the raw write access to the FAT area (all copies of FAT).
1.1165 + If FAT helper thread is running, waits until it finishes.
1.1166 +
1.1167 + @param aPos absolute media position we are going to write to. Be careful with casting it from TInt64 and losing high word.
1.1168 + @param aLen length of the area being written
1.1169 +*/
1.1170 +void CAtaFatTable::RequestRawWriteAccess(TInt64 aPos, TUint32 aLen) const
1.1171 + {
1.1172 + if(I64HIGH(aPos))
1.1173 + return;
1.1174 +
1.1175 + const TUint32 pos32 = I64LOW(aPos);
1.1176 + const TUint32 posFatStart = iOwner->StartOfFatInBytes(); //-- position of the FAT start on the volume
1.1177 + const TUint32 posFatsEnd = posFatStart + iOwner->NumberOfFats()*iOwner->FatSizeInBytes(); //-- position of the ent of ALL FATs
1.1178 +
1.1179 + if(pos32 >= posFatsEnd || (pos32+aLen) <= posFatStart)
1.1180 + return;
1.1181 +
1.1182 + __PRINT2(_L("#=- CAtaFatTable::RequestRawWriteAccess() pos:%d, len:%d"),pos32, aLen);
1.1183 +
1.1184 + //-- someone tries to write to FAT area directly. Wait for the FAT helper thread to finish
1.1185 + if(ipHelperThread)
1.1186 + ipHelperThread->WaitToFinish();
1.1187 +
1.1188 + }
1.1189 +
1.1190 +//-----------------------------------------------------------------------------
1.1191 +
1.1192 +/**
1.1193 + Checks if we have at least "aClustersRequired" clusters free in the FAT.
1.1194 + If FAT scannng thread is running, waits until requested number of free clusters counted or the thread finishes.
1.1195 +
1.1196 + @param aClustersRequired number of free clusters required
1.1197 + @return ETrue if there is at least aClustersRequired free clusters available, EFalse otherwise.
1.1198 +*/
1.1199 +TBool CAtaFatTable::RequestFreeClusters(TUint32 aClustersRequired) const
1.1200 + {
1.1201 + //__PRINT1(_L("#- CAtaFatTable::RequestFreeClusters(%d)"),aClustersRequired);
1.1202 + ASSERT(aClustersRequired >0);
1.1203 +
1.1204 + if(!ipHelperThread || !ipHelperThread->ThreadWorking() || ipHelperThread->Type() != CFatHelperThreadBase::EFreeSpaceScanner)
1.1205 + {//-- there is no FAT free space scan thread running, number of free entries can't increase in background
1.1206 + return (FreeClusters() >= aClustersRequired); //-- use simple, non-thread safe method
1.1207 + }
1.1208 +
1.1209 + //-- FAT free space scan thread is running, counting free FAT entries. wait until it has counted enough or finish.
1.1210 + ASSERT(ipHelperThread->Type() == CFatHelperThreadBase::EFreeSpaceScanner);
1.1211 +
1.1212 + TUint32 currFreeClusters;
1.1213 + const TUint KWaitGranularity = 20*K1mSec; //-- wait granularity
1.1214 +
1.1215 + ipHelperThread->BoostPriority(ETrue); //-- increase thread priority
1.1216 +
1.1217 + for(;;)
1.1218 + {
1.1219 + currFreeClusters = NumberOfFreeClusters(EFalse); //-- get _current_ number of free clusters asynchronously
1.1220 + if(currFreeClusters >= aClustersRequired)
1.1221 + break; //-- OK, the request is satisfied
1.1222 +
1.1223 + if(!ipHelperThread->ThreadWorking())
1.1224 + {//-- the thread has finished its work
1.1225 + currFreeClusters = NumberOfFreeClusters(EFalse); //-- get _current_ number of free clusters asynchronously
1.1226 + break;
1.1227 + }
1.1228 +
1.1229 + User::After(KWaitGranularity); //-- wait some time allowing FAT scanning thread to count free clusters.
1.1230 + }
1.1231 +
1.1232 + ipHelperThread->BoostPriority(EFalse); //-- set thread priority back to normal
1.1233 + //__PRINT1(_L("#- CAtaFatTable::RequestFreeClusters() #2 curr:%d"),currFreeClusters);
1.1234 +
1.1235 + return (currFreeClusters >= aClustersRequired);
1.1236 +
1.1237 + }
1.1238 +
1.1239 +//-----------------------------------------------------------------------------
1.1240 +
1.1241 +/**
1.1242 + Parse a buffer filled with FAT16 or FAT32 entries, counting free clusters and looking for the firs free cluster number.
1.1243 + Note that this method can be called from a helper FAT scan thread.
1.1244 +
1.1245 + @param aBuf FAT buffer descriptor. Must contain whole number of FAT16 or FAT32 entries
1.1246 + @param aScanParam the structure to be filled with values, like number of counted free and non-free clusters, etc.
1.1247 +*/
1.1248 +void CAtaFatTable::DoParseFatBuf(const TPtrC8& aBuf, TFatScanParam& aScanParam) const
1.1249 + {
1.1250 +
1.1251 + if(IsFat16())
1.1252 + {//-- we are processing a buffer of FAT16 entries
1.1253 + ASSERT(!ipHelperThread);
1.1254 + ASSERT((aBuf.Size() & (sizeof(TFat16Entry)-1)) == 0);
1.1255 + const TInt KNumEntries = aBuf.Size() >> KFat16EntrySzLog2;
1.1256 + const TFat16Entry* const pFatEntry = (const TFat16Entry*)(aBuf.Ptr());
1.1257 +
1.1258 + for(TInt i=0; i<KNumEntries; ++i)
1.1259 + {
1.1260 + if(aScanParam.iEntriesScanned >= KFatFirstSearchCluster)
1.1261 + {
1.1262 + const TFat16Entry entry = pFatEntry[i];
1.1263 +
1.1264 + if(entry == KSpareCluster)
1.1265 + {//-- found a free FAT entry
1.1266 + aScanParam.iCurrFreeEntries++;
1.1267 +
1.1268 + if(aScanParam.iFirstFree < KFatFirstSearchCluster)
1.1269 + aScanParam.iFirstFree = aScanParam.iEntriesScanned;
1.1270 +
1.1271 + }
1.1272 + else
1.1273 + {//-- found occupied FAT entry, count bad clusters as well
1.1274 + aScanParam.iCurrOccupiedEntries++;
1.1275 + }
1.1276 +
1.1277 + }
1.1278 +
1.1279 + aScanParam.iEntriesScanned++;
1.1280 + }
1.1281 + }//if(IsFat16())
1.1282 + else
1.1283 + if(IsFat32())
1.1284 + {//-- we are processing a buffer of FAT32 entries.
1.1285 + //-- note that here we can be in the context of the FAT free entries scan thread.
1.1286 + ASSERT((aBuf.Size() & (sizeof(TFat32Entry)-1)) == 0);
1.1287 +
1.1288 + //-- pointer to the FAT32 bit supercache. If present, we will populate it here
1.1289 + CFatBitCache *pFatBitCache = iCache->BitCacheInterface();
1.1290 +
1.1291 + const TInt KNumEntries = aBuf.Size() >> KFat32EntrySzLog2;
1.1292 + const TFat32Entry* const pFatEntry = (const TFat32Entry*)(aBuf.Ptr());
1.1293 +
1.1294 + for(TInt i=0; i<KNumEntries; ++i)
1.1295 + {
1.1296 + if(aScanParam.iEntriesScanned >= KFatFirstSearchCluster)
1.1297 + {
1.1298 + const TFat32Entry entry = pFatEntry[i] & KFat32EntryMask;
1.1299 +
1.1300 + if(entry == KSpareCluster)
1.1301 + {//-- found a free FAT32 entry
1.1302 + ++aScanParam.iCurrFreeEntries;
1.1303 +
1.1304 + if(aScanParam.iFirstFree < KFatFirstSearchCluster)
1.1305 + aScanParam.iFirstFree = aScanParam.iEntriesScanned;
1.1306 +
1.1307 +
1.1308 + //-- feed the information about free FAT entry at index aClustersScanned to the FAT bit supercache.
1.1309 + if(pFatBitCache)
1.1310 + {
1.1311 + pFatBitCache->SetFreeFatEntry(aScanParam.iEntriesScanned);
1.1312 + }
1.1313 +
1.1314 +
1.1315 + }//if(entry == KSpareCluster)
1.1316 + else
1.1317 + {//-- found occupied FAT32 entry, count bad clusters as well
1.1318 + aScanParam.iCurrOccupiedEntries++;
1.1319 + }
1.1320 + }
1.1321 +
1.1322 + ++aScanParam.iEntriesScanned;
1.1323 + }
1.1324 +
1.1325 + }//if(IsFat32())
1.1326 + else
1.1327 + {
1.1328 + ASSERT(0);
1.1329 + }
1.1330 + }
1.1331 +
1.1332 +//-----------------------------------------------------------------------------
1.1333 +
1.1334 +/**
1.1335 + Count free clusters in FAT16 or FAT32. Uses relatively large buffer to read FAT entries into;
1.1336 + This is faster than usual ReadL() calls.
1.1337 +*/
1.1338 +void CAtaFatTable::DoCountFreeClustersL()
1.1339 + {
1.1340 + __PRINT2(_L("#- CAtaFatTable::DoCountFreeClustersL() drv:%d, state:%d"), iOwner->DriveNumber(), State());
1.1341 +
1.1342 + if(!IsFat16() && !IsFat32())
1.1343 + {
1.1344 + ASSERT(0);
1.1345 + User::Leave(KErrNotSupported);
1.1346 + }
1.1347 +
1.1348 + const TUint32 KFat1StartPos = iOwner->StartOfFatInBytes();
1.1349 + const TUint32 KNumClusters = MaxEntries(); //-- FAT[0] & FAT[1] are reserved and not counted by UsableClusters()
1.1350 + const TUint32 KNumFATs = iOwner->NumberOfFats();
1.1351 + const TUint32 KFatSize = KNumClusters * (IsFat32() ? sizeof(TFat32Entry) : sizeof(TFat16Entry)); //-- usable size of one FAT.
1.1352 +
1.1353 + (void)KNumFATs;
1.1354 +
1.1355 + ASSERT(KFat1StartPos >= 1*KDefaultSectorSize);
1.1356 + ASSERT(KNumClusters > KFatFirstSearchCluster);
1.1357 + ASSERT(KNumFATs > 0);
1.1358 +
1.1359 + const TUint32 KFatBufSz = 32*K1KiloByte; //-- buffer size for FAT reading. 32K seems to be optimal size
1.1360 +
1.1361 + __ASSERT_COMPILE((KFatBufSz % sizeof(TFat32Entry)) == 0);
1.1362 + __ASSERT_COMPILE((KFatBufSz % sizeof(TFat16Entry)) == 0);
1.1363 +
1.1364 + RBuf8 buf;
1.1365 + CleanupClosePushL(buf);
1.1366 +
1.1367 + //-- allocate memory for FAT parse buffer
1.1368 + buf.CreateMaxL(KFatBufSz);
1.1369 +
1.1370 + //-- read FAT into the large buffer and parse it
1.1371 + TUint32 rem = KFatSize;
1.1372 + TUint32 mediaPos = KFat1StartPos;
1.1373 +
1.1374 + //-- prepare FAT bit supercache to being populated.
1.1375 + //-- actual populating will happen in ::DoParseFatBuf()
1.1376 + CFatBitCache *pFatBitCache = iCache->BitCacheInterface();
1.1377 +
1.1378 + if(pFatBitCache)
1.1379 + {
1.1380 + pFatBitCache->StartPopulating();
1.1381 + }
1.1382 +
1.1383 + TFatScanParam fatScanParam;
1.1384 +
1.1385 + //-- used for measuring time
1.1386 + TTime timeStart;
1.1387 + TTime timeEnd;
1.1388 + timeStart.UniversalTime(); //-- take start time
1.1389 +
1.1390 +
1.1391 + while(rem)
1.1392 + {
1.1393 + const TUint32 bytesToRead=Min(rem, KFatBufSz);
1.1394 + TPtrC8 ptrData(buf.Ptr(), bytesToRead);
1.1395 +
1.1396 + //__PRINT2(_L("#=--- CAtaFatTable::DoCountFreeClustersL() read %d bytes pos:0x%x"), bytesToRead, (TUint32)mediaPos);
1.1397 + User::LeaveIfError(iOwner->LocalDrive()->Read(mediaPos, bytesToRead, buf));
1.1398 +
1.1399 + DoParseFatBuf(ptrData, fatScanParam);
1.1400 +
1.1401 + mediaPos += bytesToRead;
1.1402 + rem -= bytesToRead;
1.1403 + }
1.1404 +
1.1405 + //-- here fatScanParam contains values for the whole FAT.
1.1406 +
1.1407 + timeEnd.UniversalTime(); //-- take end time
1.1408 + const TInt msScanTime = (TInt)( (timeEnd.MicroSecondsFrom(timeStart)).Int64() / K1mSec);
1.1409 + (void)msScanTime;
1.1410 + __PRINT1(_L("#- CAtaFatTable::DoCountFreeClustersL() finished. Taken:%d ms "), msScanTime);
1.1411 +
1.1412 +
1.1413 + //-- tell FAT bit cache that we have finished populating it
1.1414 + if(pFatBitCache)
1.1415 + {
1.1416 + pFatBitCache->FinishPopulating(ETrue);
1.1417 + pFatBitCache->Dump();
1.1418 + }
1.1419 +
1.1420 + if(!fatScanParam.iFirstFree)//-- haven't found free clusters on the volume
1.1421 + fatScanParam.iFirstFree = KFatFirstSearchCluster;
1.1422 +
1.1423 + ASSERT(fatScanParam.iCurrFreeEntries <= iOwner->UsableClusters());
1.1424 + ASSERT(ClusterNumberValid(fatScanParam.iFirstFree));
1.1425 +
1.1426 + SetFreeClusters(fatScanParam.iCurrFreeEntries);
1.1427 + SetFreeClusterHint(fatScanParam.iFirstFree);
1.1428 +
1.1429 + CleanupStack::PopAndDestroy(&buf);
1.1430 + }
1.1431 +
1.1432 +//-----------------------------------------------------------------------------
1.1433 +
1.1434 +/**
1.1435 + Count free clusters on the volume.
1.1436 + Depending on FAT type can count clusters synchronously or start a thread to do it in background.
1.1437 +*/
1.1438 +void CAtaFatTable::CountFreeClustersL()
1.1439 + {
1.1440 + __PRINT3(_L("#=- CAtaFatTable::CountFreeClustersL() drv:%d, FAT%d, state:%d"),iOwner->DriveNumber(),FatType(), State());
1.1441 +
1.1442 + ASSERT(State() == EMounting);
1.1443 + ASSERT(!ipHelperThread);
1.1444 +
1.1445 + TInt nRes;
1.1446 +
1.1447 + switch(FatType())
1.1448 + {
1.1449 + case EFat12: //-- use old default scanning, it is synchronous
1.1450 + CFatTable::CountFreeClustersL();
1.1451 + SetState(EMounted);
1.1452 + break;
1.1453 +
1.1454 + case EFat16: //-- enhanced FAT scan, but still synchronous
1.1455 + TRAP(nRes, DoCountFreeClustersL());
1.1456 + if(nRes !=KErrNone)
1.1457 + {
1.1458 + CFatTable::CountFreeClustersL(); //-- fall back to the legacy method
1.1459 + }
1.1460 +
1.1461 + SetState(EMounted);
1.1462 + break;
1.1463 +
1.1464 + case EFat32: //-- This is FAT32, try to set up a FAT scanning thread if allowed
1.1465 + {
1.1466 + TBool bFat32BkGndScan = ETrue; //-- if true, we will try to start up a background scanning thread.
1.1467 +
1.1468 + //-- 1. check if background FAT scanning is disabled in config
1.1469 + if(!iOwner->FatConfig().FAT32_AsynchMount())
1.1470 + {
1.1471 + __PRINT(_L("#=- FAT32 BkGnd scan is disabled in config."));
1.1472 + bFat32BkGndScan = EFalse;
1.1473 + }
1.1474 +
1.1475 + //-- 2. check if background FAT scanning is disabled by test interface
1.1476 +#ifdef _DEBUG
1.1477 + TInt nMntDebugFlags;
1.1478 + if(bFat32BkGndScan && RProperty::Get(KSID_Test1, iOwner->DriveNumber(), nMntDebugFlags) == KErrNone)
1.1479 + {//-- test property for this drive is defined
1.1480 + if(nMntDebugFlags & KMntDisable_FatBkGndScan)
1.1481 + {
1.1482 + __PRINT(_L("#- FAT32 BkGnd scan is disabled is disabled by debug interface."));
1.1483 + bFat32BkGndScan = EFalse;
1.1484 + }
1.1485 +
1.1486 + }
1.1487 +#endif
1.1488 + //-- 3. try to start FAT32 free entries scanning thread.
1.1489 + if(bFat32BkGndScan)
1.1490 + {
1.1491 + __PRINT(_L("#=- Starting up FAT32 free entries scanner thread..."));
1.1492 + TRAP(nRes, DoLaunchFat32FreeSpaceScanThreadL());
1.1493 + if(nRes == KErrNone)
1.1494 + break; //-- let thread run by itself
1.1495 +
1.1496 + //-- DoLaunchFat32FreeSpaceScanThreadL() has set this object state.
1.1497 + }
1.1498 +
1.1499 + //-- we either failed to launch the thread or this feature was disabled somehow. Fall back to the synchronous scan.
1.1500 + TRAP(nRes, DoCountFreeClustersL());
1.1501 + if(nRes !=KErrNone)
1.1502 + {
1.1503 + CFatTable::CountFreeClustersL(); //-- fall back to the legacy method
1.1504 + }
1.1505 +
1.1506 + SetState(EMounted);
1.1507 + }//case EFat32
1.1508 + break;
1.1509 +
1.1510 + default:
1.1511 + ASSERT(0);
1.1512 + break;
1.1513 +
1.1514 + } //switch(FatType())
1.1515 + }
1.1516 +
1.1517 +//-----------------------------------------------------------------------------
1.1518 +
1.1519 +/**
1.1520 + Set up and start FAT scan thread.
1.1521 + Leaves on error.
1.1522 +*/
1.1523 +void CAtaFatTable::DoLaunchFat32FreeSpaceScanThreadL()
1.1524 + {
1.1525 + __PRINT2(_L("#=- CAtaFatTable::DoLaunchFat32FreeSpaceScanThreadL() drv:%d, state:%d"),iOwner->DriveNumber(), State());
1.1526 + ASSERT(State() == EMounting);
1.1527 +
1.1528 + //-- 1. check if something is already working (shan't be!)
1.1529 + if(ipHelperThread)
1.1530 + {
1.1531 + if(ipHelperThread->ThreadWorking())
1.1532 + {
1.1533 + __PRINT(_L("#=- CAtaFatTable::DoLaunchScanThread() some thread is already running ?"));
1.1534 + ASSERT(0);
1.1535 + User::Leave(KErrAlreadyExists);
1.1536 + }
1.1537 +
1.1538 + DestroyHelperThread();
1.1539 + }
1.1540 +
1.1541 + //-- 2. create helper thread object and start the thread
1.1542 + ipHelperThread = CFat32FreeSpaceScanner::NewL(*this);
1.1543 +
1.1544 + SetState(EFreeClustersScan);
1.1545 +
1.1546 + ipHelperThread->Launch();
1.1547 + //-- background FAT scanning thread is running now
1.1548 + }
1.1549 +
1.1550 +//-----------------------------------------------------------------------------
1.1551 +/**
1.1552 + Read an entry from the FAT table
1.1553 +
1.1554 + @param aFatIndex FAT entry number to read
1.1555 + @return FAT entry value
1.1556 +*/
1.1557 +TUint32 CAtaFatTable::ReadL(TUint32 aFatIndex) const
1.1558 + {
1.1559 + if(!ClusterNumberValid(aFatIndex))
1.1560 + {
1.1561 + //ASSERT(0); //-- deliberately corrupted (by some tests) DOS directory entries can have 0 in the "first cluster" field.
1.1562 + __PRINT1(_L("CAtaFatTable::ReadL(%d) bad Index!"), aFatIndex);
1.1563 + User::Leave(KErrCorrupt);
1.1564 + }
1.1565 +
1.1566 +
1.1567 + const TUint entry = iCache->ReadEntryL(aFatIndex);
1.1568 + return entry;
1.1569 + }
1.1570 +
1.1571 +
1.1572 +//-----------------------------------------------------------------------------
1.1573 +/**
1.1574 + Write an entry to the FAT table
1.1575 +
1.1576 + @param aFatIndex aFatIndex FAT entry number to write
1.1577 + @param aValue FAT entry to write
1.1578 + @leave
1.1579 +*/
1.1580 +void CAtaFatTable::WriteL(TUint32 aFatIndex, TUint32 aValue)
1.1581 + {
1.1582 +
1.1583 + __PRINT2(_L("CAtaFatTable::WriteL() entry:%d, val:0x%x"), aFatIndex, aValue);
1.1584 +
1.1585 + if(!ClusterNumberValid(aFatIndex))
1.1586 + {
1.1587 + ASSERT(0);
1.1588 + User::Leave(KErrCorrupt);
1.1589 + }
1.1590 +
1.1591 + if(aValue != KSpareCluster && (aValue < KFatFirstSearchCluster || aValue > KFat32EntryMask))
1.1592 + {
1.1593 + ASSERT(0);
1.1594 + User::Leave(KErrCorrupt);
1.1595 + }
1.1596 +
1.1597 + //-- wait until we are allowed to write FAT entry
1.1598 + if(ipHelperThread && ipHelperThread->ThreadWorking())
1.1599 + {
1.1600 + ASSERT(ipHelperThread->ThreadId() != RThread().Id()); //-- this method must not be called the FAT helper thread
1.1601 + ipHelperThread->RequestFatEntryWriteAccess(aFatIndex);
1.1602 + }
1.1603 +
1.1604 + //-- write entry to the FAT through FAT cache
1.1605 + iCache->WriteEntryL(aFatIndex, aValue);
1.1606 +
1.1607 +
1.1608 + //-- if we are writing "spare" FAT entry, tell FAT bit supercache about it.
1.1609 + //-- it will store the information that corresponding FAT cache sector has a spare FAT entry.
1.1610 + //-- writing non-spare FAT entry doesn't mean anything: that FAT cache sector might or might not contain free entries.
1.1611 + if(aValue == KSpareCluster && iCache->BitCacheInterface())
1.1612 + {
1.1613 + CFatBitCache *pFatBitCache = iCache->BitCacheInterface();
1.1614 + const CFatBitCache::TState cacheState= pFatBitCache->State();
1.1615 + if(cacheState == CFatBitCache::EPopulated || cacheState == CFatBitCache::EPopulating)
1.1616 + {//-- bit cache is either normally populated or being populated by one of the helper threads
1.1617 + if(ipHelperThread && ipHelperThread->ThreadWorking())
1.1618 + {
1.1619 + //-- here we have a multithreading issue. Helper FAT thread can be parsing FAT and optionally calling ReportFreeFatEntry(..) as well.
1.1620 + //-- in this case we need either to suspend the helper thread in order to prevent corruption of the FAT bit cache data,
1.1621 + //-- or ignore this call and rely on the fact that the FAT bit supercache is a kind of self-learning and the missing data will be
1.1622 + //-- fixed during conflict resolution (this can lead to performance degradation).
1.1623 +
1.1624 + //-- ok, suspend the helper thread while we are changing data in the bit cache
1.1625 + AcquireLock();
1.1626 + ipHelperThread->Suspend();
1.1627 + pFatBitCache->SetFreeFatEntry(aFatIndex);
1.1628 + ipHelperThread->Resume();
1.1629 + ReleaseLock();
1.1630 +
1.1631 + }
1.1632 + else
1.1633 + {//-- no one else is accessing FAT in this time
1.1634 + ASSERT(pFatBitCache->UsableState());
1.1635 + pFatBitCache->SetFreeFatEntry(aFatIndex);
1.1636 + }
1.1637 + }
1.1638 +
1.1639 + }//if(aValue == KSpareCluster)
1.1640 +
1.1641 + }
1.1642 +
1.1643 +//-----------------------------------------------------------------------------
1.1644 +/**
1.1645 + This is an overridden method from CFatTable. See CFatTable::FindClosestFreeClusterL(...)
1.1646 + Does the same, i.e looks for the closest to "aCluster" free FAT entry, but more advanced,
1.1647 + it can use FAT bit supercache for quick lookup.
1.1648 +
1.1649 + @param aCluster Cluster to find nearest free cluster to.
1.1650 + @leave KErrDiskFull + system wide error codes
1.1651 + @return cluster number found
1.1652 +*/
1.1653 +TUint32 CAtaFatTable::FindClosestFreeClusterL(TUint32 aCluster)
1.1654 + {
1.1655 + __PRINT2(_L("CAtaFatTable::FindClosestFreeClusterL() drv:%d cl:%d"),iOwner->DriveNumber(),aCluster);
1.1656 +
1.1657 + if(!ClusterNumberValid(aCluster))
1.1658 + {
1.1659 + ASSERT(0);
1.1660 + User::Leave(KErrCorrupt);
1.1661 + }
1.1662 +
1.1663 +
1.1664 + if(!RequestFreeClusters(1))
1.1665 + {//-- there is no at least 1 free cluster available
1.1666 + __PRINT(_L("CAtaFatTable::FindClosestFreeClusterL() leaving KErrDiskFull #1"));
1.1667 + User::Leave(KErrDiskFull);
1.1668 + }
1.1669 +
1.1670 + //-- check if we have FAT bit supercache and it is in consistent state
1.1671 + CFatBitCache *pFatBitCache = iCache->BitCacheInterface();
1.1672 + if(!pFatBitCache)
1.1673 + return CFatTable::FindClosestFreeClusterL(aCluster); //-- fall back to the old search method
1.1674 +
1.1675 + ASSERT(IsFat32());
1.1676 +
1.1677 + if(!pFatBitCache->UsableState())
1.1678 + {
1.1679 + //__PRINT(_L("#++ CAtaFatTable::FindClosestFreeClusterL() FAT bit cache isn't consistent!"));
1.1680 + return CFatTable::FindClosestFreeClusterL(aCluster); //-- fall back to the old search method
1.1681 + }
1.1682 +
1.1683 + //-- ask FAT bit supercache to find us FAT cache sector (closest to the aCluster) that contains free FAT entries.
1.1684 + //__PRINT2(_L("#++ CAtaFatTable::FindClosestFreeClusterL(%d) hint free cl:%d"), aCluster, FreeClusterHint());
1.1685 +
1.1686 + const TInt KMaxLookupRetries = 2;
1.1687 + for(TInt i=0; i<KMaxLookupRetries; ++i)
1.1688 + {
1.1689 + const TInt nRes = pFatBitCache->FindClosestFreeFatEntry(aCluster);
1.1690 + switch(nRes)
1.1691 + {
1.1692 + case KErrNone:
1.1693 + //-- FAT bit supercache has found a free FAT entry in the FAT32 cache
1.1694 + //__PRINT1(_L("#++ CAtaFatTable::FindClosestFreeClusterL FOUND! cl:%d"), aCluster);
1.1695 +
1.1696 + ASSERT(ClusterNumberValid(aCluster));
1.1697 +
1.1698 + //-- do not update the last known free cluster, it can be quite expensive.
1.1699 + //-- do it in the caller method with bigger granularity.
1.1700 + return aCluster;
1.1701 +
1.1702 + case KErrNotFound:
1.1703 + //-- there was a bit cache conflict, when FAT cache sector is marked as having free FAT entries, but it doesn't have them in reality.
1.1704 + //-- It can happen because FAT bit cache entry is marked '1' only on populating the bit vector or if someone writes KSpareCluster into the
1.1705 + //-- corresponding FAT cache sector. Such conflict can happen quite often.
1.1706 + //-- Try search again, the search is very likely to succeed very close, because the FAT bit cache entry had already been fixed as the result of conflict resolution.
1.1707 + break;
1.1708 +
1.1709 + case KErrCorrupt:
1.1710 + //-- pFatBitCache->FindClosestFreeFatEntry failed to read a page from the media
1.1711 + //-- break out from the loop and fall back to old search just in case.
1.1712 +
1.1713 + case KErrEof:
1.1714 + //-- there are no '1' entries in whole FAT bit cache vector at all, which is quite unlikely
1.1715 + //-- break out from the loop and fall back to old search.
1.1716 + i=KMaxLookupRetries;
1.1717 + break;
1.1718 +
1.1719 + //-- unexpected result code.
1.1720 + default:
1.1721 + ASSERT(0);
1.1722 + i=KMaxLookupRetries;
1.1723 + break;
1.1724 +
1.1725 +
1.1726 + };//switch(nRes)
1.1727 +
1.1728 + }//for(TInt i=0; i<KMaxLookupRetries; ++i)
1.1729 +
1.1730 + //-- something went wrong, Bit Fat supercache could not find FAT cache sector that contains at least one free FAT entry.
1.1731 + //-- this is most likely because of the FAT data mismatch between FAT and bit cache.
1.1732 + __PRINT(_L("#++ CAtaFatTable::FindClosestFreeClusterL FALLBACK #1"));
1.1733 +
1.1734 + //!!!!?? use not aCluster, but previous search result here ???
1.1735 + return CFatTable::FindClosestFreeClusterL(aCluster); //-- fall back to the old search method
1.1736 + }
1.1737 +
1.1738 +
1.1739 +
1.1740 +/**
1.1741 + Get the next cluster in the chain from the FAT
1.1742 +
1.1743 + @param aCluster number to read, contains next cluster upon return
1.1744 + @return False if end of cluster chain
1.1745 +*/
1.1746 +TBool CFatTable::GetNextClusterL(TInt& aCluster) const
1.1747 + {
1.1748 + __PRINT1(_L("CAtaFatTable::GetNextClusterL(%d)"), aCluster);
1.1749 +
1.1750 + const TUint32 nextCluster = ReadL(aCluster);
1.1751 + const TBool bEOC = IsEndOfClusterCh(nextCluster);
1.1752 +
1.1753 + if(bEOC)
1.1754 + return EFalse; //-- the end of cluster chain
1.1755 +
1.1756 + aCluster = nextCluster;
1.1757 +
1.1758 + return ETrue;
1.1759 + }
1.1760 +
1.1761 +/**
1.1762 + Write EOF to aFatIndex
1.1763 + @param aFatIndex index in FAT (cluster number) to be written
1.1764 +*/
1.1765 +void CFatTable::WriteFatEntryEofL(TUint32 aFatIndex)
1.1766 + {
1.1767 + __PRINT1(_L("CFatTable::WriteFatEntryEofL(%d)"), aFatIndex);
1.1768 +
1.1769 + //-- use EOF_32Bit (0x0fffffff) for all types of FAT, FAT cache will mask it appropriately
1.1770 + WriteL(aFatIndex, EOF_32Bit);
1.1771 + }
1.1772 +
1.1773 +
1.1774 +
1.1775 +/**
1.1776 + Mark cluster number aFatIndex in FAT as bad
1.1777 + @param aFatIndex index in FAT (cluster number) to be written
1.1778 +*/
1.1779 +void CFatTable::MarkAsBadClusterL(TUint32 aFatIndex)
1.1780 + {
1.1781 + __PRINT1(_L("CAtaFatTable::MarkAsBadClusterL(%d)"),aFatIndex);
1.1782 +
1.1783 + //-- use KBad_32Bit (0x0ffffff7) for all types of FAT, FAT cache will mask it appropriately
1.1784 + WriteL(aFatIndex, KBad_32Bit);
1.1785 +
1.1786 + FlushL();
1.1787 + }
1.1788 +
1.1789 +
1.1790 +/**
1.1791 + Return the location of a Cluster in the data section of the media
1.1792 +
1.1793 + @param aCluster to find location of
1.1794 + @return Byte offset of the cluster data
1.1795 +*/
1.1796 +TInt64 CAtaFatTable::DataPositionInBytes(TUint32 aCluster) const
1.1797 + {
1.1798 +
1.1799 + __ASSERT_DEBUG(ClusterNumberValid(aCluster), Fault(EFatTable_InvalidIndex));
1.1800 +
1.1801 + const TInt clusterBasePosition=iOwner->ClusterBasePosition();
1.1802 + return(((TInt64(aCluster)-KFatFirstSearchCluster) << iOwner->ClusterSizeLog2()) + clusterBasePosition);
1.1803 + }
1.1804 +
1.1805 +
1.1806 +
1.1807 +
1.1808 +//#######################################################################################################################################
1.1809 +//# CFatHelperThreadBase implementation
1.1810 +//#######################################################################################################################################
1.1811 +
1.1812 +//-----------------------------------------------------------------------------
1.1813 +CFatHelperThreadBase::CFatHelperThreadBase(CAtaFatTable& aOwner)
1.1814 + :iOwner(aOwner)
1.1815 + {
1.1816 +
1.1817 + SetState(EInvalid);
1.1818 + }
1.1819 +
1.1820 +CFatHelperThreadBase::~CFatHelperThreadBase()
1.1821 + {
1.1822 + Close();
1.1823 + }
1.1824 +
1.1825 +//-----------------------------------------------------------------------------
1.1826 +/**
1.1827 + Closes the thread object handle.
1.1828 + The thread shall not be running.
1.1829 +*/
1.1830 +void CFatHelperThreadBase::Close()
1.1831 + {
1.1832 + if(ThreadWorking())
1.1833 + {
1.1834 + ASSERT(0);
1.1835 + ForceStop();
1.1836 + }
1.1837 +
1.1838 + iThread.Close();
1.1839 + }
1.1840 +
1.1841 +//-----------------------------------------------------------------------------
1.1842 +/**
1.1843 + Waits for the thread to finish (thread function exit). if it is running.
1.1844 + @return thread completion code.
1.1845 +
1.1846 + !!!! definitely need a timeout processing here to avoid any possibitlity of hanging forever !!
1.1847 +
1.1848 +*/
1.1849 +TInt CFatHelperThreadBase::WaitToFinish() const
1.1850 + {
1.1851 + if(!ThreadWorking())
1.1852 + return ThreadCompletionCode();
1.1853 +
1.1854 +
1.1855 + //--todo: use timeout and assert to avoid hanging forever ?
1.1856 + __PRINT1(_L("#= CFatHelperThreadBase::WaitToFinish(), stat:%d"),iThreadStatus.Int());
1.1857 + User::WaitForRequest(iThreadStatus);
1.1858 + return iThreadStatus.Int();
1.1859 + }
1.1860 +
1.1861 +//-----------------------------------------------------------------------------
1.1862 +
1.1863 +/**
1.1864 + Requests the fat helper thread function to finish gracefully ASAP; then closes the thread handle.
1.1865 + Just sets a flag that is analysed by the thread function and waits thread's request completion.
1.1866 +*/
1.1867 +void CFatHelperThreadBase::ForceStop()
1.1868 + {
1.1869 + if(ThreadWorking())
1.1870 + {
1.1871 + DBG_STATEMENT(TName name = iThread.Name();)
1.1872 + __PRINT3(_L("#=!! CFatHelperThreadBase::ForceStop() id:%u, name:%S, status:%d"), (TUint)iThread.Id(), &name, ThreadCompletionCode());
1.1873 + DBG_STATEMENT(name.Zero()); //-- to avoid warning
1.1874 +
1.1875 + iOwner.AcquireLock();
1.1876 +
1.1877 + AllowToLive(EFalse) ; //-- signal the thread to exit ASAP
1.1878 +
1.1879 + iOwner.ReleaseLock();
1.1880 +
1.1881 + WaitToFinish(); //-- wait for the thread to finish.
1.1882 +
1.1883 + //-- don't know why but we need a delay, at least on the emulator. Otherwise thread object doesn't look destroyed.
1.1884 + //-- probably something with scheduling.
1.1885 + User::After(10*K1mSec);
1.1886 + }
1.1887 +
1.1888 + iThread.Close();
1.1889 + }
1.1890 +
1.1891 +
1.1892 +//-----------------------------------------------------------------------------
1.1893 +
1.1894 +
1.1895 +/**
1.1896 + Created, initialises and starts the helper thread.
1.1897 +
1.1898 + @param aFunction pointer to the thread function
1.1899 + @param aThreadParameter parameter to be passed to the thread function. Its interpretation depends on the thread function.
1.1900 + @return KErrNone on success; standard error code otherwise
1.1901 +*/
1.1902 +TInt CFatHelperThreadBase::DoLaunchThread(TThreadFunction aFunction, TAny* aThreadParameter)
1.1903 + {
1.1904 + __PRINT2(_L("#=- CFatHelperThreadBase::DoLaunchThread() thread stat:%d, state:%d"), ThreadCompletionCode(), State());
1.1905 +
1.1906 + ASSERT(aFunction);
1.1907 + ASSERT(State() != EWorking);
1.1908 +
1.1909 + if(ThreadWorking())
1.1910 + {
1.1911 + ASSERT(0);
1.1912 + return KErrInUse;
1.1913 + }
1.1914 +
1.1915 + if(iOwner.OwnerMount()->Drive().IsSynchronous())
1.1916 + {
1.1917 + //-- if the drive is synchronous, this is a main File Server thread. Don't play with it, it has its own scheduler
1.1918 + //-- and completing other requests rather than native CFsRequest leads to the stray events, because it waits on the
1.1919 + //-- User::WaitForAnyRequest and doesn't check which request has completed.
1.1920 + __PRINT(_L("CFatHelperThreadBase::DoLaunchThread() the drive is synchronous, skipping."));
1.1921 + return KErrNotSupported;
1.1922 + }
1.1923 +
1.1924 +
1.1925 + TInt nRes;
1.1926 + TName nameBuf; //-- this will be initial thread name, it will rename itself in its thread function
1.1927 + nameBuf.Format(_L("Fat32HelperThread_drv_%d"), iOwner.OwnerMount()->DriveNumber());
1.1928 + const TInt stackSz = 4*K1KiloByte; //-- thread stack size, 4K
1.1929 +
1.1930 + iThread.Close();
1.1931 +
1.1932 + //-- 1. create the thread
1.1933 + nRes = iThread.Create(nameBuf, aFunction, stackSz, &User::Allocator(), aThreadParameter, EOwnerProcess);
1.1934 + if(nRes != KErrNone)
1.1935 + {
1.1936 + __PRINT1(_L("#=- CFatHelperThreadBase::DoLaunchThread() failure#1 res:%d"), nRes);
1.1937 + iThread.Close();
1.1938 + ASSERT(0);
1.1939 + return nRes;
1.1940 + }
1.1941 +
1.1942 + //-- 2. set up its working environment
1.1943 + AllowToLive(ETrue);
1.1944 + iThread.SetPriority((TThreadPriority)EHelperPriorityNormal); //-- initially the thread has very low priority
1.1945 +
1.1946 + //-- the state of this object now will be controlled by the thread
1.1947 + SetState(ENotStarted);
1.1948 +
1.1949 + //-- 3. resume thread and wait until it finishes its initialisation
1.1950 + TRequestStatus rqStatInit(KRequestPending);
1.1951 +
1.1952 + iThread.Logon(iThreadStatus);
1.1953 + iThread.Rendezvous(rqStatInit);
1.1954 + iThread.Resume();
1.1955 +
1.1956 + User::WaitForRequest(rqStatInit);
1.1957 +
1.1958 + if(rqStatInit.Int() != KErrNone)
1.1959 + {//-- thread couldn't initialise
1.1960 + __PRINT1(_L("#=- CFatHelperThreadBase::DoLaunchThread() failure#2 res:%d"), nRes);
1.1961 + ForceStop();
1.1962 + ASSERT(0);
1.1963 + return nRes;
1.1964 + }
1.1965 +
1.1966 + //-- Helper FAT thread is running now
1.1967 + return KErrNone;
1.1968 + }
1.1969 +
1.1970 +
1.1971 +//#######################################################################################################################################
1.1972 +//# CFat32ScanThread implementation
1.1973 +//#######################################################################################################################################
1.1974 +
1.1975 +
1.1976 +CFat32ScanThread::CFat32ScanThread(CAtaFatTable& aOwner)
1.1977 + :CFatHelperThreadBase(aOwner)
1.1978 + {
1.1979 + }
1.1980 +
1.1981 +//-----------------------------------------------------------------------------
1.1982 +
1.1983 +/**
1.1984 + Launches the FAT32_ScanThread scaner thread.
1.1985 + @return standard error code
1.1986 +*/
1.1987 +TInt CFat32ScanThread::Launch()
1.1988 + {
1.1989 + return DoLaunchThread(FAT32_ScanThread, this);
1.1990 + }
1.1991 +
1.1992 +//-----------------------------------------------------------------------------
1.1993 +
1.1994 +/**
1.1995 + FAT32_ScanThread preamble function. It gets called by the scan thread at the very beginning.
1.1996 + Does some initialisation work and its return code is signaled to the thread owner by RThread::Rendezvous();
1.1997 +
1.1998 + @return Thread object initialisation code, KErrNone on success.
1.1999 +*/
1.2000 +TInt CFat32ScanThread::Thread_Preamble()
1.2001 + {
1.2002 + //__PRINT(_L("#=- CFat32ScanThread::Thread_Preamble()"));
1.2003 +
1.2004 + ipFatBitCache = iOwner.iCache->BitCacheInterface();
1.2005 + iTimeStart.UniversalTime(); //-- take thread start time
1.2006 +
1.2007 + ASSERT(State() == CFatHelperThreadBase::ENotStarted); //-- see the thread launcher
1.2008 +
1.2009 + if(!iOwner.IsFat32())
1.2010 + {//-- this stuff is supposed to work for FAT32 only
1.2011 + ASSERT(0);
1.2012 + return KErrArgument;
1.2013 + }
1.2014 +
1.2015 + return KErrNone;
1.2016 + }
1.2017 +
1.2018 +//-----------------------------------------------------------------------------
1.2019 +/**
1.2020 + FAT32_ScanThread postamble function. It gets called by the scan thread just before its function exits.
1.2021 + Does some finalisation work and its return code is the thread completion code;
1.2022 +
1.2023 + @return Thread object finalisation code, KErrNone on success.
1.2024 +*/
1.2025 +TInt CFat32ScanThread::Thread_Postamble(TInt aResult)
1.2026 + {
1.2027 + //__PRINT(_L("#=- CFat32ScanThread::Thread_Postamble()"));
1.2028 +
1.2029 +#ifdef _DEBUG
1.2030 + //-- print out time taken the thread to finish
1.2031 + TName nameBuf;
1.2032 + iTimeEnd.UniversalTime(); //-- take end time
1.2033 + const TInt msScanTime = (TInt)( (iTimeEnd.MicroSecondsFrom(iTimeStart)).Int64() / K1mSec);
1.2034 + nameBuf.Copy(RThread().Name());
1.2035 + nameBuf.Insert(0,_L("#=-<<<"));
1.2036 + nameBuf.AppendFormat(_L(" Thread Exit. id:%d, Code:%d, time:%d ms"), (TUint)RThread().Id(), aResult, msScanTime);
1.2037 + __PRINT(nameBuf);
1.2038 +#endif
1.2039 +
1.2040 + //-- tell FAT bit supercache (if we have it) that we have finished populating it, successfully or not
1.2041 + if(ipFatBitCache)
1.2042 + {
1.2043 + ipFatBitCache->FinishPopulating(aResult == KErrNone);
1.2044 + ipFatBitCache->Dump();
1.2045 + }
1.2046 +
1.2047 + //-- close FAT chunk buffer
1.2048 + iFatChunkBuf.Close();
1.2049 +
1.2050 + //-- set the host object state depending on the work results.
1.2051 + if(aResult == KErrNone)
1.2052 + SetState(CFatHelperThreadBase::EFinished_OK);
1.2053 + else
1.2054 + SetState(CFatHelperThreadBase::EFailed);
1.2055 +
1.2056 +
1.2057 + return aResult;
1.2058 + }
1.2059 +
1.2060 +//#######################################################################################################################################
1.2061 +//# CFat32FreeSpaceScanner implementation
1.2062 +//#######################################################################################################################################
1.2063 +
1.2064 +CFat32FreeSpaceScanner::CFat32FreeSpaceScanner(CAtaFatTable& aOwner)
1.2065 + :CFat32ScanThread(aOwner)
1.2066 + {
1.2067 + }
1.2068 +
1.2069 +/**
1.2070 + Factory method.
1.2071 + @param aOwner owning CAtaFatTable
1.2072 + @return pointer to the constructed instance of the class
1.2073 +*/
1.2074 +CFat32FreeSpaceScanner* CFat32FreeSpaceScanner::NewL(CAtaFatTable& aOwner)
1.2075 + {
1.2076 + CFat32FreeSpaceScanner* pThis = NULL;
1.2077 + pThis = new (ELeave) CFat32FreeSpaceScanner(aOwner);
1.2078 +
1.2079 + return pThis;
1.2080 + }
1.2081 +
1.2082 +//-----------------------------------------------------------------------------
1.2083 +
1.2084 +/**
1.2085 + Waits until FAT32 free clusters scan thread allows other thread (caller) to write to the FAT entry "aFatIndex".
1.2086 + Thread scans FAT from the beginning to the end and just waits untill scanning passes the entry number "aFatIndex"
1.2087 +
1.2088 + @param aFatIndex index of the FAT entry we are going to write.
1.2089 +*/
1.2090 +void CFat32FreeSpaceScanner::RequestFatEntryWriteAccess(TUint32 aFatIndex) const
1.2091 + {
1.2092 + if(!ThreadWorking())
1.2093 + return;
1.2094 +
1.2095 + ASSERT(iOwner.ClusterNumberValid(aFatIndex));
1.2096 +
1.2097 + const TUint KWaitGranularity = 20*K1mSec; //-- wait granularity
1.2098 +
1.2099 + //-- wait until FAT[aFatIndex] is available to write
1.2100 + while(aFatIndex > ClustersScanned() && ThreadWorking())
1.2101 + {
1.2102 + BoostPriority(ETrue); //-- Boost scan thread priority
1.2103 + User::After(KWaitGranularity);
1.2104 + }
1.2105 + }
1.2106 +
1.2107 +//-----------------------------------------------------------------------------
1.2108 +
1.2109 +/** just an internal helper method. Stores the number of FAT entries already scanned by FAT free entries scan thread. */
1.2110 +void CFat32FreeSpaceScanner::SetClustersScanned(TUint32 aClusters)
1.2111 + {
1.2112 + XAutoLock lock(iOwner.DriveInterface()); //-- enter critical section
1.2113 + iClustersScanned=aClusters;
1.2114 + }
1.2115 +
1.2116 +/** just an internal helper method. returns the number of FAT entries already scanned by FAT free entrie sscan thread. */
1.2117 +TUint32 CFat32FreeSpaceScanner::ClustersScanned() const
1.2118 + {
1.2119 + XAutoLock lock(iOwner.DriveInterface()); //-- enter critical section
1.2120 + return iClustersScanned;
1.2121 + }
1.2122 +
1.2123 +//-----------------------------------------------------------------------------
1.2124 +
1.2125 +/**
1.2126 + overriden FAT32_ScanThread preamble function.
1.2127 + See CFat32ScanThread::Thread_Preamble()
1.2128 +*/
1.2129 +TInt CFat32FreeSpaceScanner::Thread_Preamble()
1.2130 + {
1.2131 + __PRINT1(_L("#=- CFat32FreeSpaceScanner::Thread_Preamble(), FAT state:%d"), iOwner.State());
1.2132 +
1.2133 + ASSERT(iOwner.State() == CAtaFatTable::EFreeClustersScan);
1.2134 +
1.2135 + //-- invoke generic preamble first
1.2136 + TInt nRes = CFat32ScanThread::Thread_Preamble();
1.2137 + if(nRes != KErrNone)
1.2138 + return nRes;
1.2139 +
1.2140 + //-- do specific to this thread object initialisation work
1.2141 +
1.2142 + //-- rename the thread
1.2143 + TName nameBuf;
1.2144 + const CFatMountCB& fatMount = *(iOwner.OwnerMount());
1.2145 + nameBuf.Format(_L("Fat32FreeSpaceScanner_drv_%d"), fatMount.DriveNumber());
1.2146 + RThread::RenameMe(nameBuf);
1.2147 +
1.2148 + //-- allocate FAT chunk buffer; its size will depend on FAT table size.
1.2149 + const TUint32 fatSz = iOwner.MaxEntries() << KFat32EntrySzLog2;
1.2150 +
1.2151 + if(fatSz < KBigSzFat_Threshold)
1.2152 + {//-- create a small buffer
1.2153 + if(iFatChunkBuf.CreateMax(KFatChunkBufSize_Small) != KErrNone)
1.2154 + return KErrNoMemory;
1.2155 + }
1.2156 + else
1.2157 + {//-- try to create larger buffer
1.2158 + if(iFatChunkBuf.CreateMax(KFatChunkBufSize_Big) != KErrNone && iFatChunkBuf.CreateMax(KFatChunkBufSize_Small) != KErrNone)
1.2159 + return KErrNoMemory;
1.2160 + }
1.2161 +
1.2162 +
1.2163 + //-- setup FAT table's parameters
1.2164 + //-- No free clusters yet; be careful with SetFreeClusters(), free clusters count can be
1.2165 + //-- modified from other thread, e.g. from FreeClusterList. Use read-modify-write instead of assignment.
1.2166 + SetClustersScanned(0);
1.2167 + iOwner.SetFreeClusters(0);
1.2168 +
1.2169 + //-- calculate number of FAT entires need to be processed for CMountCB::SetDiskSpaceChange() call.
1.2170 + //-- if number of processed entries in FAT exceeds iEntriesNotifyThreshold, CMountCB::SetDiskSpaceChange()
1.2171 + //-- will be called and the iEntriesNotifyThreshold will be updated.
1.2172 + iNfyThresholdInc = (TUint32)KVolSpaceNotifyThreshold >> fatMount.ClusterSizeLog2();
1.2173 + iEntriesNotifyThreshold = iNfyThresholdInc;
1.2174 +
1.2175 + //-- if there is an interface to the FAT bit supercache, tell it to start populating.
1.2176 + //-- We will be populating this cache while reading and parsing FAT32.
1.2177 + if(ipFatBitCache)
1.2178 + ipFatBitCache->StartPopulating();
1.2179 +
1.2180 +
1.2181 + return KErrNone;
1.2182 + }
1.2183 +
1.2184 +//-----------------------------------------------------------------------------
1.2185 +/**
1.2186 + overriden FAT32_ScanThread postamble function.
1.2187 + See CFat32ScanThread::Thread_Postamble()
1.2188 +*/
1.2189 +TInt CFat32FreeSpaceScanner::Thread_Postamble(TInt aResult)
1.2190 + {
1.2191 + __PRINT2(_L("#=- CFat32FreeSpaceScanner::Thread_Postamble(%d), FAT state:%d"), aResult, iOwner.State());
1.2192 + __PRINT2(_L("#=- FAT_ScanThread: counted Free clusters:%d, 1st free:%d"), iOwner.NumberOfFreeClusters(), iOwner.FreeClusterHint());
1.2193 +
1.2194 + ASSERT(iOwner.State() == CAtaFatTable::EFreeClustersScan);
1.2195 +
1.2196 + //-- there was an error somewhere within FAT32 scan thread
1.2197 + if(aResult != KErrNone)
1.2198 + {
1.2199 + //-- indicate that the FAT table initialisation failed
1.2200 + __PRINT(_L("#=- Asynch FAT table initialisation failed !"));
1.2201 +
1.2202 + iOwner.SetState(CAtaFatTable::EMountAborted);
1.2203 +
1.2204 + //-- fix up some FAT table parameters
1.2205 + if(iOwner.FreeClusterHint() < KFatFirstSearchCluster)
1.2206 + iOwner.SetFreeClusterHint(KFatFirstSearchCluster);
1.2207 +
1.2208 + }
1.2209 +
1.2210 +
1.2211 + //-- call generic postamble
1.2212 + TInt nRes = CFat32ScanThread::Thread_Postamble(aResult);
1.2213 +
1.2214 + if(nRes == KErrNone)
1.2215 + {//-- FAT table now fully initialised
1.2216 + ASSERT(aResult == KErrNone);
1.2217 + iOwner.SetState(CAtaFatTable::EMounted);
1.2218 +
1.2219 + //-- free space counting finished OK, call the notifier last time
1.2220 + CFatMountCB& fatMount = *(iOwner.OwnerMount());
1.2221 +
1.2222 + iOwner.AcquireLock();
1.2223 + const TInt64 currFreeSpace = ((TInt64)iOwner.FreeClusters()) << fatMount.ClusterSizeLog2();
1.2224 + iOwner.ReleaseLock();
1.2225 +
1.2226 + fatMount.SetDiskSpaceChange(currFreeSpace);
1.2227 +
1.2228 +
1.2229 + }
1.2230 + else if(aResult == KErrNone)
1.2231 + {//-- CFat32ScanThread::Thread_Postamble() signaled a fault
1.2232 + iOwner.SetState(CAtaFatTable::EMountAborted);
1.2233 + }
1.2234 +
1.2235 + return aResult;
1.2236 + }
1.2237 +
1.2238 +//-----------------------------------------------------------------------------
1.2239 +/**
1.2240 + Process free FAT entries collected by the scan thread that parses chunk of FAT data.
1.2241 + This method gets called by the FAT scanning thread after a portion of FAT is read into the buffer and parsed
1.2242 +
1.2243 + @param aFreeEntriesInChunk number of free FAT entries counted in FAT chunk
1.2244 + @param aCurrFirstFreeEntry current number of the first free FAT entry found
1.2245 + @param aClustersScanned total number of FAT entries scanned by the thread
1.2246 +
1.2247 + @return standard error code, KErrNone on success
1.2248 +*/
1.2249 +TInt CFat32FreeSpaceScanner::Thread_ProcessCollectedFreeEntries(const CAtaFatTable::TFatScanParam& aFatScanParam)
1.2250 + {
1.2251 + ASSERT(State() == CFatHelperThreadBase::EWorking);
1.2252 +
1.2253 + CAtaFatTable& ataFatTable = iOwner;
1.2254 +
1.2255 + //-------------------------------------------
1.2256 + //-- publish values to the CAtaFatTable object
1.2257 + ataFatTable.AcquireLock();
1.2258 +
1.2259 + //-- publish free cluster count, use read-modify-write here
1.2260 + //-- CFatTable::iFreeClusters can be already modified from other thread.
1.2261 + TUint32 currFreeClusters = ataFatTable.FreeClusters(); //-- simple non-thread safe method
1.2262 +
1.2263 + currFreeClusters += aFatScanParam.iCurrFreeEntries;
1.2264 +
1.2265 + ataFatTable.SetFreeClusters(currFreeClusters);
1.2266 +
1.2267 + //-- store total number of scanned clusters (not to be modified from other thread)
1.2268 + const TUint32 scannedEntries = aFatScanParam.iEntriesScanned;
1.2269 + SetClustersScanned(scannedEntries);
1.2270 +
1.2271 +
1.2272 + if(aFatScanParam.iFirstFree >= KFatFirstSearchCluster)
1.2273 + ataFatTable.SetFreeClusterHint(aFatScanParam.iFirstFree);//-- probably found next free cluster number
1.2274 +
1.2275 + ataFatTable.ReleaseLock();
1.2276 +
1.2277 + //-- check if we need to call CMountCB::SetDiskSpaceChange() to notify it that the amount of processed FAT entries has reached the given threshold
1.2278 + if(scannedEntries >= iEntriesNotifyThreshold)
1.2279 + {
1.2280 + iEntriesNotifyThreshold += iNfyThresholdInc;
1.2281 +
1.2282 + CFatMountCB& fatMount = *(iOwner.OwnerMount());
1.2283 + const TInt64 currFreeSpace = ((TInt64)currFreeClusters) << fatMount.ClusterSizeLog2();
1.2284 + fatMount.SetDiskSpaceChange(currFreeSpace);
1.2285 + }
1.2286 +
1.2287 +
1.2288 + return KErrNone;
1.2289 + }
1.2290 +
1.2291 +//#######################################################################################################################################
1.2292 +//# CFat32BitCachePopulator implementation
1.2293 +//#######################################################################################################################################
1.2294 +CFat32BitCachePopulator::CFat32BitCachePopulator(CAtaFatTable& aOwner)
1.2295 + :CFat32ScanThread(aOwner)
1.2296 + {
1.2297 + }
1.2298 +
1.2299 +/**
1.2300 + Factory method.
1.2301 + @param aOwner owning CAtaFatTable
1.2302 + @return pointer to the constructed instance of the class
1.2303 +*/
1.2304 +CFat32BitCachePopulator* CFat32BitCachePopulator::NewL(CAtaFatTable& aOwner)
1.2305 + {
1.2306 + CFat32BitCachePopulator* pThis = NULL;
1.2307 + pThis = new (ELeave) CFat32BitCachePopulator(aOwner);
1.2308 +
1.2309 + return pThis;
1.2310 + }
1.2311 +
1.2312 +//-----------------------------------------------------------------------------
1.2313 +
1.2314 +/**
1.2315 + The main FS thread tries to write the "aFatIndex" entry in FAT while this thread is running.
1.2316 + We can't do anything useful here, because FAT32 bit supercache doesn't work on FAT entry level and
1.2317 + deals with much less scale - FAT32 cache sector, which can consist from many FAT32 entries.
1.2318 + The conflict situation will be resolved in the CAtaFatTable::WriteL()
1.2319 +*/
1.2320 +void CFat32BitCachePopulator::RequestFatEntryWriteAccess(TUint32 /*aFatIndex*/) const
1.2321 + {
1.2322 + //-- do nothing here, do not block the caller
1.2323 + }
1.2324 +
1.2325 +
1.2326 +//-----------------------------------------------------------------------------
1.2327 +/**
1.2328 + overriden FAT32_ScanThread preamble function.
1.2329 + See CFat32ScanThread::Thread_Preamble()
1.2330 +*/
1.2331 +TInt CFat32BitCachePopulator::Thread_Preamble()
1.2332 + {
1.2333 + __PRINT(_L("#=- CFat32BitCachePopulator::Thread_Preamble()"));
1.2334 +
1.2335 + //-- invoke generic preamble
1.2336 + TInt nRes = CFat32ScanThread::Thread_Preamble();
1.2337 + if(nRes != KErrNone)
1.2338 + return nRes;
1.2339 +
1.2340 + //-- do specific to this thread object initialisation work
1.2341 + iTotalOccupiedFatEntries = 0;
1.2342 +
1.2343 + //-- rename the thread
1.2344 + TName nameBuf;
1.2345 + const CFatMountCB& fatMount = *(iOwner.OwnerMount());
1.2346 + nameBuf.Format(_L("CFat32BitCachePopulator_drv_%d"), fatMount.DriveNumber());
1.2347 + RThread::RenameMe(nameBuf);
1.2348 +
1.2349 + //-- allocate FAT chunk buffer
1.2350 + nRes = iFatChunkBuf.CreateMax(KFatChunkBufSize);
1.2351 + if(nRes != KErrNone)
1.2352 + return nRes;
1.2353 +
1.2354 +
1.2355 + if(!ipFatBitCache)
1.2356 + {//-- this is a bit cache populator and the bit cache object must have been constructed before setting up the populating thread.
1.2357 + ASSERT(0);
1.2358 + return KErrCorrupt;
1.2359 + }
1.2360 +
1.2361 + //-- Tell FAT bit supercache to start populating. We will be populating this cache while reading and parsing FAT32.
1.2362 + if(ipFatBitCache->StartPopulating())
1.2363 + nRes = KErrNone;
1.2364 + else
1.2365 + nRes = KErrCorrupt;
1.2366 +
1.2367 + return nRes;
1.2368 + }
1.2369 +
1.2370 +//-----------------------------------------------------------------------------
1.2371 +
1.2372 +/**
1.2373 + overriden FAT32_ScanThread postamble function.
1.2374 + See CFat32ScanThread::Thread_Postamble()
1.2375 +*/
1.2376 +TInt CFat32BitCachePopulator::Thread_Postamble(TInt aResult)
1.2377 + {
1.2378 + __PRINT1(_L("#=- CFat32BitCachePopulator::Thread_Postamble(%d)"), aResult);
1.2379 +
1.2380 + //-- nothing specific to do, just call generic method
1.2381 + return CFat32ScanThread::Thread_Postamble(aResult);
1.2382 + }
1.2383 +
1.2384 +//-----------------------------------------------------------------------------
1.2385 +/**
1.2386 + This method gets called by the FAT scanning thread after a portion of FAT is read into the buffer and parsed
1.2387 + @return standard error code, KErrNone on success
1.2388 +*/
1.2389 +TInt CFat32BitCachePopulator::Thread_ProcessCollectedFreeEntries(const CAtaFatTable::TFatScanParam& aFatScanParam)
1.2390 + {
1.2391 + ASSERT(State() == CFatHelperThreadBase::EWorking);
1.2392 +
1.2393 + //-- check the bit cache state
1.2394 + if(ipFatBitCache->State() != CFatBitCache::EPopulating)
1.2395 + {//-- something wrong happened to the cache, e.g. someone forcedly invalidated it (probably from another thread)
1.2396 + return KErrAbort;
1.2397 + }
1.2398 +
1.2399 +
1.2400 + //-- if CFat32BitCachePopulator has already counted all _occupied_ FAT entries, there is no need to
1.2401 + //-- continue FAT reading; just mark the rest of the FAT bit supercache as containing free FAT entries and abort scanning
1.2402 +
1.2403 + CAtaFatTable& ataFatTable = iOwner;
1.2404 +
1.2405 + ataFatTable.AcquireLock();
1.2406 +
1.2407 + //-- current amount of non-free entries in FAT, excluding FAT[0] & FAT[1]
1.2408 + const TUint32 KCurrNonFreeEntries = ataFatTable.MaxEntries() - ataFatTable.FreeClusters() - KFatFirstSearchCluster;
1.2409 +
1.2410 + iTotalOccupiedFatEntries += aFatScanParam.iCurrOccupiedEntries;
1.2411 +
1.2412 + //-- check if the thread needs to continue it work
1.2413 + const TBool KNoNeedToScanFurther = (iTotalOccupiedFatEntries >= KCurrNonFreeEntries);
1.2414 +
1.2415 + if(KNoNeedToScanFurther)
1.2416 + {
1.2417 + //-- tell FAT bit supercache to mark the range from currently scanned FAT entry to the end of the FAT as containing free entries.
1.2418 + __PRINT2(_L("#=- CFat32BitCachePopulator::Thread_ProcessCollectedFreeEntries() counted: %d/%d; aborting scan."), iTotalOccupiedFatEntries, KCurrNonFreeEntries);
1.2419 +
1.2420 + const TUint32 entryStart = aFatScanParam.iEntriesScanned; //-- first FAT entry in the range to be marked as 'free'
1.2421 + const TUint32 entryEnd = ataFatTable.MaxEntries()-1; //-- last FAT entry in the range to be marked as 'free', last FAT entry
1.2422 +
1.2423 + ipFatBitCache->MarkFatRange(entryStart, entryEnd, ETrue);
1.2424 +
1.2425 + //-- signal that the thread shall finish with normal (KErrNone) reason
1.2426 + //-- it will also normally finish FAT bit cache populating in postamble
1.2427 + AllowToLive(EFalse);
1.2428 + }
1.2429 +
1.2430 + ataFatTable.ReleaseLock();
1.2431 +
1.2432 +
1.2433 + return KErrNone;
1.2434 + }
1.2435 +
1.2436 +
1.2437 +//#######################################################################################################################################
1.2438 +/**
1.2439 + FAT32 free entries scan thread function. Walks through FAT32 and counts free entries.
1.2440 + It uses its own buffer to read FAT and parse it in order to avoid multithreaded problems with FAT cache and don't thrash it.
1.2441 +
1.2442 + @param apHostObject pointer to the host object of CFat32ScanThread base class.
1.2443 +*/
1.2444 +//#######################################################################################################################################
1.2445 +TInt FAT32_ScanThread(TAny* apHostObject)
1.2446 + {
1.2447 + TInt nRes;
1.2448 +
1.2449 +#ifdef _DEBUG
1.2450 + TName nameBuf;
1.2451 + nameBuf.Copy(RThread().Name());
1.2452 + nameBuf.Insert(0,_L("#=->>>")); nameBuf.AppendFormat(_L(" Thread Enter (id:%d)"), (TUint)RThread().Id());
1.2453 + __PRINT(nameBuf);
1.2454 +#endif
1.2455 +
1.2456 + ASSERT(apHostObject);
1.2457 + CFat32FreeSpaceScanner* pSelf = (CFat32FreeSpaceScanner*)apHostObject;
1.2458 +
1.2459 + CAtaFatTable& ataFatTable = pSelf->iOwner;
1.2460 + CFatMountCB& fatMount = *(ataFatTable.OwnerMount());
1.2461 +
1.2462 + const TUint32 KFat32EntrySz = sizeof(TFat32Entry);
1.2463 + const TUint32 KFat1StartPos = fatMount.StartOfFatInBytes();
1.2464 + const TUint32 KNumClusters = ataFatTable.MaxEntries(); //-- FAT[0] & FAT[1] are reserved and not counted by UsableClusters()
1.2465 +
1.2466 + //-- perform thread preamble work
1.2467 + nRes = pSelf->Thread_Preamble();
1.2468 +
1.2469 + //-- signal the thread initialisation result
1.2470 + RThread::Rendezvous(nRes);
1.2471 +
1.2472 +
1.2473 + //-- Initialisation OK, do real job: FAT scanning
1.2474 + if(nRes == KErrNone)
1.2475 + {
1.2476 + pSelf->SetState(CFatHelperThreadBase::EWorking);
1.2477 +
1.2478 + TUint32 rem = KNumClusters * KFat32EntrySz;
1.2479 + TUint32 mediaPos = KFat1StartPos;
1.2480 +
1.2481 + CAtaFatTable::TFatScanParam fatScanParam; //-- FAT scanning parameters
1.2482 +
1.2483 + //============================================
1.2484 + //=== FAT read and parse loop ================
1.2485 + //-- in this loop we read portions of raw FAT32 data in a buffer, than parse this buffer
1.2486 + //-- in order to find out the number of free FAT entries there and other stuff
1.2487 + while(rem)
1.2488 + {
1.2489 + const TUint32 bytesToRead=Min(rem, (TUint32)pSelf->iFatChunkBuf.Size());
1.2490 + TPtrC8 ptrData(pSelf->iFatChunkBuf.Ptr(), bytesToRead);
1.2491 +
1.2492 + //-- check for sudden media change
1.2493 + if(fatMount.Drive().IsChanged())
1.2494 + {
1.2495 + __PRINT(_L("#=--- FAT32_ScanThread: Media change occured, aborting!"));
1.2496 + nRes = KErrAbort;
1.2497 + break;
1.2498 + }
1.2499 +
1.2500 + //-------------------------------------------
1.2501 + //-- read a portion of FAT into the buffer
1.2502 + ataFatTable.AcquireLock();
1.2503 +
1.2504 + //-- check if the thread was requested to finish
1.2505 + if(!pSelf->AllowedToLive())
1.2506 + {
1.2507 + ataFatTable.ReleaseLock();
1.2508 + nRes = KErrAbort;
1.2509 + break;
1.2510 + }
1.2511 +
1.2512 + //-- actual read
1.2513 + //__PRINT3(_L("#=--- FAT32_ScanThread: read %d bytes pos:0x%x, boost:%d"), bytesToRead, mediaPos, pSelf->IsPriorityBoosted());
1.2514 +
1.2515 + nRes = fatMount.LocalDrive()->Read(mediaPos, bytesToRead, pSelf->iFatChunkBuf);
1.2516 +
1.2517 + ataFatTable.ReleaseLock();
1.2518 +
1.2519 + //-------------------------------------------
1.2520 + //-- analyse the read error code
1.2521 + if(nRes != KErrNone)
1.2522 + {
1.2523 + __PRINT1(_L("#=--- FAT32_ScanThread read error! res:%d"), nRes);
1.2524 + break; //-- abort scanning
1.2525 + }
1.2526 +
1.2527 + //-------------------------------------------
1.2528 + //-- parse FAT from the buffer
1.2529 +
1.2530 + //-- we need number of free and occupied entries in the _current_ FAT chunk being read and parsed
1.2531 + fatScanParam.iCurrFreeEntries = 0;
1.2532 + fatScanParam.iCurrOccupiedEntries = 0;
1.2533 +
1.2534 + ataFatTable.DoParseFatBuf(ptrData, fatScanParam);
1.2535 +
1.2536 + //--- process the the results of FAT buffer parsing
1.2537 + nRes = pSelf->Thread_ProcessCollectedFreeEntries(fatScanParam);
1.2538 + if(nRes != KErrNone || !pSelf->AllowedToLive())
1.2539 + {//-- some types of worker threads may wish to finish normally but prematurely, by the result of Thread_ProcessCollectedFreeEntries()
1.2540 + break; //-- abort scanning
1.2541 + }
1.2542 +
1.2543 +
1.2544 + //-- allow this thread to be preempted by another one that wants to access the media driver.
1.2545 + //-- without this wait we will have priority inversion, because this (low priority) thread continiously reads data by big chunks
1.2546 + //-- and doesn't allow others to access the driver.
1.2547 + //-- On the other hand, if the thread's priority is boosted, there is no reason to be polite.
1.2548 + if(!pSelf->IsPriorityBoosted())
1.2549 + User::After(K1mSec); //-- User::After() granularity can be much coarser than 1ms
1.2550 +
1.2551 + //-------------------------------------------
1.2552 + mediaPos += bytesToRead;
1.2553 + rem -= bytesToRead;
1.2554 +
1.2555 + }//while(rem)
1.2556 +
1.2557 + }//if(nRes == KErrNone)
1.2558 +
1.2559 +
1.2560 + //-- perform thread postamble work
1.2561 + nRes = pSelf->Thread_Postamble(nRes);
1.2562 +
1.2563 + return nRes;
1.2564 + }
1.2565 +
1.2566 +
1.2567 +
1.2568 +
1.2569 +
1.2570 +
1.2571 +
1.2572 +
1.2573 +
1.2574 +
1.2575 +
1.2576 +
1.2577 +
1.2578 +
1.2579 +
1.2580 +
1.2581 +
1.2582 +
1.2583 +
1.2584 +
1.2585 +
1.2586 +
1.2587 +
1.2588 +
1.2589 +
1.2590 +
1.2591 +
1.2592 +
1.2593 +
1.2594 +
1.2595 +
1.2596 +
1.2597 +