sl@0: // Copyright (c) 1996-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of the License "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // f32\sfat32\fat_table32.cpp sl@0: // FAT32 File Allocation Table classes implementation sl@0: // sl@0: // sl@0: sl@0: /** sl@0: @file sl@0: @internalTechnology sl@0: */ sl@0: sl@0: sl@0: sl@0: #include "sl_std.h" sl@0: #include "sl_fatcache32.h" sl@0: #include "fat_table32.h" sl@0: sl@0: sl@0: sl@0: sl@0: //--------------------------------------------------------------------------------------------------------------------------------------- sl@0: /** sl@0: Implements automatic locking object. sl@0: Calls TDriveInterface::AcquireLock() on construction and TDriveInterface::ReleaseLock() on destruction. sl@0: Can be constructed on the stack only. sl@0: */ sl@0: class XAutoLock sl@0: { sl@0: public: sl@0: inline XAutoLock(CFatMountCB* apOwner) : iDrv(apOwner->DriveInterface()) {iDrv.AcquireLock();} sl@0: inline XAutoLock(TDriveInterface& aDrv) : iDrv(aDrv) {iDrv.AcquireLock();} sl@0: inline ~XAutoLock() {iDrv.ReleaseLock();} sl@0: sl@0: private: sl@0: void* operator new(TUint); //-- disable creating objects on heap. sl@0: void* operator new(TUint, void*); sl@0: sl@0: private: sl@0: TDriveInterface &iDrv; ///< reference to the drive interface sl@0: }; sl@0: sl@0: sl@0: //--------------------------------------------------------------------------------------------------------------------------------------- sl@0: sl@0: sl@0: sl@0: //####################################################################################################################################### sl@0: //# CFatTable class implementation sl@0: //####################################################################################################################################### sl@0: sl@0: /** sl@0: FAT object factory method. sl@0: Constructs either CAtaFatTable or CRamFatTable depending on the media type parameter sl@0: sl@0: @param aOwner Pointer to the owning mount sl@0: @param aLocDrvCaps local drive attributes sl@0: @leave KErrNoMemory sl@0: @return Pointer to the Fat table sl@0: */ sl@0: CFatTable* CFatTable::NewL(CFatMountCB& aOwner, const TLocalDriveCaps& aLocDrvCaps) sl@0: { sl@0: CFatTable* pFatTable=NULL; sl@0: sl@0: switch(aLocDrvCaps.iType) sl@0: { sl@0: case EMediaRam: sl@0: {//-- this is RAM media, try to create CRamFatTable instance. sl@0: const TFatType fatType = aOwner.FatType(); sl@0: sl@0: if(fatType != EFat16 && fatType != EFat32) sl@0: {//-- CRamFatTable doesn't support FAT12, FAT16 & FAT32 only. sl@0: __PRINT1(_L("CFatTable::NewL() CRamFatTable doesn't support this FAT type:%d"), fatType); sl@0: ASSERT(0); sl@0: return NULL; sl@0: } sl@0: sl@0: pFatTable = CRamFatTable::NewL(aOwner); sl@0: } sl@0: break; sl@0: sl@0: default: sl@0: //-- other media sl@0: pFatTable = CAtaFatTable::NewL(aOwner); sl@0: break; sl@0: }; sl@0: sl@0: return pFatTable; sl@0: } sl@0: sl@0: sl@0: CFatTable::CFatTable(CFatMountCB& aOwner) sl@0: { sl@0: iOwner = &aOwner; sl@0: ASSERT(iOwner); sl@0: } sl@0: sl@0: CFatTable::~CFatTable() sl@0: { sl@0: //-- destroy cache ignoring dirty data in cache sl@0: //-- the destructor isn't an appropriate place to flush the data. sl@0: Dismount(ETrue); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Initialise the object, get data from the owning CFatMountCB sl@0: */ sl@0: void CFatTable::InitializeL() sl@0: { sl@0: ASSERT(iOwner); sl@0: sl@0: //-- get FAT type from the owner sl@0: iFatType = iOwner->FatType(); sl@0: ASSERT(IsFat12() || IsFat16() || IsFat32()); sl@0: sl@0: //-- set the EOC code sl@0: iFatEocCode = EocCodeByFatType(iFatType); sl@0: sl@0: sl@0: sl@0: sl@0: iFreeClusterHint = KFatFirstSearchCluster; sl@0: sl@0: //-- cache the media attributes sl@0: TLocalDriveCapsV2 caps; sl@0: TPckg capsPckg(caps); sl@0: User::LeaveIfError(iOwner->LocalDrive()->Caps(capsPckg)); sl@0: iMediaAtt = caps.iMediaAtt; sl@0: sl@0: //-- obtain maximal number of entries in the table sl@0: iMaxEntries = iOwner->UsableClusters()+KFatFirstSearchCluster; //-- FAT[0] & FAT[1] are not in use sl@0: sl@0: __PRINT3(_L("CFatTable::InitializeL(), drv:%d, iMediaAtt = %08X, max Entries:%d"), iOwner->DriveNumber(), iMediaAtt, iMaxEntries); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Decrements the free cluster count. sl@0: Note that can be quite expensive operation (especially for overrides with synchronisation), if it is called for every sl@0: cluster of a large file. Use more than one cluster granularity. sl@0: sl@0: @param aCount a number of clusters sl@0: */ sl@0: void CFatTable::DecrementFreeClusterCount(TUint32 aCount) sl@0: { sl@0: __ASSERT_DEBUG(iFreeClusters >= aCount, Fault(EFatCorrupt)); sl@0: iFreeClusters -= aCount; sl@0: } sl@0: sl@0: /** sl@0: Increments the free cluster count. sl@0: Note that can be quite expensive operation (especially for overrides with synchronisation), if it is called for every sl@0: cluster of a large file. Use more than one cluster granularity. sl@0: sl@0: @param aCount a number of clusters sl@0: */ sl@0: void CFatTable::IncrementFreeClusterCount(TUint32 aCount) sl@0: { sl@0: const TUint32 newVal = iFreeClusters+aCount; sl@0: __ASSERT_DEBUG(newVal<=MaxEntries(), Fault(EFatCorrupt)); sl@0: sl@0: iFreeClusters = newVal; sl@0: } sl@0: sl@0: /** @return number of free clusters in the FAT */ sl@0: TUint32 CFatTable::NumberOfFreeClusters(TBool /*aSyncOperation=EFalse*/) const sl@0: { sl@0: return FreeClusters(); sl@0: } sl@0: sl@0: void CFatTable::SetFreeClusters(TUint32 aFreeClusters) sl@0: { sl@0: iFreeClusters=aFreeClusters; sl@0: } sl@0: sl@0: /** sl@0: Get the hint about the last known free cluster number. sl@0: Note that can be quite expensive operation (especially for overrides with synchronisation), if it is called for every sl@0: cluster of a large file. sl@0: sl@0: @return cluster number supposedly close to the free one. sl@0: */ sl@0: TUint32 CFatTable::FreeClusterHint() const sl@0: { sl@0: ASSERT(ClusterNumberValid(iFreeClusterHint)); sl@0: return iFreeClusterHint; sl@0: } sl@0: sl@0: /** sl@0: Set a free cluster hint. The next search fro the free cluster can start from this value. sl@0: aCluster doesn't have to be a precise number of free FAT entry; it just needs to be as close as possible to the sl@0: free entries chain. sl@0: Note that can be quite expensive operation (especially for overrides with synchronisation), if it is called for every sl@0: cluster of a large file. sl@0: sl@0: @param aCluster cluster number hint. sl@0: */ sl@0: void CFatTable::SetFreeClusterHint(TUint32 aCluster) sl@0: { sl@0: ASSERT(ClusterNumberValid(aCluster)); sl@0: iFreeClusterHint=aCluster; sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Find out the number of free clusters on the volume. sl@0: Reads whole FAT and counts free clusters. sl@0: */ sl@0: void CFatTable::CountFreeClustersL() sl@0: { sl@0: __PRINT1(_L("#- CFatTable::CountFreeClustersL(), drv:%d"), iOwner->DriveNumber()); sl@0: sl@0: const TUint32 KUsableClusters = iOwner->UsableClusters(); sl@0: (void)KUsableClusters; sl@0: sl@0: TUint32 freeClusters = 0; sl@0: TUint32 firstFreeCluster = 0; sl@0: sl@0: TTime timeStart; sl@0: TTime timeEnd; sl@0: timeStart.UniversalTime(); //-- take start time sl@0: sl@0: //-- walk through whole FAT table looking for free clusters sl@0: for(TUint i=KFatFirstSearchCluster; iClusterSizeLog2()))) sl@0: { sl@0: endCluster=oldCluster; sl@0: break; sl@0: } sl@0: clusterListLen++; sl@0: } sl@0: anEndCluster=endCluster; sl@0: return(clusterListLen); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Extend a file or directory cluster chain, leaves if there are no free clusters (the disk is full). sl@0: sl@0: @param aNumber amount of clusters to allocate sl@0: @param aCluster FAT entry index to start with. sl@0: sl@0: @leave KErrDiskFull + system wide error codes sl@0: */ sl@0: void CFatTable::ExtendClusterListL(TUint32 aNumber,TInt& aCluster) sl@0: { sl@0: __PRINT2(_L("CFatTable::ExtendClusterListL() num:%d, clust:%d"), aNumber, aCluster); sl@0: __ASSERT_DEBUG(aNumber>0,Fault(EFatBadParameter)); sl@0: sl@0: while(aNumber && GetNextClusterL(aCluster)) sl@0: aNumber--; sl@0: sl@0: if(!aNumber) sl@0: return; sl@0: sl@0: if(!RequestFreeClusters(aNumber)) sl@0: { sl@0: __PRINT(_L("CFatTable::ExtendClusterListL - leaving KErrDirFull")); sl@0: User::Leave(KErrDiskFull); sl@0: } sl@0: sl@0: sl@0: TUint32 freeCluster = 0; sl@0: sl@0: //-- note: this can be impoved by trying to fing as long chain of free clusters as possible in FindClosestFreeClusterL() sl@0: for(TUint i=0; i0, Fault(EFatBadParameter)); sl@0: sl@0: if(!RequestFreeClusters(aNumber)) sl@0: { sl@0: __PRINT(_L("CFatTable::AllocateClusterListL - leaving KErrDirFull")); sl@0: User::Leave(KErrDiskFull); sl@0: } sl@0: sl@0: TInt firstCluster = aNearestCluster = AllocateSingleClusterL(aNearestCluster); sl@0: if (aNumber>1) sl@0: ExtendClusterListL(aNumber-1, (TInt&)aNearestCluster); sl@0: sl@0: return(firstCluster); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Notify the media drive about media areas that shall be treated as "deleted" if this feature is supported. sl@0: @param aFreedClusters array with FAT numbers of clusters that shall be marked as "deleted" sl@0: */ sl@0: void CFatTable::DoFreedClustersNotify(RClusterArray &aFreedClusters) sl@0: { sl@0: ASSERT(iMediaAtt & KMediaAttDeleteNotify); sl@0: sl@0: const TUint clusterCount = aFreedClusters.Count(); sl@0: sl@0: if(!clusterCount) sl@0: return; sl@0: sl@0: FlushL(); //-- Commit the FAT changes to disk first to be safe sl@0: sl@0: const TUint bytesPerCluster = 1 << iOwner->ClusterSizeLog2(); sl@0: sl@0: TInt64 byteAddress = 0; sl@0: TUint deleteLen = 0; // zero indicates no clusters accumulated yet sl@0: sl@0: for (TUint i=0; iClusterBasePosition()) >> iOwner->ClusterSizeLog2()) + 2, cluster); sl@0: sl@0: const TInt r = iOwner->LocalDrive()->DeleteNotify(byteAddress, deleteLen); sl@0: if(r != KErrNone) sl@0: {//-- if DeleteNotify() failed, it means that something terribly wrong happened to the NAND media; sl@0: //-- in normal circumstances it can not happen. One of the reasons: totally worn out media. sl@0: const TBool platSecEnabled = PlatSec::ConfigSetting(PlatSec::EPlatSecEnforcement); sl@0: __PRINT3(_L("CFatTable::DoFreedClustersNotify() DeleteNotify failure! drv:%d err:%d, PlatSec:%d"),iOwner->DriveNumber(), r, platSecEnabled); sl@0: sl@0: if(platSecEnabled) sl@0: { sl@0: //-- if PlatSec is enabled, we can't afford jeopardize the security; without DeleteNotify() sl@0: //-- it's possible to pick up data from deleted files, so, panic the file server. sl@0: Fault(EFatBadLocalDrive); sl@0: } sl@0: else sl@0: { sl@0: //-- if PlatSec is disabled, it's OK to ignore the NAND fault in release mode. sl@0: __ASSERT_DEBUG(0, Fault(EFatBadLocalDrive)); sl@0: } sl@0: } sl@0: sl@0: sl@0: deleteLen = 0; sl@0: } sl@0: sl@0: } sl@0: sl@0: //-- empty the array. sl@0: aFreedClusters.Reset(); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: /** sl@0: Mark a chain of clusters as free in the FAT. sl@0: sl@0: @param aCluster Start cluster of cluster chain to free sl@0: @leave System wide error codes sl@0: */ sl@0: void CFatTable::FreeClusterListL(TUint32 aCluster) sl@0: { sl@0: __PRINT1(_L("CFatTable::FreeClusterListL startCluster=%d"),aCluster); sl@0: if (aCluster == KSpareCluster) sl@0: return; sl@0: sl@0: //-- here we can store array of freed cluster numbers in order to sl@0: //-- notify media drive about the media addresses marked as "invalid" sl@0: RClusterArray deletedClusters; sl@0: CleanupClosePushL(deletedClusters); sl@0: sl@0: //-- if ETrue, we need to notify media driver about invalidated media addressses sl@0: const TBool bFreeClustersNotify = iMediaAtt & KMediaAttDeleteNotify; sl@0: sl@0: //-- this is a maximal number of FAT entries in the deletedClusters array. sl@0: //-- as soon as we collect this number of entries in the array, FAT cache will be flushed sl@0: //-- and driver notified. The array will be emptied. Used to avoid huge array when deleting sl@0: //-- large files on NAND media sl@0: const TUint KSubListLen = 4096; sl@0: ASSERT(IsPowerOf2(KSubListLen)); sl@0: sl@0: TUint32 lastKnownFreeCluster = FreeClusterHint(); sl@0: TUint32 cntFreedClusters = 0; sl@0: sl@0: TUint32 currCluster = aCluster; sl@0: TInt nextCluster = aCluster; sl@0: sl@0: for(;;) sl@0: { sl@0: const TBool bEOF = !GetNextClusterL(nextCluster); sl@0: WriteL(currCluster, KSpareCluster); sl@0: sl@0: lastKnownFreeCluster = Min(currCluster, lastKnownFreeCluster); sl@0: sl@0: // Keep a record of the deleted clusters so that we can subsequently notify the media driver. This is only safe sl@0: // to do once the FAT changes have been written to disk. sl@0: if(bFreeClustersNotify) sl@0: deletedClusters.Append(currCluster); sl@0: sl@0: ++cntFreedClusters; sl@0: currCluster = nextCluster; sl@0: sl@0: if (bEOF || aCluster == KSpareCluster) sl@0: break; sl@0: sl@0: if(bFreeClustersNotify && cntFreedClusters && (cntFreedClusters & (KSubListLen-1))==0) sl@0: {//-- reached a limit of the entries in the array. Flush FAT cache, notify the driver and empty the array. sl@0: IncrementFreeClusterCount(cntFreedClusters); sl@0: cntFreedClusters = 0; sl@0: sl@0: SetFreeClusterHint(lastKnownFreeCluster); sl@0: DoFreedClustersNotify(deletedClusters); sl@0: } sl@0: sl@0: } sl@0: sl@0: //-- increase the number of free clusters and notify the driver if required. sl@0: IncrementFreeClusterCount(cntFreedClusters); sl@0: SetFreeClusterHint(lastKnownFreeCluster); sl@0: sl@0: if(bFreeClustersNotify) sl@0: DoFreedClustersNotify(deletedClusters); sl@0: sl@0: CleanupStack::PopAndDestroy(&deletedClusters); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Find a free cluster nearest to aCluster, Always checks to the right of aCluster first sl@0: but checks in both directions in the Fat. sl@0: sl@0: @param aCluster Cluster to find nearest free cluster to. sl@0: @leave KErrDiskFull + system wide error codes sl@0: @return cluster number found sl@0: */ sl@0: TUint32 CFatTable::FindClosestFreeClusterL(TUint32 aCluster) sl@0: { sl@0: __PRINT2(_L("CFatTable::FindClosestFreeClusterL() drv:%d cl:%d"),iOwner->DriveNumber(),aCluster); sl@0: sl@0: if(!ClusterNumberValid(aCluster)) sl@0: { sl@0: ASSERT(0); sl@0: User::Leave(KErrCorrupt); sl@0: } sl@0: sl@0: if(!RequestFreeClusters(1)) sl@0: {//-- there is no at least 1 free cluster available sl@0: __PRINT(_L("CFatTable::FindClosestFreeClusterL() leaving KErrDiskFull #1")); sl@0: User::Leave(KErrDiskFull); sl@0: } sl@0: sl@0: //-- 1. look if the given index contains a free entry sl@0: if(ReadL(aCluster) != KSpareCluster) sl@0: {//-- no, it doesn't... sl@0: sl@0: //-- 2. look in both directions starting from the aCluster, looking in the right direction first sl@0: sl@0: const TUint32 maxEntries = MaxEntries(); sl@0: const TUint32 MinIdx = KFatFirstSearchCluster; sl@0: const TUint32 MaxIdx = maxEntries-1; sl@0: sl@0: TBool canGoRight = ETrue; sl@0: TBool canGoLeft = ETrue; sl@0: sl@0: TUint32 rightIdx = aCluster; sl@0: TUint32 leftIdx = aCluster; sl@0: sl@0: for(TUint i=0; i MinIdx) sl@0: --leftIdx; sl@0: else sl@0: canGoLeft = EFalse; sl@0: } sl@0: sl@0: if(!canGoRight && !canGoLeft) sl@0: { sl@0: __PRINT(_L("CFatTable::FindClosestFreeClusterL() leaving KErrDiskFull #2")); sl@0: User::Leave(KErrDiskFull); sl@0: } sl@0: sl@0: if(canGoRight && ReadL(rightIdx) == KSpareCluster) sl@0: { sl@0: aCluster = rightIdx; sl@0: break; sl@0: } sl@0: sl@0: if (canGoLeft && ReadL(leftIdx) == KSpareCluster) sl@0: { sl@0: aCluster = leftIdx; sl@0: break; sl@0: } sl@0: }//for(..) sl@0: sl@0: }//if(ReadL(aCluster) != KSpareCluster) sl@0: sl@0: sl@0: //-- note: do not update free cluster hint here by calling SetFreeClusterHint(). This is going to be sl@0: //-- expensive especially if overridden methods with synchronisation are called. Instead, set the number of sl@0: //-- the last known free cluster in the caller of this internal method. sl@0: sl@0: //__PRINT1(_L("CFatTable::FindClosestFreeClusterL found:%d"),aCluster); sl@0: sl@0: return aCluster; sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Converts a cluster number to byte offset in the FAT sl@0: sl@0: @param aFatIndex Cluster number sl@0: @return Number of bytes from the beginning of the FAT sl@0: */ sl@0: TUint32 CFatTable::PosInBytes(TUint32 aFatIndex) const sl@0: { sl@0: switch(FatType()) sl@0: { sl@0: case EFat12: sl@0: return (((aFatIndex>>1)<<1) + (aFatIndex>>1)); //-- 1.5 bytes per FAT entry sl@0: sl@0: case EFat16: sl@0: return aFatIndex<<1; //-- 2 bytes per FAT entry sl@0: sl@0: case EFat32: sl@0: return aFatIndex<<2; //-- 4 bytes per FAT entry sl@0: sl@0: default: sl@0: ASSERT(0); sl@0: return 0;//-- get rid of warning sl@0: }; sl@0: sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Checks if we have at least aClustersRequired clusters free in the FAT. sl@0: This is, actually a dummy implementation. sl@0: sl@0: @param aClustersRequired number of free clusters required sl@0: @return ETrue if there is at least aClustersRequired free clusters available, EFalse otherwise. sl@0: */ sl@0: TBool CFatTable::RequestFreeClusters(TUint32 aClustersRequired) const sl@0: { sl@0: //__PRINT1(_L("#- CFatTable::RequestFreeClusters(%d)"),aClustersRequired); sl@0: ASSERT(aClustersRequired >0); sl@0: return (NumberOfFreeClusters() >= aClustersRequired); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: /** sl@0: @return ETrue if the cluster number aClusterNo is valid, i.e. belongs to the FAT table sl@0: */ sl@0: TBool CFatTable::ClusterNumberValid(TUint32 aClusterNo) const sl@0: { sl@0: return (aClusterNo >= KFatFirstSearchCluster) && (aClusterNo < iMaxEntries); sl@0: } sl@0: sl@0: sl@0: sl@0: //####################################################################################################################################### sl@0: //# CAtaFatTable class implementation sl@0: //####################################################################################################################################### sl@0: sl@0: /** sl@0: Constructor sl@0: */ sl@0: CAtaFatTable::CAtaFatTable(CFatMountCB& aOwner) sl@0: :CFatTable(aOwner), iDriveInteface(aOwner.DriveInterface()) sl@0: { sl@0: iState = ENotInitialised; sl@0: } sl@0: sl@0: sl@0: CAtaFatTable::~CAtaFatTable() sl@0: { sl@0: DestroyHelperThread(); sl@0: } sl@0: sl@0: sl@0: /** factory method */ sl@0: CAtaFatTable* CAtaFatTable::NewL(CFatMountCB& aOwner) sl@0: { sl@0: __PRINT1(_L("CAtaFatTable::NewL() drv:%d"),aOwner.DriveNumber()); sl@0: CAtaFatTable* pSelf = new (ELeave) CAtaFatTable(aOwner); sl@0: sl@0: CleanupStack::PushL(pSelf); sl@0: pSelf->InitializeL(); sl@0: CleanupStack::Pop(); sl@0: sl@0: return pSelf; sl@0: } sl@0: sl@0: sl@0: //--------------------------------------------------------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: CAtaFatTable's FAT cache factory method. sl@0: Creates fixed cache for FAT12/FAT16 or LRU cache for FAT32 sl@0: */ sl@0: void CAtaFatTable::CreateCacheL() sl@0: { sl@0: ASSERT(iOwner); sl@0: const TUint32 fatSize=iOwner->FatSizeInBytes(); sl@0: __PRINT3(_L("CAtaFatTable::CreateCacheL drv:%d, FAT:%d, FAT Size:%d"), iOwner->DriveNumber(), FatType(), fatSize); sl@0: sl@0: sl@0: //-- according to FAT specs: sl@0: //-- FAT12 max size is 4084 entries or 6126 bytes => create fixed cache for whole FAT sl@0: //-- FAT16 min size is 4085 entries or 8170 bytes, max size is 65525 entries or 131048 bytes => create fixed cache for whole FAT sl@0: //-- FAT32 min size is 65526 entries or 262104 bytes => create LRU paged cache of max size: KFat32LRUCacheSize sl@0: sl@0: ASSERT(!iCache); sl@0: sl@0: //-- this is used for chaches granularity sanity check sl@0: const TUint32 KMinGranularityLog2 = KDefSectorSzLog2; //-- 512 bytes is a minimal allowed granularity sl@0: const TUint32 KMaxGranularityLog2 = 18; //-- 256K is a maximal allowed granularity sl@0: sl@0: switch(FatType()) sl@0: { sl@0: //-- create fixed FAT12 cache sl@0: case EFat12: sl@0: iCache = CFat12Cache::NewL(iOwner, fatSize); sl@0: break; sl@0: sl@0: //-- create fixed FAT16 cache sl@0: case EFat16: sl@0: { sl@0: TUint32 fat16_ReadGranularity_Log2; //-- FAT16 cache read granularity Log2 sl@0: TUint32 fat16_WriteGranularity_Log2;//-- FAT16 cache write granularity Log2 sl@0: sl@0: iOwner->FatConfig().Fat16FixedCacheParams(fat16_ReadGranularity_Log2, fat16_WriteGranularity_Log2); sl@0: sl@0: //-- check if granularity values look sensible sl@0: const TBool bParamsValid = fat16_ReadGranularity_Log2 >= KMinGranularityLog2 && fat16_ReadGranularity_Log2 <= KMaxGranularityLog2 && sl@0: fat16_WriteGranularity_Log2 >= KMinGranularityLog2 && fat16_WriteGranularity_Log2 <= KMaxGranularityLog2; sl@0: sl@0: __ASSERT_ALWAYS(bParamsValid, Fault(EFatCache_BadGranularity)); sl@0: sl@0: sl@0: iCache = CFat16FixedCache::NewL(iOwner, fatSize, fat16_ReadGranularity_Log2, fat16_WriteGranularity_Log2); sl@0: } sl@0: break; sl@0: sl@0: //-- create FAT32 LRU paged cache sl@0: case EFat32: sl@0: { sl@0: TUint32 fat32_LRUCache_MaxMemSize; //-- Maximum memory for the LRU FAT32 cache sl@0: TUint32 fat32_ReadGranularity_Log2; //-- FAT32 cache read granularity Log2 sl@0: TUint32 fat32_WriteGranularity_Log2;//-- FAT32 cache write granularity Log2 sl@0: sl@0: iOwner->FatConfig().Fat32LruCacheParams(fat32_ReadGranularity_Log2, fat32_WriteGranularity_Log2, fat32_LRUCache_MaxMemSize); sl@0: sl@0: sl@0: //-- check if granularity and required cache size values look sensible sl@0: const TBool bParamsValid = fat32_ReadGranularity_Log2 >= KMinGranularityLog2 && fat32_ReadGranularity_Log2 <= KMaxGranularityLog2 && sl@0: fat32_WriteGranularity_Log2 >= KMinGranularityLog2 && fat32_WriteGranularity_Log2 <= KMaxGranularityLog2 && sl@0: fat32_LRUCache_MaxMemSize >= 8*K1KiloByte && fat32_LRUCache_MaxMemSize < 4*K1MegaByte; sl@0: sl@0: __ASSERT_ALWAYS(bParamsValid, Fault(EFatCache_BadGranularity)); sl@0: sl@0: iCache = CFat32LruCache::NewL(iOwner, fat32_LRUCache_MaxMemSize, fat32_ReadGranularity_Log2, fat32_WriteGranularity_Log2); sl@0: } sl@0: break; sl@0: sl@0: default: sl@0: ASSERT(0); sl@0: User::Leave(KErrCorrupt); sl@0: break; sl@0: }; sl@0: sl@0: ASSERT(iCache); sl@0: } sl@0: sl@0: //--------------------------------------------------------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Destroys a helper thread object. sl@0: If the thread is running, stops it first. than deletes the ipHelperThread and sets it to NULL sl@0: */ sl@0: void CAtaFatTable::DestroyHelperThread() sl@0: { sl@0: sl@0: if(!ipHelperThread) sl@0: return; sl@0: sl@0: __PRINT1(_L("CAtaFatTable::DestroyHelperThread(), drv:%d"), iOwner->DriveNumber()); sl@0: ipHelperThread->ForceStop(); sl@0: delete ipHelperThread; sl@0: ipHelperThread = NULL; sl@0: } sl@0: sl@0: //--------------------------------------------------------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Flush the FAT cache on disk sl@0: @leave System wide error codes sl@0: */ sl@0: void CAtaFatTable::FlushL() sl@0: { sl@0: __PRINT1(_L("CAtaFatTable::FlushL(), drv:%d"), iOwner->DriveNumber()); sl@0: sl@0: //-- the data can't be written if the mount is inconsistent sl@0: iOwner->CheckStateConsistentL(); sl@0: sl@0: if (iCache) sl@0: iCache->FlushL(); sl@0: } sl@0: sl@0: sl@0: //--------------------------------------------------------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Dismount the cache. Stops any activity, deallocates caches etc. sl@0: @param aDiscardDirtyData if ETrue, non-flushed data in the cache will be discarded. sl@0: */ sl@0: void CAtaFatTable::Dismount(TBool aDiscardDirtyData) sl@0: { sl@0: __PRINT3(_L("#=-= CAtaFatTable::Dismount(%d), drv:%d, state:%d"), aDiscardDirtyData, iOwner->DriveNumber(), State()); sl@0: sl@0: //-- if there is a helper thread, stop it and delete its object sl@0: DestroyHelperThread(); sl@0: sl@0: //-- if there is the cache, close it (it may lead to deallocating its memory) sl@0: if(iCache) sl@0: { sl@0: //-- cache's Close() can check if the cache is clean. sl@0: //-- ignore dirty data in cache if the mount is not in consistent state (it's impossible to flush cache data) sl@0: //-- or if we are asked to do so. sl@0: const TBool bIgnoreDirtyData = aDiscardDirtyData || !iOwner->ConsistentState(); sl@0: iCache->Close(bIgnoreDirtyData); sl@0: sl@0: delete iCache; sl@0: iCache=NULL; sl@0: } sl@0: sl@0: SetState(EDismounted); sl@0: } sl@0: sl@0: //--------------------------------------------------------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Invalidate whole FAT cache. sl@0: Depending of cache type this may just mark cache invalid with reading on demand or re-read whole cache from the media sl@0: */ sl@0: void CAtaFatTable::InvalidateCacheL() sl@0: { sl@0: __PRINT1(_L("CAtaFatTable::InvalidateCache(), drv:%d"), iOwner->DriveNumber()); sl@0: sl@0: //-- if we have a cache, invalidate it entirely sl@0: if(iCache) sl@0: { sl@0: User::LeaveIfError(iCache->Invalidate()); sl@0: } sl@0: sl@0: //-- invalidating whole FAT cache means that something very serious happened. sl@0: //-- if we have a helper thread running, abort it. sl@0: if(ipHelperThread) sl@0: ipHelperThread->ForceStop(); sl@0: sl@0: } sl@0: sl@0: sl@0: //--------------------------------------------------------------------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Invalidate specified region of the FAT cache sl@0: Depending of cache type this may just mark part of the cache invalid with reading on demand later sl@0: or re-read whole cache from the media. sl@0: sl@0: @param aPos absolute media position where the region being invalidated starts. sl@0: @param aLength length in bytes of region to invalidate / refresh sl@0: */ sl@0: void CAtaFatTable::InvalidateCacheL(TInt64 aPos, TUint32 aLength) sl@0: { sl@0: __PRINT3(_L("CAtaFatTable::InvalidateCacheL() drv:%d, pos:%LU, len:%u,"), iOwner->DriveNumber(), aPos, aLength); sl@0: sl@0: if(I64HIGH(aPos) || !aLength || I64HIGH(aPos+aLength)) sl@0: return; //-- FAT tables can't span over 4G sl@0: sl@0: const TUint32 mediaPos32 = I64LOW(aPos); sl@0: sl@0: //-- we do not use other copies of FAT, so trach changes only in FAT1 sl@0: const TUint32 fat1StartPos = iOwner->StartOfFatInBytes(); sl@0: const TUint32 fat1EndPos = fat1StartPos + iOwner->FatSizeInBytes(); sl@0: sl@0: TUint32 invRegionPosStart = 0; //-- media pos where the invalidated region starts sl@0: TUint32 invRegionLen = 0; //-- size of the invalidated region, bytes sl@0: sl@0: //-- calculate the FAT1 region being invalidated sl@0: if(mediaPos32 < fat1StartPos) sl@0: { sl@0: if((mediaPos32 + aLength) <= fat1StartPos) sl@0: return; sl@0: sl@0: invRegionPosStart = fat1StartPos; sl@0: invRegionLen = aLength - (fat1StartPos-mediaPos32); sl@0: } sl@0: else //if(mediaPos32 < fat1StartPos) sl@0: {//-- mediaPos32 >= fat1StartPos) sl@0: if(mediaPos32 >= fat1EndPos) sl@0: return; sl@0: sl@0: invRegionPosStart = mediaPos32; sl@0: sl@0: if((mediaPos32 + aLength) <= fat1EndPos) sl@0: { sl@0: invRegionLen = aLength; sl@0: } sl@0: else sl@0: { sl@0: invRegionLen = mediaPos32+aLength-fat1EndPos; sl@0: } sl@0: } sl@0: sl@0: //-- convert the media pos of the region into FAT entries basis, depending on the FAT type sl@0: ASSERT(invRegionPosStart >= fat1StartPos && invRegionLen <= (TUint)iOwner->FatSizeInBytes()); sl@0: sl@0: TUint32 startFatEntry=0; sl@0: TUint32 numEntries = 0; sl@0: sl@0: switch(FatType()) sl@0: { sl@0: case EFat12: sl@0: //-- invalidate whole cache; it is not worth making calculations for such small memory region. sl@0: User::LeaveIfError(iCache->Invalidate()); sl@0: return; sl@0: sl@0: case EFat16: sl@0: startFatEntry = (invRegionPosStart-fat1StartPos) >> KFat16EntrySzLog2; sl@0: numEntries = (invRegionLen + (sizeof(TFat16Entry)-1)) >> KFat16EntrySzLog2; sl@0: break; sl@0: sl@0: case EFat32: sl@0: startFatEntry = (invRegionPosStart-fat1StartPos) >> KFat32EntrySzLog2; sl@0: numEntries = (invRegionLen + (sizeof(TFat32Entry)-1)) >> KFat32EntrySzLog2; sl@0: break; sl@0: sl@0: default: sl@0: ASSERT(0); sl@0: return; sl@0: }; sl@0: sl@0: if(startFatEntry < KFatFirstSearchCluster) sl@0: {//-- FAT[0] and FAT[1] can't be legally accessed, they are reserved entries. We need to adjust region being refreshed. sl@0: if(numEntries <= KFatFirstSearchCluster) sl@0: return; //-- nothing to refresh sl@0: sl@0: startFatEntry += KFatFirstSearchCluster; sl@0: numEntries -= KFatFirstSearchCluster; sl@0: } sl@0: sl@0: User::LeaveIfError(iCache->InvalidateRegion(startFatEntry, numEntries)); sl@0: } sl@0: sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: /** sl@0: Initialize the object, create FAT cache if required sl@0: @leave KErrNoMemory sl@0: */ sl@0: void CAtaFatTable::InitializeL() sl@0: { sl@0: __PRINT2(_L("CAtaFatTable::InitializeL() drv:%d, state%d"), iOwner->DriveNumber(), State()); sl@0: CFatTable::InitializeL(); sl@0: sl@0: ASSERT(!iCache); sl@0: ASSERT(State() == ENotInitialised); sl@0: sl@0: //-- create the FAT cache. sl@0: CreateCacheL(); sl@0: sl@0: SetState(EInitialised); sl@0: sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: /** sl@0: Mount the FAT table to the CFatMountCB. Depending on mount parameters and configuration this method sl@0: can do various things, like counting free clusters synchronously if data from FSInfo isn't valid, sl@0: or setting up a FAT backround thread and return immediately etc. sl@0: sl@0: @param aMountParam mounting parameters, like some data from FSInfo sl@0: sl@0: */ sl@0: void CAtaFatTable::MountL(const TMountParams& aMountParam) sl@0: { sl@0: __PRINT2(_L("CAtaFatTable::MountL() drv:%d, state:%d"), iOwner->DriveNumber(), State()); sl@0: sl@0: ASSERT(State() == EInitialised); sl@0: SetState(EMounting); sl@0: sl@0: if(ipHelperThread) sl@0: { sl@0: __PRINT(_L("CAtaFatTable::MountL() Helper thread is present!")); sl@0: ASSERT(0); sl@0: DestroyHelperThread(); sl@0: } sl@0: sl@0: sl@0: //-- Check if we have valid data from FSInfo. In this case we don't need to count free clusters sl@0: if(aMountParam.iFsInfoValid) sl@0: { sl@0: ASSERT(IsFat32()); sl@0: ASSERT(aMountParam.iFreeClusters <= MaxEntries()); sl@0: sl@0: ASSERT(ClusterNumberValid(aMountParam.iFirstFreeCluster)); sl@0: sl@0: SetFreeClusters(aMountParam.iFreeClusters); sl@0: SetFreeClusterHint(aMountParam.iFirstFreeCluster); sl@0: sl@0: __PRINT2(_L("CAtaFatTable::MountL() Using data from FSInfo sector. free clusters:%d, 1st free:%d"), FreeClusters(), FreeClusterHint()); sl@0: sl@0: //-- We don't need to scan entire FAT to find out the number of free entries, because the data are taken from FSInfo. sl@0: //-- But if we are going to use the FAT32 bit supercache, we need to populate it. So, try to start up a special sl@0: //-- populating thread. sl@0: CFatBitCache *pFatBitCache = iCache->BitCacheInterface(); sl@0: if(pFatBitCache) sl@0: {//-- bit cache is present, we need to populate (or repopulate it) sl@0: //-- create helper thread object and start the thread sl@0: ipHelperThread = CFat32BitCachePopulator::NewL(*this); sl@0: sl@0: ipHelperThread->Launch(); sl@0: //-- background FAT bit cache populating thread is running now. sl@0: //-- the result of thread start up and completion isn't very interesting: If it fails to sl@0: //-- properly populate the cache, nothing fatal will happen. sl@0: } sl@0: sl@0: //-- CFat32BitCachePopulator doesn't affect FAT table state. sl@0: SetState(EMounted); sl@0: return; sl@0: } sl@0: sl@0: //-- FSInfo data are invalid; we need to count free clusters by reading whole FAT table sl@0: //-- This method can optionally create a background thread (that will count free clusters) and return immideately. sl@0: CountFreeClustersL(); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Decrements the free cluster count. This is an overridden method with synchronisation. sl@0: @param aCount a number of clusters sl@0: */ sl@0: void CAtaFatTable::DecrementFreeClusterCount(TUint32 aCount) sl@0: { sl@0: XAutoLock lock(iOwner); //-- enter critical section sl@0: CFatTable::DecrementFreeClusterCount(aCount); sl@0: } sl@0: sl@0: /** sl@0: Increments the free cluster count. This is an overridden method with synchronisation. sl@0: @param aCount a number of clusters sl@0: */ sl@0: void CAtaFatTable::IncrementFreeClusterCount(TUint32 aCount) sl@0: { sl@0: XAutoLock lock(iOwner); //-- enter critical section sl@0: CFatTable::IncrementFreeClusterCount(aCount); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Obtain number of free clusters on the volume. This is an overridden method. sl@0: Depending on the "aSyncOperation" parameter this operation can be fully synhronous (exact number of free clusters ) or asynchronous sl@0: (current number of free clusters) if the FAT scanning thread is still running. sl@0: sl@0: @param aSyncOperation if ETrue, this method will wait until FAT scan thread finishes and return exact number of free clusters sl@0: if false, it will return current number of free clusters counted by FAT scan thread if it hasn't finished yet. sl@0: sl@0: @return Number of free clusters. See also CAtaFatTable::RequestFreeClusters() sl@0: */ sl@0: TUint32 CAtaFatTable::NumberOfFreeClusters(TBool aSyncOperation/*=EFalse*/) const sl@0: { sl@0: if(ipHelperThread && ipHelperThread->ThreadWorking() && ipHelperThread->Type() == CFatHelperThreadBase::EFreeSpaceScanner) sl@0: {//-- here we have running helper thread that counts free entries in FAT. sl@0: //-- if this operation is synchronous, we need to wait until it finish its job in order to get _exact_ number of free cluster, sl@0: //-- not currently counted sl@0: sl@0: //__PRINT2(_L("#- CAtaFatTable::NumberOfFreeClusters(), drv:%d enter, sync:%d"), iOwner->DriveNumber(), aSyncOperation); sl@0: sl@0: if(aSyncOperation) sl@0: {//-- wait for background scanning thread to finish counting free clusters if this operation is synchronous sl@0: ipHelperThread->BoostPriority(ETrue); sl@0: ipHelperThread->WaitToFinish(); sl@0: } sl@0: sl@0: XAutoLock lock(iOwner); //-- enter critical section sl@0: sl@0: const TUint32 freeClusters = FreeClusters(); sl@0: //__PRINT2(_L("#- CAtaFatTable::NumberOfFreeClusters(), drv:%d Exit, clusters:%d"), iOwner->DriveNumber(), freeClusters); sl@0: return freeClusters; sl@0: } sl@0: sl@0: return FreeClusters(); sl@0: sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Set free cluster count. This is an overridden method with synchronisation. sl@0: @param aFreeClusters new value of free clusters sl@0: */ sl@0: void CAtaFatTable::SetFreeClusters(TUint32 aFreeClusters) sl@0: { sl@0: XAutoLock lock(iOwner); //-- enter critical section sl@0: CFatTable::SetFreeClusters(aFreeClusters); sl@0: } sl@0: sl@0: /** sl@0: This is an overridden method with synchronisation. sl@0: @return the last known free cluster number. sl@0: */ sl@0: TUint32 CAtaFatTable::FreeClusterHint() const sl@0: { sl@0: XAutoLock lock(iOwner); //-- enter critical section sl@0: return CFatTable::FreeClusterHint(); sl@0: } sl@0: sl@0: /** Set next free cluster number. This is an overridden method with synchronisation. */ sl@0: void CAtaFatTable::SetFreeClusterHint(TUint32 aCluster) sl@0: { sl@0: XAutoLock lock(iOwner); //-- enter critical section sl@0: CFatTable::SetFreeClusterHint(aCluster); sl@0: } sl@0: sl@0: /** sl@0: @return ETrue if the state of the object is consistent; i.e. it is sl@0: fully constructed, valid and the amount of free entries is known. sl@0: Used in the case of asynchronous mounting. sl@0: */ sl@0: TBool CAtaFatTable::ConsistentState() const sl@0: { sl@0: return State() == EMounted; sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Request for the raw write access to the FAT area (all copies of FAT). sl@0: If FAT helper thread is running, waits until it finishes. sl@0: sl@0: @param aPos absolute media position we are going to write to. Be careful with casting it from TInt64 and losing high word. sl@0: @param aLen length of the area being written sl@0: */ sl@0: void CAtaFatTable::RequestRawWriteAccess(TInt64 aPos, TUint32 aLen) const sl@0: { sl@0: if(I64HIGH(aPos)) sl@0: return; sl@0: sl@0: const TUint32 pos32 = I64LOW(aPos); sl@0: const TUint32 posFatStart = iOwner->StartOfFatInBytes(); //-- position of the FAT start on the volume sl@0: const TUint32 posFatsEnd = posFatStart + iOwner->NumberOfFats()*iOwner->FatSizeInBytes(); //-- position of the ent of ALL FATs sl@0: sl@0: if(pos32 >= posFatsEnd || (pos32+aLen) <= posFatStart) sl@0: return; sl@0: sl@0: __PRINT2(_L("#=- CAtaFatTable::RequestRawWriteAccess() pos:%d, len:%d"),pos32, aLen); sl@0: sl@0: //-- someone tries to write to FAT area directly. Wait for the FAT helper thread to finish sl@0: if(ipHelperThread) sl@0: ipHelperThread->WaitToFinish(); sl@0: sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Checks if we have at least "aClustersRequired" clusters free in the FAT. sl@0: If FAT scannng thread is running, waits until requested number of free clusters counted or the thread finishes. sl@0: sl@0: @param aClustersRequired number of free clusters required sl@0: @return ETrue if there is at least aClustersRequired free clusters available, EFalse otherwise. sl@0: */ sl@0: TBool CAtaFatTable::RequestFreeClusters(TUint32 aClustersRequired) const sl@0: { sl@0: //__PRINT1(_L("#- CAtaFatTable::RequestFreeClusters(%d)"),aClustersRequired); sl@0: ASSERT(aClustersRequired >0); sl@0: sl@0: if(!ipHelperThread || !ipHelperThread->ThreadWorking() || ipHelperThread->Type() != CFatHelperThreadBase::EFreeSpaceScanner) sl@0: {//-- there is no FAT free space scan thread running, number of free entries can't increase in background sl@0: return (FreeClusters() >= aClustersRequired); //-- use simple, non-thread safe method sl@0: } sl@0: sl@0: //-- FAT free space scan thread is running, counting free FAT entries. wait until it has counted enough or finish. sl@0: ASSERT(ipHelperThread->Type() == CFatHelperThreadBase::EFreeSpaceScanner); sl@0: sl@0: TUint32 currFreeClusters; sl@0: const TUint KWaitGranularity = 20*K1mSec; //-- wait granularity sl@0: sl@0: ipHelperThread->BoostPriority(ETrue); //-- increase thread priority sl@0: sl@0: for(;;) sl@0: { sl@0: currFreeClusters = NumberOfFreeClusters(EFalse); //-- get _current_ number of free clusters asynchronously sl@0: if(currFreeClusters >= aClustersRequired) sl@0: break; //-- OK, the request is satisfied sl@0: sl@0: if(!ipHelperThread->ThreadWorking()) sl@0: {//-- the thread has finished its work sl@0: currFreeClusters = NumberOfFreeClusters(EFalse); //-- get _current_ number of free clusters asynchronously sl@0: break; sl@0: } sl@0: sl@0: User::After(KWaitGranularity); //-- wait some time allowing FAT scanning thread to count free clusters. sl@0: } sl@0: sl@0: ipHelperThread->BoostPriority(EFalse); //-- set thread priority back to normal sl@0: //__PRINT1(_L("#- CAtaFatTable::RequestFreeClusters() #2 curr:%d"),currFreeClusters); sl@0: sl@0: return (currFreeClusters >= aClustersRequired); sl@0: sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Parse a buffer filled with FAT16 or FAT32 entries, counting free clusters and looking for the firs free cluster number. sl@0: Note that this method can be called from a helper FAT scan thread. sl@0: sl@0: @param aBuf FAT buffer descriptor. Must contain whole number of FAT16 or FAT32 entries sl@0: @param aScanParam the structure to be filled with values, like number of counted free and non-free clusters, etc. sl@0: */ sl@0: void CAtaFatTable::DoParseFatBuf(const TPtrC8& aBuf, TFatScanParam& aScanParam) const sl@0: { sl@0: sl@0: if(IsFat16()) sl@0: {//-- we are processing a buffer of FAT16 entries sl@0: ASSERT(!ipHelperThread); sl@0: ASSERT((aBuf.Size() & (sizeof(TFat16Entry)-1)) == 0); sl@0: const TInt KNumEntries = aBuf.Size() >> KFat16EntrySzLog2; sl@0: const TFat16Entry* const pFatEntry = (const TFat16Entry*)(aBuf.Ptr()); sl@0: sl@0: for(TInt i=0; i= KFatFirstSearchCluster) sl@0: { sl@0: const TFat16Entry entry = pFatEntry[i]; sl@0: sl@0: if(entry == KSpareCluster) sl@0: {//-- found a free FAT entry sl@0: aScanParam.iCurrFreeEntries++; sl@0: sl@0: if(aScanParam.iFirstFree < KFatFirstSearchCluster) sl@0: aScanParam.iFirstFree = aScanParam.iEntriesScanned; sl@0: sl@0: } sl@0: else sl@0: {//-- found occupied FAT entry, count bad clusters as well sl@0: aScanParam.iCurrOccupiedEntries++; sl@0: } sl@0: sl@0: } sl@0: sl@0: aScanParam.iEntriesScanned++; sl@0: } sl@0: }//if(IsFat16()) sl@0: else sl@0: if(IsFat32()) sl@0: {//-- we are processing a buffer of FAT32 entries. sl@0: //-- note that here we can be in the context of the FAT free entries scan thread. sl@0: ASSERT((aBuf.Size() & (sizeof(TFat32Entry)-1)) == 0); sl@0: sl@0: //-- pointer to the FAT32 bit supercache. If present, we will populate it here sl@0: CFatBitCache *pFatBitCache = iCache->BitCacheInterface(); sl@0: sl@0: const TInt KNumEntries = aBuf.Size() >> KFat32EntrySzLog2; sl@0: const TFat32Entry* const pFatEntry = (const TFat32Entry*)(aBuf.Ptr()); sl@0: sl@0: for(TInt i=0; i= KFatFirstSearchCluster) sl@0: { sl@0: const TFat32Entry entry = pFatEntry[i] & KFat32EntryMask; sl@0: sl@0: if(entry == KSpareCluster) sl@0: {//-- found a free FAT32 entry sl@0: ++aScanParam.iCurrFreeEntries; sl@0: sl@0: if(aScanParam.iFirstFree < KFatFirstSearchCluster) sl@0: aScanParam.iFirstFree = aScanParam.iEntriesScanned; sl@0: sl@0: sl@0: //-- feed the information about free FAT entry at index aClustersScanned to the FAT bit supercache. sl@0: if(pFatBitCache) sl@0: { sl@0: pFatBitCache->SetFreeFatEntry(aScanParam.iEntriesScanned); sl@0: } sl@0: sl@0: sl@0: }//if(entry == KSpareCluster) sl@0: else sl@0: {//-- found occupied FAT32 entry, count bad clusters as well sl@0: aScanParam.iCurrOccupiedEntries++; sl@0: } sl@0: } sl@0: sl@0: ++aScanParam.iEntriesScanned; sl@0: } sl@0: sl@0: }//if(IsFat32()) sl@0: else sl@0: { sl@0: ASSERT(0); sl@0: } sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Count free clusters in FAT16 or FAT32. Uses relatively large buffer to read FAT entries into; sl@0: This is faster than usual ReadL() calls. sl@0: */ sl@0: void CAtaFatTable::DoCountFreeClustersL() sl@0: { sl@0: __PRINT2(_L("#- CAtaFatTable::DoCountFreeClustersL() drv:%d, state:%d"), iOwner->DriveNumber(), State()); sl@0: sl@0: if(!IsFat16() && !IsFat32()) sl@0: { sl@0: ASSERT(0); sl@0: User::Leave(KErrNotSupported); sl@0: } sl@0: sl@0: const TUint32 KFat1StartPos = iOwner->StartOfFatInBytes(); sl@0: const TUint32 KNumClusters = MaxEntries(); //-- FAT[0] & FAT[1] are reserved and not counted by UsableClusters() sl@0: const TUint32 KNumFATs = iOwner->NumberOfFats(); sl@0: const TUint32 KFatSize = KNumClusters * (IsFat32() ? sizeof(TFat32Entry) : sizeof(TFat16Entry)); //-- usable size of one FAT. sl@0: sl@0: (void)KNumFATs; sl@0: sl@0: ASSERT(KFat1StartPos >= 1*KDefaultSectorSize); sl@0: ASSERT(KNumClusters > KFatFirstSearchCluster); sl@0: ASSERT(KNumFATs > 0); sl@0: sl@0: const TUint32 KFatBufSz = 32*K1KiloByte; //-- buffer size for FAT reading. 32K seems to be optimal size sl@0: sl@0: __ASSERT_COMPILE((KFatBufSz % sizeof(TFat32Entry)) == 0); sl@0: __ASSERT_COMPILE((KFatBufSz % sizeof(TFat16Entry)) == 0); sl@0: sl@0: RBuf8 buf; sl@0: CleanupClosePushL(buf); sl@0: sl@0: //-- allocate memory for FAT parse buffer sl@0: buf.CreateMaxL(KFatBufSz); sl@0: sl@0: //-- read FAT into the large buffer and parse it sl@0: TUint32 rem = KFatSize; sl@0: TUint32 mediaPos = KFat1StartPos; sl@0: sl@0: //-- prepare FAT bit supercache to being populated. sl@0: //-- actual populating will happen in ::DoParseFatBuf() sl@0: CFatBitCache *pFatBitCache = iCache->BitCacheInterface(); sl@0: sl@0: if(pFatBitCache) sl@0: { sl@0: pFatBitCache->StartPopulating(); sl@0: } sl@0: sl@0: TFatScanParam fatScanParam; sl@0: sl@0: //-- used for measuring time sl@0: TTime timeStart; sl@0: TTime timeEnd; sl@0: timeStart.UniversalTime(); //-- take start time sl@0: sl@0: sl@0: while(rem) sl@0: { sl@0: const TUint32 bytesToRead=Min(rem, KFatBufSz); sl@0: TPtrC8 ptrData(buf.Ptr(), bytesToRead); sl@0: sl@0: //__PRINT2(_L("#=--- CAtaFatTable::DoCountFreeClustersL() read %d bytes pos:0x%x"), bytesToRead, (TUint32)mediaPos); sl@0: User::LeaveIfError(iOwner->LocalDrive()->Read(mediaPos, bytesToRead, buf)); sl@0: sl@0: DoParseFatBuf(ptrData, fatScanParam); sl@0: sl@0: mediaPos += bytesToRead; sl@0: rem -= bytesToRead; sl@0: } sl@0: sl@0: //-- here fatScanParam contains values for the whole FAT. sl@0: sl@0: timeEnd.UniversalTime(); //-- take end time sl@0: const TInt msScanTime = (TInt)( (timeEnd.MicroSecondsFrom(timeStart)).Int64() / K1mSec); sl@0: (void)msScanTime; sl@0: __PRINT1(_L("#- CAtaFatTable::DoCountFreeClustersL() finished. Taken:%d ms "), msScanTime); sl@0: sl@0: sl@0: //-- tell FAT bit cache that we have finished populating it sl@0: if(pFatBitCache) sl@0: { sl@0: pFatBitCache->FinishPopulating(ETrue); sl@0: pFatBitCache->Dump(); sl@0: } sl@0: sl@0: if(!fatScanParam.iFirstFree)//-- haven't found free clusters on the volume sl@0: fatScanParam.iFirstFree = KFatFirstSearchCluster; sl@0: sl@0: ASSERT(fatScanParam.iCurrFreeEntries <= iOwner->UsableClusters()); sl@0: ASSERT(ClusterNumberValid(fatScanParam.iFirstFree)); sl@0: sl@0: SetFreeClusters(fatScanParam.iCurrFreeEntries); sl@0: SetFreeClusterHint(fatScanParam.iFirstFree); sl@0: sl@0: CleanupStack::PopAndDestroy(&buf); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Count free clusters on the volume. sl@0: Depending on FAT type can count clusters synchronously or start a thread to do it in background. sl@0: */ sl@0: void CAtaFatTable::CountFreeClustersL() sl@0: { sl@0: __PRINT3(_L("#=- CAtaFatTable::CountFreeClustersL() drv:%d, FAT%d, state:%d"),iOwner->DriveNumber(),FatType(), State()); sl@0: sl@0: ASSERT(State() == EMounting); sl@0: ASSERT(!ipHelperThread); sl@0: sl@0: TInt nRes; sl@0: sl@0: switch(FatType()) sl@0: { sl@0: case EFat12: //-- use old default scanning, it is synchronous sl@0: CFatTable::CountFreeClustersL(); sl@0: SetState(EMounted); sl@0: break; sl@0: sl@0: case EFat16: //-- enhanced FAT scan, but still synchronous sl@0: TRAP(nRes, DoCountFreeClustersL()); sl@0: if(nRes !=KErrNone) sl@0: { sl@0: CFatTable::CountFreeClustersL(); //-- fall back to the legacy method sl@0: } sl@0: sl@0: SetState(EMounted); sl@0: break; sl@0: sl@0: case EFat32: //-- This is FAT32, try to set up a FAT scanning thread if allowed sl@0: { sl@0: TBool bFat32BkGndScan = ETrue; //-- if true, we will try to start up a background scanning thread. sl@0: sl@0: //-- 1. check if background FAT scanning is disabled in config sl@0: if(!iOwner->FatConfig().FAT32_AsynchMount()) sl@0: { sl@0: __PRINT(_L("#=- FAT32 BkGnd scan is disabled in config.")); sl@0: bFat32BkGndScan = EFalse; sl@0: } sl@0: sl@0: //-- 2. check if background FAT scanning is disabled by test interface sl@0: #ifdef _DEBUG sl@0: TInt nMntDebugFlags; sl@0: if(bFat32BkGndScan && RProperty::Get(KSID_Test1, iOwner->DriveNumber(), nMntDebugFlags) == KErrNone) sl@0: {//-- test property for this drive is defined sl@0: if(nMntDebugFlags & KMntDisable_FatBkGndScan) sl@0: { sl@0: __PRINT(_L("#- FAT32 BkGnd scan is disabled is disabled by debug interface.")); sl@0: bFat32BkGndScan = EFalse; sl@0: } sl@0: sl@0: } sl@0: #endif sl@0: //-- 3. try to start FAT32 free entries scanning thread. sl@0: if(bFat32BkGndScan) sl@0: { sl@0: __PRINT(_L("#=- Starting up FAT32 free entries scanner thread...")); sl@0: TRAP(nRes, DoLaunchFat32FreeSpaceScanThreadL()); sl@0: if(nRes == KErrNone) sl@0: break; //-- let thread run by itself sl@0: sl@0: //-- DoLaunchFat32FreeSpaceScanThreadL() has set this object state. sl@0: } sl@0: sl@0: //-- we either failed to launch the thread or this feature was disabled somehow. Fall back to the synchronous scan. sl@0: TRAP(nRes, DoCountFreeClustersL()); sl@0: if(nRes !=KErrNone) sl@0: { sl@0: CFatTable::CountFreeClustersL(); //-- fall back to the legacy method sl@0: } sl@0: sl@0: SetState(EMounted); sl@0: }//case EFat32 sl@0: break; sl@0: sl@0: default: sl@0: ASSERT(0); sl@0: break; sl@0: sl@0: } //switch(FatType()) sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Set up and start FAT scan thread. sl@0: Leaves on error. sl@0: */ sl@0: void CAtaFatTable::DoLaunchFat32FreeSpaceScanThreadL() sl@0: { sl@0: __PRINT2(_L("#=- CAtaFatTable::DoLaunchFat32FreeSpaceScanThreadL() drv:%d, state:%d"),iOwner->DriveNumber(), State()); sl@0: ASSERT(State() == EMounting); sl@0: sl@0: //-- 1. check if something is already working (shan't be!) sl@0: if(ipHelperThread) sl@0: { sl@0: if(ipHelperThread->ThreadWorking()) sl@0: { sl@0: __PRINT(_L("#=- CAtaFatTable::DoLaunchScanThread() some thread is already running ?")); sl@0: ASSERT(0); sl@0: User::Leave(KErrAlreadyExists); sl@0: } sl@0: sl@0: DestroyHelperThread(); sl@0: } sl@0: sl@0: //-- 2. create helper thread object and start the thread sl@0: ipHelperThread = CFat32FreeSpaceScanner::NewL(*this); sl@0: sl@0: SetState(EFreeClustersScan); sl@0: sl@0: ipHelperThread->Launch(); sl@0: //-- background FAT scanning thread is running now sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: /** sl@0: Read an entry from the FAT table sl@0: sl@0: @param aFatIndex FAT entry number to read sl@0: @return FAT entry value sl@0: */ sl@0: TUint32 CAtaFatTable::ReadL(TUint32 aFatIndex) const sl@0: { sl@0: if(!ClusterNumberValid(aFatIndex)) sl@0: { sl@0: //ASSERT(0); //-- deliberately corrupted (by some tests) DOS directory entries can have 0 in the "first cluster" field. sl@0: __PRINT1(_L("CAtaFatTable::ReadL(%d) bad Index!"), aFatIndex); sl@0: User::Leave(KErrCorrupt); sl@0: } sl@0: sl@0: sl@0: const TUint entry = iCache->ReadEntryL(aFatIndex); sl@0: return entry; sl@0: } sl@0: sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: /** sl@0: Write an entry to the FAT table sl@0: sl@0: @param aFatIndex aFatIndex FAT entry number to write sl@0: @param aValue FAT entry to write sl@0: @leave sl@0: */ sl@0: void CAtaFatTable::WriteL(TUint32 aFatIndex, TUint32 aValue) sl@0: { sl@0: sl@0: __PRINT2(_L("CAtaFatTable::WriteL() entry:%d, val:0x%x"), aFatIndex, aValue); sl@0: sl@0: if(!ClusterNumberValid(aFatIndex)) sl@0: { sl@0: ASSERT(0); sl@0: User::Leave(KErrCorrupt); sl@0: } sl@0: sl@0: if(aValue != KSpareCluster && (aValue < KFatFirstSearchCluster || aValue > KFat32EntryMask)) sl@0: { sl@0: ASSERT(0); sl@0: User::Leave(KErrCorrupt); sl@0: } sl@0: sl@0: //-- wait until we are allowed to write FAT entry sl@0: if(ipHelperThread && ipHelperThread->ThreadWorking()) sl@0: { sl@0: ASSERT(ipHelperThread->ThreadId() != RThread().Id()); //-- this method must not be called the FAT helper thread sl@0: ipHelperThread->RequestFatEntryWriteAccess(aFatIndex); sl@0: } sl@0: sl@0: //-- write entry to the FAT through FAT cache sl@0: iCache->WriteEntryL(aFatIndex, aValue); sl@0: sl@0: sl@0: //-- if we are writing "spare" FAT entry, tell FAT bit supercache about it. sl@0: //-- it will store the information that corresponding FAT cache sector has a spare FAT entry. sl@0: //-- writing non-spare FAT entry doesn't mean anything: that FAT cache sector might or might not contain free entries. sl@0: if(aValue == KSpareCluster && iCache->BitCacheInterface()) sl@0: { sl@0: CFatBitCache *pFatBitCache = iCache->BitCacheInterface(); sl@0: const CFatBitCache::TState cacheState= pFatBitCache->State(); sl@0: if(cacheState == CFatBitCache::EPopulated || cacheState == CFatBitCache::EPopulating) sl@0: {//-- bit cache is either normally populated or being populated by one of the helper threads sl@0: if(ipHelperThread && ipHelperThread->ThreadWorking()) sl@0: { sl@0: //-- here we have a multithreading issue. Helper FAT thread can be parsing FAT and optionally calling ReportFreeFatEntry(..) as well. sl@0: //-- in this case we need either to suspend the helper thread in order to prevent corruption of the FAT bit cache data, sl@0: //-- 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 sl@0: //-- fixed during conflict resolution (this can lead to performance degradation). sl@0: sl@0: //-- ok, suspend the helper thread while we are changing data in the bit cache sl@0: AcquireLock(); sl@0: ipHelperThread->Suspend(); sl@0: pFatBitCache->SetFreeFatEntry(aFatIndex); sl@0: ipHelperThread->Resume(); sl@0: ReleaseLock(); sl@0: sl@0: } sl@0: else sl@0: {//-- no one else is accessing FAT in this time sl@0: ASSERT(pFatBitCache->UsableState()); sl@0: pFatBitCache->SetFreeFatEntry(aFatIndex); sl@0: } sl@0: } sl@0: sl@0: }//if(aValue == KSpareCluster) sl@0: sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: /** sl@0: This is an overridden method from CFatTable. See CFatTable::FindClosestFreeClusterL(...) sl@0: Does the same, i.e looks for the closest to "aCluster" free FAT entry, but more advanced, sl@0: it can use FAT bit supercache for quick lookup. sl@0: sl@0: @param aCluster Cluster to find nearest free cluster to. sl@0: @leave KErrDiskFull + system wide error codes sl@0: @return cluster number found sl@0: */ sl@0: TUint32 CAtaFatTable::FindClosestFreeClusterL(TUint32 aCluster) sl@0: { sl@0: __PRINT2(_L("CAtaFatTable::FindClosestFreeClusterL() drv:%d cl:%d"),iOwner->DriveNumber(),aCluster); sl@0: sl@0: if(!ClusterNumberValid(aCluster)) sl@0: { sl@0: ASSERT(0); sl@0: User::Leave(KErrCorrupt); sl@0: } sl@0: sl@0: sl@0: if(!RequestFreeClusters(1)) sl@0: {//-- there is no at least 1 free cluster available sl@0: __PRINT(_L("CAtaFatTable::FindClosestFreeClusterL() leaving KErrDiskFull #1")); sl@0: User::Leave(KErrDiskFull); sl@0: } sl@0: sl@0: //-- check if we have FAT bit supercache and it is in consistent state sl@0: CFatBitCache *pFatBitCache = iCache->BitCacheInterface(); sl@0: if(!pFatBitCache) sl@0: return CFatTable::FindClosestFreeClusterL(aCluster); //-- fall back to the old search method sl@0: sl@0: ASSERT(IsFat32()); sl@0: sl@0: if(!pFatBitCache->UsableState()) sl@0: { sl@0: //__PRINT(_L("#++ CAtaFatTable::FindClosestFreeClusterL() FAT bit cache isn't consistent!")); sl@0: return CFatTable::FindClosestFreeClusterL(aCluster); //-- fall back to the old search method sl@0: } sl@0: sl@0: //-- ask FAT bit supercache to find us FAT cache sector (closest to the aCluster) that contains free FAT entries. sl@0: //__PRINT2(_L("#++ CAtaFatTable::FindClosestFreeClusterL(%d) hint free cl:%d"), aCluster, FreeClusterHint()); sl@0: sl@0: const TInt KMaxLookupRetries = 2; sl@0: for(TInt i=0; iFindClosestFreeFatEntry(aCluster); sl@0: switch(nRes) sl@0: { sl@0: case KErrNone: sl@0: //-- FAT bit supercache has found a free FAT entry in the FAT32 cache sl@0: //__PRINT1(_L("#++ CAtaFatTable::FindClosestFreeClusterL FOUND! cl:%d"), aCluster); sl@0: sl@0: ASSERT(ClusterNumberValid(aCluster)); sl@0: sl@0: //-- do not update the last known free cluster, it can be quite expensive. sl@0: //-- do it in the caller method with bigger granularity. sl@0: return aCluster; sl@0: sl@0: case KErrNotFound: sl@0: //-- 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. sl@0: //-- It can happen because FAT bit cache entry is marked '1' only on populating the bit vector or if someone writes KSpareCluster into the sl@0: //-- corresponding FAT cache sector. Such conflict can happen quite often. sl@0: //-- 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. sl@0: break; sl@0: sl@0: case KErrCorrupt: sl@0: //-- pFatBitCache->FindClosestFreeFatEntry failed to read a page from the media sl@0: //-- break out from the loop and fall back to old search just in case. sl@0: sl@0: case KErrEof: sl@0: //-- there are no '1' entries in whole FAT bit cache vector at all, which is quite unlikely sl@0: //-- break out from the loop and fall back to old search. sl@0: i=KMaxLookupRetries; sl@0: break; sl@0: sl@0: //-- unexpected result code. sl@0: default: sl@0: ASSERT(0); sl@0: i=KMaxLookupRetries; sl@0: break; sl@0: sl@0: sl@0: };//switch(nRes) sl@0: sl@0: }//for(TInt i=0; iClusterBasePosition(); sl@0: return(((TInt64(aCluster)-KFatFirstSearchCluster) << iOwner->ClusterSizeLog2()) + clusterBasePosition); sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: //####################################################################################################################################### sl@0: //# CFatHelperThreadBase implementation sl@0: //####################################################################################################################################### sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: CFatHelperThreadBase::CFatHelperThreadBase(CAtaFatTable& aOwner) sl@0: :iOwner(aOwner) sl@0: { sl@0: sl@0: SetState(EInvalid); sl@0: } sl@0: sl@0: CFatHelperThreadBase::~CFatHelperThreadBase() sl@0: { sl@0: Close(); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: /** sl@0: Closes the thread object handle. sl@0: The thread shall not be running. sl@0: */ sl@0: void CFatHelperThreadBase::Close() sl@0: { sl@0: if(ThreadWorking()) sl@0: { sl@0: ASSERT(0); sl@0: ForceStop(); sl@0: } sl@0: sl@0: iThread.Close(); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: /** sl@0: Waits for the thread to finish (thread function exit). if it is running. sl@0: @return thread completion code. sl@0: sl@0: !!!! definitely need a timeout processing here to avoid any possibitlity of hanging forever !! sl@0: sl@0: */ sl@0: TInt CFatHelperThreadBase::WaitToFinish() const sl@0: { sl@0: if(!ThreadWorking()) sl@0: return ThreadCompletionCode(); sl@0: sl@0: sl@0: //--todo: use timeout and assert to avoid hanging forever ? sl@0: __PRINT1(_L("#= CFatHelperThreadBase::WaitToFinish(), stat:%d"),iThreadStatus.Int()); sl@0: User::WaitForRequest(iThreadStatus); sl@0: return iThreadStatus.Int(); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Requests the fat helper thread function to finish gracefully ASAP; then closes the thread handle. sl@0: Just sets a flag that is analysed by the thread function and waits thread's request completion. sl@0: */ sl@0: void CFatHelperThreadBase::ForceStop() sl@0: { sl@0: if(ThreadWorking()) sl@0: { sl@0: DBG_STATEMENT(TName name = iThread.Name();) sl@0: __PRINT3(_L("#=!! CFatHelperThreadBase::ForceStop() id:%u, name:%S, status:%d"), (TUint)iThread.Id(), &name, ThreadCompletionCode()); sl@0: DBG_STATEMENT(name.Zero()); //-- to avoid warning sl@0: sl@0: iOwner.AcquireLock(); sl@0: sl@0: AllowToLive(EFalse) ; //-- signal the thread to exit ASAP sl@0: sl@0: iOwner.ReleaseLock(); sl@0: sl@0: WaitToFinish(); //-- wait for the thread to finish. sl@0: sl@0: //-- don't know why but we need a delay, at least on the emulator. Otherwise thread object doesn't look destroyed. sl@0: //-- probably something with scheduling. sl@0: User::After(10*K1mSec); sl@0: } sl@0: sl@0: iThread.Close(); sl@0: } sl@0: sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: sl@0: sl@0: /** sl@0: Created, initialises and starts the helper thread. sl@0: sl@0: @param aFunction pointer to the thread function sl@0: @param aThreadParameter parameter to be passed to the thread function. Its interpretation depends on the thread function. sl@0: @return KErrNone on success; standard error code otherwise sl@0: */ sl@0: TInt CFatHelperThreadBase::DoLaunchThread(TThreadFunction aFunction, TAny* aThreadParameter) sl@0: { sl@0: __PRINT2(_L("#=- CFatHelperThreadBase::DoLaunchThread() thread stat:%d, state:%d"), ThreadCompletionCode(), State()); sl@0: sl@0: ASSERT(aFunction); sl@0: ASSERT(State() != EWorking); sl@0: sl@0: if(ThreadWorking()) sl@0: { sl@0: ASSERT(0); sl@0: return KErrInUse; sl@0: } sl@0: sl@0: if(iOwner.OwnerMount()->Drive().IsSynchronous()) sl@0: { sl@0: //-- if the drive is synchronous, this is a main File Server thread. Don't play with it, it has its own scheduler sl@0: //-- and completing other requests rather than native CFsRequest leads to the stray events, because it waits on the sl@0: //-- User::WaitForAnyRequest and doesn't check which request has completed. sl@0: __PRINT(_L("CFatHelperThreadBase::DoLaunchThread() the drive is synchronous, skipping.")); sl@0: return KErrNotSupported; sl@0: } sl@0: sl@0: sl@0: TInt nRes; sl@0: TName nameBuf; //-- this will be initial thread name, it will rename itself in its thread function sl@0: nameBuf.Format(_L("Fat32HelperThread_drv_%d"), iOwner.OwnerMount()->DriveNumber()); sl@0: const TInt stackSz = 4*K1KiloByte; //-- thread stack size, 4K sl@0: sl@0: iThread.Close(); sl@0: sl@0: //-- 1. create the thread sl@0: nRes = iThread.Create(nameBuf, aFunction, stackSz, &User::Allocator(), aThreadParameter, EOwnerProcess); sl@0: if(nRes != KErrNone) sl@0: { sl@0: __PRINT1(_L("#=- CFatHelperThreadBase::DoLaunchThread() failure#1 res:%d"), nRes); sl@0: iThread.Close(); sl@0: ASSERT(0); sl@0: return nRes; sl@0: } sl@0: sl@0: //-- 2. set up its working environment sl@0: AllowToLive(ETrue); sl@0: iThread.SetPriority((TThreadPriority)EHelperPriorityNormal); //-- initially the thread has very low priority sl@0: sl@0: //-- the state of this object now will be controlled by the thread sl@0: SetState(ENotStarted); sl@0: sl@0: //-- 3. resume thread and wait until it finishes its initialisation sl@0: TRequestStatus rqStatInit(KRequestPending); sl@0: sl@0: iThread.Logon(iThreadStatus); sl@0: iThread.Rendezvous(rqStatInit); sl@0: iThread.Resume(); sl@0: sl@0: User::WaitForRequest(rqStatInit); sl@0: sl@0: if(rqStatInit.Int() != KErrNone) sl@0: {//-- thread couldn't initialise sl@0: __PRINT1(_L("#=- CFatHelperThreadBase::DoLaunchThread() failure#2 res:%d"), nRes); sl@0: ForceStop(); sl@0: ASSERT(0); sl@0: return nRes; sl@0: } sl@0: sl@0: //-- Helper FAT thread is running now sl@0: return KErrNone; sl@0: } sl@0: sl@0: sl@0: //####################################################################################################################################### sl@0: //# CFat32ScanThread implementation sl@0: //####################################################################################################################################### sl@0: sl@0: sl@0: CFat32ScanThread::CFat32ScanThread(CAtaFatTable& aOwner) sl@0: :CFatHelperThreadBase(aOwner) sl@0: { sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Launches the FAT32_ScanThread scaner thread. sl@0: @return standard error code sl@0: */ sl@0: TInt CFat32ScanThread::Launch() sl@0: { sl@0: return DoLaunchThread(FAT32_ScanThread, this); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: FAT32_ScanThread preamble function. It gets called by the scan thread at the very beginning. sl@0: Does some initialisation work and its return code is signaled to the thread owner by RThread::Rendezvous(); sl@0: sl@0: @return Thread object initialisation code, KErrNone on success. sl@0: */ sl@0: TInt CFat32ScanThread::Thread_Preamble() sl@0: { sl@0: //__PRINT(_L("#=- CFat32ScanThread::Thread_Preamble()")); sl@0: sl@0: ipFatBitCache = iOwner.iCache->BitCacheInterface(); sl@0: iTimeStart.UniversalTime(); //-- take thread start time sl@0: sl@0: ASSERT(State() == CFatHelperThreadBase::ENotStarted); //-- see the thread launcher sl@0: sl@0: if(!iOwner.IsFat32()) sl@0: {//-- this stuff is supposed to work for FAT32 only sl@0: ASSERT(0); sl@0: return KErrArgument; sl@0: } sl@0: sl@0: return KErrNone; sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: /** sl@0: FAT32_ScanThread postamble function. It gets called by the scan thread just before its function exits. sl@0: Does some finalisation work and its return code is the thread completion code; sl@0: sl@0: @return Thread object finalisation code, KErrNone on success. sl@0: */ sl@0: TInt CFat32ScanThread::Thread_Postamble(TInt aResult) sl@0: { sl@0: //__PRINT(_L("#=- CFat32ScanThread::Thread_Postamble()")); sl@0: sl@0: #ifdef _DEBUG sl@0: //-- print out time taken the thread to finish sl@0: TName nameBuf; sl@0: iTimeEnd.UniversalTime(); //-- take end time sl@0: const TInt msScanTime = (TInt)( (iTimeEnd.MicroSecondsFrom(iTimeStart)).Int64() / K1mSec); sl@0: nameBuf.Copy(RThread().Name()); sl@0: nameBuf.Insert(0,_L("#=-<<<")); sl@0: nameBuf.AppendFormat(_L(" Thread Exit. id:%d, Code:%d, time:%d ms"), (TUint)RThread().Id(), aResult, msScanTime); sl@0: __PRINT(nameBuf); sl@0: #endif sl@0: sl@0: //-- tell FAT bit supercache (if we have it) that we have finished populating it, successfully or not sl@0: if(ipFatBitCache) sl@0: { sl@0: ipFatBitCache->FinishPopulating(aResult == KErrNone); sl@0: ipFatBitCache->Dump(); sl@0: } sl@0: sl@0: //-- close FAT chunk buffer sl@0: iFatChunkBuf.Close(); sl@0: sl@0: //-- set the host object state depending on the work results. sl@0: if(aResult == KErrNone) sl@0: SetState(CFatHelperThreadBase::EFinished_OK); sl@0: else sl@0: SetState(CFatHelperThreadBase::EFailed); sl@0: sl@0: sl@0: return aResult; sl@0: } sl@0: sl@0: //####################################################################################################################################### sl@0: //# CFat32FreeSpaceScanner implementation sl@0: //####################################################################################################################################### sl@0: sl@0: CFat32FreeSpaceScanner::CFat32FreeSpaceScanner(CAtaFatTable& aOwner) sl@0: :CFat32ScanThread(aOwner) sl@0: { sl@0: } sl@0: sl@0: /** sl@0: Factory method. sl@0: @param aOwner owning CAtaFatTable sl@0: @return pointer to the constructed instance of the class sl@0: */ sl@0: CFat32FreeSpaceScanner* CFat32FreeSpaceScanner::NewL(CAtaFatTable& aOwner) sl@0: { sl@0: CFat32FreeSpaceScanner* pThis = NULL; sl@0: pThis = new (ELeave) CFat32FreeSpaceScanner(aOwner); sl@0: sl@0: return pThis; sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: Waits until FAT32 free clusters scan thread allows other thread (caller) to write to the FAT entry "aFatIndex". sl@0: Thread scans FAT from the beginning to the end and just waits untill scanning passes the entry number "aFatIndex" sl@0: sl@0: @param aFatIndex index of the FAT entry we are going to write. sl@0: */ sl@0: void CFat32FreeSpaceScanner::RequestFatEntryWriteAccess(TUint32 aFatIndex) const sl@0: { sl@0: if(!ThreadWorking()) sl@0: return; sl@0: sl@0: ASSERT(iOwner.ClusterNumberValid(aFatIndex)); sl@0: sl@0: const TUint KWaitGranularity = 20*K1mSec; //-- wait granularity sl@0: sl@0: //-- wait until FAT[aFatIndex] is available to write sl@0: while(aFatIndex > ClustersScanned() && ThreadWorking()) sl@0: { sl@0: BoostPriority(ETrue); //-- Boost scan thread priority sl@0: User::After(KWaitGranularity); sl@0: } sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: sl@0: /** just an internal helper method. Stores the number of FAT entries already scanned by FAT free entries scan thread. */ sl@0: void CFat32FreeSpaceScanner::SetClustersScanned(TUint32 aClusters) sl@0: { sl@0: XAutoLock lock(iOwner.DriveInterface()); //-- enter critical section sl@0: iClustersScanned=aClusters; sl@0: } sl@0: sl@0: /** just an internal helper method. returns the number of FAT entries already scanned by FAT free entrie sscan thread. */ sl@0: TUint32 CFat32FreeSpaceScanner::ClustersScanned() const sl@0: { sl@0: XAutoLock lock(iOwner.DriveInterface()); //-- enter critical section sl@0: return iClustersScanned; sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: overriden FAT32_ScanThread preamble function. sl@0: See CFat32ScanThread::Thread_Preamble() sl@0: */ sl@0: TInt CFat32FreeSpaceScanner::Thread_Preamble() sl@0: { sl@0: __PRINT1(_L("#=- CFat32FreeSpaceScanner::Thread_Preamble(), FAT state:%d"), iOwner.State()); sl@0: sl@0: ASSERT(iOwner.State() == CAtaFatTable::EFreeClustersScan); sl@0: sl@0: //-- invoke generic preamble first sl@0: TInt nRes = CFat32ScanThread::Thread_Preamble(); sl@0: if(nRes != KErrNone) sl@0: return nRes; sl@0: sl@0: //-- do specific to this thread object initialisation work sl@0: sl@0: //-- rename the thread sl@0: TName nameBuf; sl@0: const CFatMountCB& fatMount = *(iOwner.OwnerMount()); sl@0: nameBuf.Format(_L("Fat32FreeSpaceScanner_drv_%d"), fatMount.DriveNumber()); sl@0: RThread::RenameMe(nameBuf); sl@0: sl@0: //-- allocate FAT chunk buffer; its size will depend on FAT table size. sl@0: const TUint32 fatSz = iOwner.MaxEntries() << KFat32EntrySzLog2; sl@0: sl@0: if(fatSz < KBigSzFat_Threshold) sl@0: {//-- create a small buffer sl@0: if(iFatChunkBuf.CreateMax(KFatChunkBufSize_Small) != KErrNone) sl@0: return KErrNoMemory; sl@0: } sl@0: else sl@0: {//-- try to create larger buffer sl@0: if(iFatChunkBuf.CreateMax(KFatChunkBufSize_Big) != KErrNone && iFatChunkBuf.CreateMax(KFatChunkBufSize_Small) != KErrNone) sl@0: return KErrNoMemory; sl@0: } sl@0: sl@0: sl@0: //-- setup FAT table's parameters sl@0: //-- No free clusters yet; be careful with SetFreeClusters(), free clusters count can be sl@0: //-- modified from other thread, e.g. from FreeClusterList. Use read-modify-write instead of assignment. sl@0: SetClustersScanned(0); sl@0: iOwner.SetFreeClusters(0); sl@0: sl@0: //-- calculate number of FAT entires need to be processed for CMountCB::SetDiskSpaceChange() call. sl@0: //-- if number of processed entries in FAT exceeds iEntriesNotifyThreshold, CMountCB::SetDiskSpaceChange() sl@0: //-- will be called and the iEntriesNotifyThreshold will be updated. sl@0: iNfyThresholdInc = (TUint32)KVolSpaceNotifyThreshold >> fatMount.ClusterSizeLog2(); sl@0: iEntriesNotifyThreshold = iNfyThresholdInc; sl@0: sl@0: //-- if there is an interface to the FAT bit supercache, tell it to start populating. sl@0: //-- We will be populating this cache while reading and parsing FAT32. sl@0: if(ipFatBitCache) sl@0: ipFatBitCache->StartPopulating(); sl@0: sl@0: sl@0: return KErrNone; sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: /** sl@0: overriden FAT32_ScanThread postamble function. sl@0: See CFat32ScanThread::Thread_Postamble() sl@0: */ sl@0: TInt CFat32FreeSpaceScanner::Thread_Postamble(TInt aResult) sl@0: { sl@0: __PRINT2(_L("#=- CFat32FreeSpaceScanner::Thread_Postamble(%d), FAT state:%d"), aResult, iOwner.State()); sl@0: __PRINT2(_L("#=- FAT_ScanThread: counted Free clusters:%d, 1st free:%d"), iOwner.NumberOfFreeClusters(), iOwner.FreeClusterHint()); sl@0: sl@0: ASSERT(iOwner.State() == CAtaFatTable::EFreeClustersScan); sl@0: sl@0: //-- there was an error somewhere within FAT32 scan thread sl@0: if(aResult != KErrNone) sl@0: { sl@0: //-- indicate that the FAT table initialisation failed sl@0: __PRINT(_L("#=- Asynch FAT table initialisation failed !")); sl@0: sl@0: iOwner.SetState(CAtaFatTable::EMountAborted); sl@0: sl@0: //-- fix up some FAT table parameters sl@0: if(iOwner.FreeClusterHint() < KFatFirstSearchCluster) sl@0: iOwner.SetFreeClusterHint(KFatFirstSearchCluster); sl@0: sl@0: } sl@0: sl@0: sl@0: //-- call generic postamble sl@0: TInt nRes = CFat32ScanThread::Thread_Postamble(aResult); sl@0: sl@0: if(nRes == KErrNone) sl@0: {//-- FAT table now fully initialised sl@0: ASSERT(aResult == KErrNone); sl@0: iOwner.SetState(CAtaFatTable::EMounted); sl@0: sl@0: //-- free space counting finished OK, call the notifier last time sl@0: CFatMountCB& fatMount = *(iOwner.OwnerMount()); sl@0: sl@0: iOwner.AcquireLock(); sl@0: const TInt64 currFreeSpace = ((TInt64)iOwner.FreeClusters()) << fatMount.ClusterSizeLog2(); sl@0: iOwner.ReleaseLock(); sl@0: sl@0: fatMount.SetDiskSpaceChange(currFreeSpace); sl@0: sl@0: sl@0: } sl@0: else if(aResult == KErrNone) sl@0: {//-- CFat32ScanThread::Thread_Postamble() signaled a fault sl@0: iOwner.SetState(CAtaFatTable::EMountAborted); sl@0: } sl@0: sl@0: return aResult; sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: /** sl@0: Process free FAT entries collected by the scan thread that parses chunk of FAT data. sl@0: This method gets called by the FAT scanning thread after a portion of FAT is read into the buffer and parsed sl@0: sl@0: @param aFreeEntriesInChunk number of free FAT entries counted in FAT chunk sl@0: @param aCurrFirstFreeEntry current number of the first free FAT entry found sl@0: @param aClustersScanned total number of FAT entries scanned by the thread sl@0: sl@0: @return standard error code, KErrNone on success sl@0: */ sl@0: TInt CFat32FreeSpaceScanner::Thread_ProcessCollectedFreeEntries(const CAtaFatTable::TFatScanParam& aFatScanParam) sl@0: { sl@0: ASSERT(State() == CFatHelperThreadBase::EWorking); sl@0: sl@0: CAtaFatTable& ataFatTable = iOwner; sl@0: sl@0: //------------------------------------------- sl@0: //-- publish values to the CAtaFatTable object sl@0: ataFatTable.AcquireLock(); sl@0: sl@0: //-- publish free cluster count, use read-modify-write here sl@0: //-- CFatTable::iFreeClusters can be already modified from other thread. sl@0: TUint32 currFreeClusters = ataFatTable.FreeClusters(); //-- simple non-thread safe method sl@0: sl@0: currFreeClusters += aFatScanParam.iCurrFreeEntries; sl@0: sl@0: ataFatTable.SetFreeClusters(currFreeClusters); sl@0: sl@0: //-- store total number of scanned clusters (not to be modified from other thread) sl@0: const TUint32 scannedEntries = aFatScanParam.iEntriesScanned; sl@0: SetClustersScanned(scannedEntries); sl@0: sl@0: sl@0: if(aFatScanParam.iFirstFree >= KFatFirstSearchCluster) sl@0: ataFatTable.SetFreeClusterHint(aFatScanParam.iFirstFree);//-- probably found next free cluster number sl@0: sl@0: ataFatTable.ReleaseLock(); sl@0: sl@0: //-- check if we need to call CMountCB::SetDiskSpaceChange() to notify it that the amount of processed FAT entries has reached the given threshold sl@0: if(scannedEntries >= iEntriesNotifyThreshold) sl@0: { sl@0: iEntriesNotifyThreshold += iNfyThresholdInc; sl@0: sl@0: CFatMountCB& fatMount = *(iOwner.OwnerMount()); sl@0: const TInt64 currFreeSpace = ((TInt64)currFreeClusters) << fatMount.ClusterSizeLog2(); sl@0: fatMount.SetDiskSpaceChange(currFreeSpace); sl@0: } sl@0: sl@0: sl@0: return KErrNone; sl@0: } sl@0: sl@0: //####################################################################################################################################### sl@0: //# CFat32BitCachePopulator implementation sl@0: //####################################################################################################################################### sl@0: CFat32BitCachePopulator::CFat32BitCachePopulator(CAtaFatTable& aOwner) sl@0: :CFat32ScanThread(aOwner) sl@0: { sl@0: } sl@0: sl@0: /** sl@0: Factory method. sl@0: @param aOwner owning CAtaFatTable sl@0: @return pointer to the constructed instance of the class sl@0: */ sl@0: CFat32BitCachePopulator* CFat32BitCachePopulator::NewL(CAtaFatTable& aOwner) sl@0: { sl@0: CFat32BitCachePopulator* pThis = NULL; sl@0: pThis = new (ELeave) CFat32BitCachePopulator(aOwner); sl@0: sl@0: return pThis; sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: The main FS thread tries to write the "aFatIndex" entry in FAT while this thread is running. sl@0: We can't do anything useful here, because FAT32 bit supercache doesn't work on FAT entry level and sl@0: deals with much less scale - FAT32 cache sector, which can consist from many FAT32 entries. sl@0: The conflict situation will be resolved in the CAtaFatTable::WriteL() sl@0: */ sl@0: void CFat32BitCachePopulator::RequestFatEntryWriteAccess(TUint32 /*aFatIndex*/) const sl@0: { sl@0: //-- do nothing here, do not block the caller sl@0: } sl@0: sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: /** sl@0: overriden FAT32_ScanThread preamble function. sl@0: See CFat32ScanThread::Thread_Preamble() sl@0: */ sl@0: TInt CFat32BitCachePopulator::Thread_Preamble() sl@0: { sl@0: __PRINT(_L("#=- CFat32BitCachePopulator::Thread_Preamble()")); sl@0: sl@0: //-- invoke generic preamble sl@0: TInt nRes = CFat32ScanThread::Thread_Preamble(); sl@0: if(nRes != KErrNone) sl@0: return nRes; sl@0: sl@0: //-- do specific to this thread object initialisation work sl@0: iTotalOccupiedFatEntries = 0; sl@0: sl@0: //-- rename the thread sl@0: TName nameBuf; sl@0: const CFatMountCB& fatMount = *(iOwner.OwnerMount()); sl@0: nameBuf.Format(_L("CFat32BitCachePopulator_drv_%d"), fatMount.DriveNumber()); sl@0: RThread::RenameMe(nameBuf); sl@0: sl@0: //-- allocate FAT chunk buffer sl@0: nRes = iFatChunkBuf.CreateMax(KFatChunkBufSize); sl@0: if(nRes != KErrNone) sl@0: return nRes; sl@0: sl@0: sl@0: if(!ipFatBitCache) sl@0: {//-- this is a bit cache populator and the bit cache object must have been constructed before setting up the populating thread. sl@0: ASSERT(0); sl@0: return KErrCorrupt; sl@0: } sl@0: sl@0: //-- Tell FAT bit supercache to start populating. We will be populating this cache while reading and parsing FAT32. sl@0: if(ipFatBitCache->StartPopulating()) sl@0: nRes = KErrNone; sl@0: else sl@0: nRes = KErrCorrupt; sl@0: sl@0: return nRes; sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: sl@0: /** sl@0: overriden FAT32_ScanThread postamble function. sl@0: See CFat32ScanThread::Thread_Postamble() sl@0: */ sl@0: TInt CFat32BitCachePopulator::Thread_Postamble(TInt aResult) sl@0: { sl@0: __PRINT1(_L("#=- CFat32BitCachePopulator::Thread_Postamble(%d)"), aResult); sl@0: sl@0: //-- nothing specific to do, just call generic method sl@0: return CFat32ScanThread::Thread_Postamble(aResult); sl@0: } sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: /** sl@0: This method gets called by the FAT scanning thread after a portion of FAT is read into the buffer and parsed sl@0: @return standard error code, KErrNone on success sl@0: */ sl@0: TInt CFat32BitCachePopulator::Thread_ProcessCollectedFreeEntries(const CAtaFatTable::TFatScanParam& aFatScanParam) sl@0: { sl@0: ASSERT(State() == CFatHelperThreadBase::EWorking); sl@0: sl@0: //-- check the bit cache state sl@0: if(ipFatBitCache->State() != CFatBitCache::EPopulating) sl@0: {//-- something wrong happened to the cache, e.g. someone forcedly invalidated it (probably from another thread) sl@0: return KErrAbort; sl@0: } sl@0: sl@0: sl@0: //-- if CFat32BitCachePopulator has already counted all _occupied_ FAT entries, there is no need to sl@0: //-- continue FAT reading; just mark the rest of the FAT bit supercache as containing free FAT entries and abort scanning sl@0: sl@0: CAtaFatTable& ataFatTable = iOwner; sl@0: sl@0: ataFatTable.AcquireLock(); sl@0: sl@0: //-- current amount of non-free entries in FAT, excluding FAT[0] & FAT[1] sl@0: const TUint32 KCurrNonFreeEntries = ataFatTable.MaxEntries() - ataFatTable.FreeClusters() - KFatFirstSearchCluster; sl@0: sl@0: iTotalOccupiedFatEntries += aFatScanParam.iCurrOccupiedEntries; sl@0: sl@0: //-- check if the thread needs to continue it work sl@0: const TBool KNoNeedToScanFurther = (iTotalOccupiedFatEntries >= KCurrNonFreeEntries); sl@0: sl@0: if(KNoNeedToScanFurther) sl@0: { sl@0: //-- tell FAT bit supercache to mark the range from currently scanned FAT entry to the end of the FAT as containing free entries. sl@0: __PRINT2(_L("#=- CFat32BitCachePopulator::Thread_ProcessCollectedFreeEntries() counted: %d/%d; aborting scan."), iTotalOccupiedFatEntries, KCurrNonFreeEntries); sl@0: sl@0: const TUint32 entryStart = aFatScanParam.iEntriesScanned; //-- first FAT entry in the range to be marked as 'free' sl@0: const TUint32 entryEnd = ataFatTable.MaxEntries()-1; //-- last FAT entry in the range to be marked as 'free', last FAT entry sl@0: sl@0: ipFatBitCache->MarkFatRange(entryStart, entryEnd, ETrue); sl@0: sl@0: //-- signal that the thread shall finish with normal (KErrNone) reason sl@0: //-- it will also normally finish FAT bit cache populating in postamble sl@0: AllowToLive(EFalse); sl@0: } sl@0: sl@0: ataFatTable.ReleaseLock(); sl@0: sl@0: sl@0: return KErrNone; sl@0: } sl@0: sl@0: sl@0: //####################################################################################################################################### sl@0: /** sl@0: FAT32 free entries scan thread function. Walks through FAT32 and counts free entries. sl@0: 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. sl@0: sl@0: @param apHostObject pointer to the host object of CFat32ScanThread base class. sl@0: */ sl@0: //####################################################################################################################################### sl@0: TInt FAT32_ScanThread(TAny* apHostObject) sl@0: { sl@0: TInt nRes; sl@0: sl@0: #ifdef _DEBUG sl@0: TName nameBuf; sl@0: nameBuf.Copy(RThread().Name()); sl@0: nameBuf.Insert(0,_L("#=->>>")); nameBuf.AppendFormat(_L(" Thread Enter (id:%d)"), (TUint)RThread().Id()); sl@0: __PRINT(nameBuf); sl@0: #endif sl@0: sl@0: ASSERT(apHostObject); sl@0: CFat32FreeSpaceScanner* pSelf = (CFat32FreeSpaceScanner*)apHostObject; sl@0: sl@0: CAtaFatTable& ataFatTable = pSelf->iOwner; sl@0: CFatMountCB& fatMount = *(ataFatTable.OwnerMount()); sl@0: sl@0: const TUint32 KFat32EntrySz = sizeof(TFat32Entry); sl@0: const TUint32 KFat1StartPos = fatMount.StartOfFatInBytes(); sl@0: const TUint32 KNumClusters = ataFatTable.MaxEntries(); //-- FAT[0] & FAT[1] are reserved and not counted by UsableClusters() sl@0: sl@0: //-- perform thread preamble work sl@0: nRes = pSelf->Thread_Preamble(); sl@0: sl@0: //-- signal the thread initialisation result sl@0: RThread::Rendezvous(nRes); sl@0: sl@0: sl@0: //-- Initialisation OK, do real job: FAT scanning sl@0: if(nRes == KErrNone) sl@0: { sl@0: pSelf->SetState(CFatHelperThreadBase::EWorking); sl@0: sl@0: TUint32 rem = KNumClusters * KFat32EntrySz; sl@0: TUint32 mediaPos = KFat1StartPos; sl@0: sl@0: CAtaFatTable::TFatScanParam fatScanParam; //-- FAT scanning parameters sl@0: sl@0: //============================================ sl@0: //=== FAT read and parse loop ================ sl@0: //-- in this loop we read portions of raw FAT32 data in a buffer, than parse this buffer sl@0: //-- in order to find out the number of free FAT entries there and other stuff sl@0: while(rem) sl@0: { sl@0: const TUint32 bytesToRead=Min(rem, (TUint32)pSelf->iFatChunkBuf.Size()); sl@0: TPtrC8 ptrData(pSelf->iFatChunkBuf.Ptr(), bytesToRead); sl@0: sl@0: //-- check for sudden media change sl@0: if(fatMount.Drive().IsChanged()) sl@0: { sl@0: __PRINT(_L("#=--- FAT32_ScanThread: Media change occured, aborting!")); sl@0: nRes = KErrAbort; sl@0: break; sl@0: } sl@0: sl@0: //------------------------------------------- sl@0: //-- read a portion of FAT into the buffer sl@0: ataFatTable.AcquireLock(); sl@0: sl@0: //-- check if the thread was requested to finish sl@0: if(!pSelf->AllowedToLive()) sl@0: { sl@0: ataFatTable.ReleaseLock(); sl@0: nRes = KErrAbort; sl@0: break; sl@0: } sl@0: sl@0: //-- actual read sl@0: //__PRINT3(_L("#=--- FAT32_ScanThread: read %d bytes pos:0x%x, boost:%d"), bytesToRead, mediaPos, pSelf->IsPriorityBoosted()); sl@0: sl@0: nRes = fatMount.LocalDrive()->Read(mediaPos, bytesToRead, pSelf->iFatChunkBuf); sl@0: sl@0: ataFatTable.ReleaseLock(); sl@0: sl@0: //------------------------------------------- sl@0: //-- analyse the read error code sl@0: if(nRes != KErrNone) sl@0: { sl@0: __PRINT1(_L("#=--- FAT32_ScanThread read error! res:%d"), nRes); sl@0: break; //-- abort scanning sl@0: } sl@0: sl@0: //------------------------------------------- sl@0: //-- parse FAT from the buffer sl@0: sl@0: //-- we need number of free and occupied entries in the _current_ FAT chunk being read and parsed sl@0: fatScanParam.iCurrFreeEntries = 0; sl@0: fatScanParam.iCurrOccupiedEntries = 0; sl@0: sl@0: ataFatTable.DoParseFatBuf(ptrData, fatScanParam); sl@0: sl@0: //--- process the the results of FAT buffer parsing sl@0: nRes = pSelf->Thread_ProcessCollectedFreeEntries(fatScanParam); sl@0: if(nRes != KErrNone || !pSelf->AllowedToLive()) sl@0: {//-- some types of worker threads may wish to finish normally but prematurely, by the result of Thread_ProcessCollectedFreeEntries() sl@0: break; //-- abort scanning sl@0: } sl@0: sl@0: sl@0: //-- allow this thread to be preempted by another one that wants to access the media driver. sl@0: //-- without this wait we will have priority inversion, because this (low priority) thread continiously reads data by big chunks sl@0: //-- and doesn't allow others to access the driver. sl@0: //-- On the other hand, if the thread's priority is boosted, there is no reason to be polite. sl@0: if(!pSelf->IsPriorityBoosted()) sl@0: User::After(K1mSec); //-- User::After() granularity can be much coarser than 1ms sl@0: sl@0: //------------------------------------------- sl@0: mediaPos += bytesToRead; sl@0: rem -= bytesToRead; sl@0: sl@0: }//while(rem) sl@0: sl@0: }//if(nRes == KErrNone) sl@0: sl@0: sl@0: //-- perform thread postamble work sl@0: nRes = pSelf->Thread_Postamble(nRes); sl@0: sl@0: return nRes; sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: