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\sfat\fat_table.cpp sl@0: // FAT12/16 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: //!! sl@0: //!! WARNING!! DO NOT edit this file !! '\sfat' component is obsolete and is not being used. '\sfat32'replaces it sl@0: //!! sl@0: //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! sl@0: //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! sl@0: sl@0: sl@0: #include "sl_std.h" sl@0: #include "sl_fatcache.h" sl@0: #include "fat_table.h" 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: 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 ) sl@0: {//-- CRamFatTable doesn't support FAT12; FAT16 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: 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()); 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 (iFreeClusters> CFatTable::AllocateClusterList() N:%d,NearestCL:%d"),aNumber,aNearestCluster); sl@0: __ASSERT_DEBUG(aNumber>0,Fault(EFatBadParameter)); sl@0: sl@0: if (iFreeClusters1) 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: 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: sl@0: if(iFreeClusters==0) 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: 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: //ASSERT(aClustersRequired >0 && aClustersRequired <= iOwner->UsableClusters()); 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) sl@0: { 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 or FAT16 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: sl@0: ASSERT(!iCache); sl@0: sl@0: //-- this is used for chaches granularity sanity check sl@0: const TUint32 KMaxGranularityLog2 = 18; //-- 256K is a maximal allowed granularity sl@0: const TUint32 KMinGranularityLog2 = KDefSectorSzLog2; //-- 512 bytes is a minimal allowed granularity sl@0: sl@0: switch(FatType()) sl@0: { sl@0: case EFat12: //-- create fixed FAT12 cache sl@0: iCache = CFat12Cache::NewL(iOwner, fatSize); sl@0: break; sl@0: sl@0: case EFat16: //-- create fixed FAT16 cache 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: 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: /** 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: //-- 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: Clear any cached data 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: 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: } 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: 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: 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: __PRINT1(_L("CAtaFatTable::InitializeL() drv:%d"), iOwner->DriveNumber()); sl@0: CFatTable::InitializeL(); sl@0: sl@0: //-- create the FAT cache. sl@0: ASSERT(!iCache); sl@0: CreateCacheL(); sl@0: } sl@0: sl@0: sl@0: //----------------------------------------------------------------------------- sl@0: /** sl@0: Remount the FAT table. This method call means that the media parameters wasn't changed, sl@0: otherwise CFatMountCB::DoReMountL() would reject it. sl@0: Just do some re-initialisation work. sl@0: */ sl@0: void CAtaFatTable::ReMountL() sl@0: { sl@0: __PRINT1(_L("CAtaFatTable::ReMountL() drv:%d"), iOwner->DriveNumber()); sl@0: sl@0: if(iCache) sl@0: { sl@0: iCache->Invalidate(); sl@0: } sl@0: else sl@0: { sl@0: //-- this situation can happen when someone called CAtaFatTable::Dismount() that deletes the cache object sl@0: //-- and then ReMount happens. We need to re-initialise this object. sl@0: InitializeL(); sl@0: } sl@0: } 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); //-- for some silly reason some callers pass 0 here and expect it to leave 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: const TUint32 KFat16EntryMask = 0x0FFFF; 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 > KFat16EntryMask)) sl@0: { sl@0: ASSERT(0); sl@0: User::Leave(KErrCorrupt); sl@0: } sl@0: iCache->WriteEntryL(aFatIndex, aValue); sl@0: } sl@0: sl@0: sl@0: /** sl@0: Get the next cluster in the chain from the FAT sl@0: sl@0: @param aCluster number to read, contains next cluster upon return sl@0: @leave sl@0: @return False if end of cluster chain sl@0: */ sl@0: TBool CFatTable::GetNextClusterL(TInt& aCluster) const sl@0: { sl@0: __PRINT1(_L("CAtaFatTable::GetNextClusterL(%d)"), aCluster); sl@0: sl@0: const TInt nextCluster = ReadL(aCluster); sl@0: TBool ret = EFalse; sl@0: sl@0: switch(FatType()) sl@0: { sl@0: case EFat12: sl@0: ret=!IsEof12Bit(nextCluster); sl@0: break; sl@0: sl@0: case EFat16: sl@0: ret=!IsEof16Bit(nextCluster); sl@0: break; sl@0: sl@0: default: sl@0: ASSERT(0); sl@0: return EFalse;//-- get rid of warning sl@0: }; sl@0: sl@0: if (ret) sl@0: { sl@0: aCluster=nextCluster; sl@0: } sl@0: sl@0: return ret; sl@0: sl@0: } sl@0: sl@0: /** sl@0: Write EOF to aFatIndex sl@0: @param aFatIndex index in FAT (cluster number) to be written sl@0: */ sl@0: void CFatTable::WriteFatEntryEofL(TUint32 aFatIndex) sl@0: { sl@0: __PRINT1(_L("CAtaFatTable::WriteFatEntryEofL(%d)"), aFatIndex); sl@0: sl@0: //-- use EOF_16Bit (0x0ffff) for all types of FAT, FAT cache will mask it appropriately sl@0: WriteL(aFatIndex, EOF_16Bit); sl@0: } sl@0: sl@0: sl@0: sl@0: /** sl@0: Mark cluster number aFatIndex in FAT as bad sl@0: @param aFatIndex index in FAT (cluster number) to be written sl@0: */ sl@0: void CFatTable::MarkAsBadClusterL(TUint32 aFatIndex) sl@0: { sl@0: __PRINT1(_L("CAtaFatTable::MarkAsBadClusterL(%d)"),aFatIndex); sl@0: sl@0: //-- use KBad_16Bit (0x0fff7) for all types of FAT, FAT cache will mask it appropriately sl@0: WriteL(aFatIndex, KBad_16Bit); sl@0: sl@0: FlushL(); sl@0: } sl@0: sl@0: sl@0: /** sl@0: Return the location of a Cluster in the data section of the media sl@0: sl@0: @param aCluster to find location of sl@0: @return Byte offset of the cluster data sl@0: */ sl@0: TInt64 CAtaFatTable::DataPositionInBytes(TUint32 aCluster) const sl@0: { sl@0: __ASSERT_DEBUG(ClusterNumberValid(aCluster), Fault(EFatTable_InvalidIndex)); sl@0: sl@0: const TInt clusterBasePosition=iOwner->ClusterBasePosition(); sl@0: return(((TInt64(aCluster)-KFatFirstSearchCluster) << iOwner->ClusterSizeLog2()) + clusterBasePosition); sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: