--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/userlibandfileserver/fileserver/sfat32/sl_fatcache32.cpp Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,1227 @@
+// Copyright (c) 1996-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of the License "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+// f32\sfat32\sl_fatcache32.cpp
+//
+//
+
+#include "sl_std.h"
+#include "sl_fatcache32.h"
+
+/**
+ @file
+ Various FAT32 caches implementation
+*/
+
+
+
+//#################################################################################################################################
+//# CFat32LruCache implementation
+//#################################################################################################################################
+
+//-----------------------------------------------------------------------------
+CFat32LruCache::CFat32LruCache()
+ :CFatPagedCacheBase(), iPageList(_FOFF(CFat32LruCachePage, iLink))
+ {
+ }
+
+//-----------------------------------------------------------------------------
+/**
+ FAT32 LRU cache factory function.
+ @param aOwner pointer to the owning FAT mount
+ @param aMaxMemSize maximal size of the memory the cache can use, bytes
+ @param aRdGranularityLog2 Log2(read granularity)
+ @param aWrGranularityLog2 Log2(write granularity)
+
+ @return pointer to the constructed object.
+*/
+CFat32LruCache* CFat32LruCache::NewL(CFatMountCB* aOwner, TUint32 aMaxMemSize, TUint32 aRdGranularityLog2, TUint32 aWrGranularityLog2)
+ {
+ __PRINT(_L("#-CFat32LruCache::NewL()"));
+ CFat32LruCache* pSelf = NULL;
+ pSelf = new (ELeave) CFat32LruCache;
+
+ CleanupStack::PushL(pSelf);
+ pSelf->InitialiseL(aOwner, aMaxMemSize, aRdGranularityLog2, aWrGranularityLog2);
+ CleanupStack::Pop();
+
+ return pSelf;
+ }
+
+//-----------------------------------------------------------------------------
+/**
+ @return pointer to the CFatBitCache interface.
+*/
+CFatBitCache* CFat32LruCache::BitCacheInterface()
+ {
+ return iBitCache;
+ }
+
+//-----------------------------------------------------------------------------
+
+/**
+ FAT32 LRU cache initialisation.
+
+ @param aOwner pointer to the owning FAT mount
+ @param aMaxMemSize maximal size of the memory the cache can use, bytes
+ @param aRdGranularityLog2 Log2(read granularity)
+ @param aWrGranularityLog2 Log2(write granularity)
+
+ @return pointer to the constructed object.
+*/
+void CFat32LruCache::InitialiseL(CFatMountCB* aOwner, TUint32 aMaxMemSize, TUint32 aRdGranularityLog2, TUint32 aWrGranularityLog2)
+ {
+ const TUint32 KReadGranularity = Pow2(aRdGranularityLog2);
+ const TUint32 KWriteGranularity = Pow2(aWrGranularityLog2);
+
+ __PRINT3(_L("#-CFat32LruCache::InitialiseL MaxMem:%u, RdGr:%d, WrGr:%d"),aMaxMemSize, KReadGranularity, KWriteGranularity);
+ (void)KReadGranularity;
+ (void)KWriteGranularity;
+
+
+ const TBool bParamsValid = (aRdGranularityLog2 >= aWrGranularityLog2) && (aWrGranularityLog2 >= KDefSectorSzLog2) && (aMaxMemSize > KReadGranularity);
+ __ASSERT_ALWAYS(bParamsValid, Fault(EFatCache_BadGranularity));
+
+ CFatPagedCacheBase::InitialiseL(aOwner);
+
+ ASSERT(FatType() == EFat32);
+
+ //-- according to the FAT32 specs, FAT32 min size is 65526 entries or 262104 bytes.
+ //-- It's possible to incorrectly format a small volume to FAT32, it shall be accessible read-only.
+ if(aMaxMemSize > FatSize())
+ {//-- strange situation, memory allocated for LRU cache is enough to cache whole FAT32
+ __PRINT(_L("#-CFat32LruCache::InitialiseL warning: LRU cache becomes fixed! (too much memory allowed)"));
+ aMaxMemSize = FatSize();
+ }
+
+ //-- LRU cache page size is (2^aRdGranularityLog2) bytes and consists of 2^(aRdGranularityLog2-aWrGranularity) sectors.
+ iPageSizeLog2 = aRdGranularityLog2;
+ iSectorSizeLog2 = aWrGranularityLog2; //-- Log2(number of sectors in cache page)
+
+ iMaxPages = aMaxMemSize / PageSize(); //-- maximal number of cache pages we can allocate
+ iNumPagesAllocated = 0;
+
+ __ASSERT_ALWAYS((iMaxPages > 1 && SectorsInPage() < KMaxSectorsInPage), Fault(EFatCache_BadGranularity));
+
+ //-- obtain maximal number of entries in the table
+ if(aOwner->UsableClusters() < 1)
+ {
+ ASSERT(0);
+ User::Leave(KErrCorrupt);
+ }
+
+ iMaxFatEntries = aOwner->UsableClusters()+KFatFirstSearchCluster; //-- FAT[0] & FAT[1] are not in use
+
+ //-- create FAT bit supercache if it is enabled in config
+ ASSERT(!iBitCache);
+ if(aOwner->FatConfig().FAT32_UseBitSupercache())
+ {
+ iBitCache = CFatBitCache::New(*this);
+ }
+ else
+ {
+ __PRINT(_L("#++ !! Fat Bit Supercache is disabled in config !!"));
+ }
+
+ }
+
+//-----------------------------------------------------------------------------
+/**
+ Close the cache and deallocate its memory.
+ @param aDiscardDirtyData if ETrue, will ignore dirty data. If EFalse, will panic on atempt to close dirty cache.
+*/
+void CFat32LruCache::Close(TBool aDiscardDirtyData)
+ {
+ __PRINT1(_L("#-CFat32LruCache::Close(%d)"), aDiscardDirtyData);
+
+ //-- delete FAT bit supercache if present
+ delete iBitCache;
+ iBitCache=NULL;
+
+
+ //-- delete existing cache pages
+ TPageIterator itr(iPageList);
+
+ for(;;)
+ {
+ CFat32LruCachePage* pPage = itr++;
+ if(!pPage)
+ break;
+
+ pPage->iLink.Deque(); //-- remove page from the list
+
+ if(pPage->IsDirty())
+ {//-- trying to destroy the cache that has dirty pages
+ __PRINT1(_L("#-CFat32LruCache::Close() The page is dirty! Start idx:%d"), pPage->StartFatIndex());
+ if(!aDiscardDirtyData)
+ {
+ Fault(EFatCache_DiscardingDirtyData);
+ }
+ //-- ignore this fact if requested.
+ }
+
+ delete pPage;
+ --iNumPagesAllocated;
+ }
+
+ SetDirty(EFalse);
+ ASSERT(!iNumPagesAllocated);
+ }
+
+
+//-----------------------------------------------------------------------------
+/**
+ Tries to read FAT entry from the cache. If the entry at aFatIndex is not cached, does nothing and returns EFalse.
+ If finds the cache page that contains entry at index "aFatIndex", reads it and returns ETrue.
+
+ @param aFatIndex FAT entry index within FAT table
+ @param aFatEntry on success it will contain FAT entry value
+ @return ETrue if the entry has been read
+ EFalse if index aFatIndex isn't cached
+*/
+TBool CFat32LruCache::ReadCachedEntryL(TUint32 aFatIndex, TFat32Entry& aResult)
+ {
+ //-- iterate through LRU list looking if the entry is cached.
+ TPageIterator itr(iPageList);
+
+ for(;;)
+ {
+ CFat32LruCachePage* pPage = itr++;
+ if(!pPage)
+ break;
+
+ if(pPage->ReadCachedEntryL(aFatIndex, aResult))
+ {//-- found entry in some cache page. Make this page LRU
+ if(!iPageList.IsFirst(pPage))
+ {
+ pPage->iLink.Deque();
+ iPageList.AddFirst(*pPage);
+ }
+ return ETrue;
+ }
+ }
+
+ return EFalse; //-- the entry is not cached
+ }
+
+//-----------------------------------------------------------------------------
+/**
+ Tries to write FAT entry to the cache. If the entry at aFatIndex is not cached, does nothing and returns EFalse.
+ If finds the cache page that contains entry at index "aFatIndex", overwrites it and returns ETrue
+
+ @param aFatIndex FAT entry index within FAT table
+ @param aFatEntry new FAT entry value
+ @return ETrue if the entry has been overwritten
+ EFalse if index aFatIndex isn't cached
+*/
+TBool CFat32LruCache::WriteCachedEntryL(TUint32 aFatIndex, TFat32Entry aFatEntry)
+ {
+ //-- iterate through LRU list looking if the entry is cached.
+ TPageIterator itr(iPageList);
+
+ for(;;)
+ {
+ CFat32LruCachePage* pPage = itr++;
+ if(!pPage)
+ break;
+
+ if(pPage->WriteCachedEntryL(aFatIndex, aFatEntry))
+ {//-- the entry was cached and modified now. Make this page LRU
+ if(!iPageList.IsFirst(pPage))
+ {
+ pPage->iLink.Deque();
+ iPageList.AddFirst(*pPage);
+ }
+ return ETrue;
+ }
+ }
+
+ return EFalse; //-- the entry is not cached
+ }
+
+//-----------------------------------------------------------------------------
+/**
+ Get a spare page. This function can either allocate a page if memory limit isn't reached yet
+ or find the least recently used (in the end of the LRU list) and evict it.
+
+ @return pointer to the cache page to use, it will be insertet to the beginning of the LRU list
+*/
+CFat32LruCachePage* CFat32LruCache::DoGetSpareCachePageL()
+ {
+ CFat32LruCachePage* pPage=NULL;
+
+ if(iNumPagesAllocated < iMaxPages)
+ {//-- we still can allocate a page
+
+ pPage = CFat32LruCachePage::NewL(*this);
+ ++iNumPagesAllocated;
+ iPageList.AddFirst(*pPage); //-- insert the page into the beginning of LRU list
+ return pPage;
+ }
+
+ //-- all pages are already allocated, evict the last recently used and remove it from the list
+ pPage = iPageList.Last(); //-- least recently used page, last in the list
+ pPage->iLink.Deque(); //-- remove it from the LRU list
+ iPageList.AddFirst(*pPage); //-- insert the page into the beginning of LRU list
+
+ //__PRINT1(_L("#-CFat32LruCache::DoGetSpareCachePageL() page @FAT idx:%d evicted"), pPage->StartFatIndex());
+
+ //-- flush the page, writing its data to all copies of FAT, to FAT1, then to FAT2 etc.
+ ASSERT(NumFATs() >0);
+ if(pPage->IsDirty())
+ {
+ //-- write page data to all copies of FAT
+ for(iCurrentFatNo=0; iCurrentFatNo < NumFATs(); ++iCurrentFatNo)
+ {
+ const TBool keepDirty = iCurrentFatNo < (NumFATs()-1);
+ pPage->FlushL(keepDirty);
+ }
+
+ iCurrentFatNo = KInvalidFatNo;
+ }
+
+
+ return pPage;
+ }
+
+
+
+//-----------------------------------------------------------------------------
+/**
+ Read FAT entry from the cache.
+
+ @param aIndex FAT entry index to read
+ @return FAT entry value at the index "aIndex"
+*/
+TUint32 CFat32LruCache::ReadEntryL(TUint32 aIndex)
+ {
+// __PRINT1(_L("#-CFat32LruCache::ReadEntryL() FAT idx:%d"), aIndex);
+
+ ASSERT(aIndex >= KFatFirstSearchCluster && aIndex < (FatSize() >> KFat32EntrySzLog2));
+
+ //-- firstly try to locate required entry in cache
+ TFat32Entry entry;
+ if(ReadCachedEntryL(aIndex, entry))
+ return entry; //-- the requested entry found in cache
+
+ //-- No luck, get a spare cache page (it will be inserted to the head of the LRU list)
+ CFat32LruCachePage* pPage = DoGetSpareCachePageL();
+ ASSERT(pPage);
+
+ entry = pPage->ReadFromMediaL(aIndex); //-- read whole FAT page from the media
+
+ return entry;
+ }
+
+//-----------------------------------------------------------------------------
+/**
+ Write FAT entry to the cache.
+ Appropriate FAT cache sector will be marked as "dirty" and will be eventually flushed to the media.
+
+ @param aIndex FAT entry index
+ @param aEntry FAT entry value
+*/
+void CFat32LruCache::WriteEntryL(TUint32 aIndex, TUint32 aEntry)
+ {
+ //__PRINT2(_L("#-CFat32LruCache::WriteEntryL() FAT idx:%d, val:%d"), aIndex, aEntry);
+
+ ASSERT(aIndex >= KFatFirstSearchCluster && aIndex < (FatSize() >> KFat32EntrySzLog2));
+
+ SetDirty(ETrue);
+
+ //-- 1. try to locate entry in the cache and overwrite it there if it is cached
+ if(WriteCachedEntryL(aIndex, aEntry))
+ return; //-- the entry in cache altered
+
+ //-- 2. the entry isn't cached; find a spare cache page (it will be inserted to the head of the LRU list)
+ CFat32LruCachePage* pPage = DoGetSpareCachePageL();
+ ASSERT(pPage);
+
+ pPage->ReadFromMediaL(aIndex); //-- read whole FAT page from the media
+
+
+ //-- 3. overwrite entry in cache
+ TBool bRes = pPage->WriteCachedEntryL(aIndex, aEntry);
+ ASSERT(bRes);
+ (void)bRes;
+ }
+
+//-----------------------------------------------------------------------------
+/**
+ A debug method that asserts that the cache is really clean
+*/
+void CFat32LruCache::AssertCacheReallyClean()
+ {
+#ifdef _DEBUG
+
+ TPageIterator itr(iPageList);
+ for(;;)
+ {//-- iterate through LRU list flushing pages into the current copy of FAT
+ CFat32LruCachePage* pPage = itr++;
+
+ if(!pPage)
+ break;
+
+ if(pPage->IsDirty())
+ {
+ __PRINT(_L("#-CFat32LruCache::AssertCacheReallyClean()"));
+ ASSERT(0);
+ }
+ }
+
+#endif
+ }
+
+//-----------------------------------------------------------------------------
+/**
+ Flushes all dirty data to the media.
+*/
+void CFat32LruCache::FlushL()
+ {
+ if(!IsDirty())
+ {
+ AssertCacheReallyClean();
+ return;
+ }
+
+ //-- flush dirty data to all copies of FAT
+ //-- all dirty pages will be written firstly to FAT1, then all of them will be written to FAT2 etc.
+ for(iCurrentFatNo=0; iCurrentFatNo < NumFATs(); ++iCurrentFatNo)
+ {
+ TPageIterator itr(iPageList);
+ for(;;)
+ {//-- iterate through LRU list flushing pages into the current copy of FAT
+ CFat32LruCachePage* pPage = itr++;
+ if(!pPage)
+ break;
+
+ //-- we need to keep page dirty until it is flushed to the last copy of FAT table
+ const TBool keepDirty = iCurrentFatNo < (NumFATs() - 1);
+ pPage->FlushL(keepDirty);
+ }
+ }
+
+ iCurrentFatNo = KInvalidFatNo;
+
+ SetDirty(EFalse);
+ }
+
+//-----------------------------------------------------------------------------
+
+/**
+ Invalidate whole cache. All pages will be marked as invalid and will be re-read from the media on first access to them.
+ @return always KErrNone
+*/
+TInt CFat32LruCache::Invalidate()
+ {
+ __PRINT(_L("#-CFat32LruCache::Invalidate()"));
+ const TBool bIgnoreDirtyData = CheckInvalidatingDirtyCache();
+
+ //-- iterate through LRU list marking every page as invalid
+ TPageIterator itr(iPageList);
+ for(;;)
+ {
+ CFat32LruCachePage* pPage = itr++;
+ if(!pPage)
+ break;
+
+ pPage->Invalidate(bIgnoreDirtyData);
+ }
+
+ SetDirty(EFalse);
+
+ return KErrNone;
+ }
+
+
+//-----------------------------------------------------------------------------
+
+/**
+ Invalidate FAT cache pages that contain FAT32 entries from aStartIndex to (aStartIndex+aNumEntries)
+ These pages will be marked as invalid and will be re-read from the media on first access to them.
+
+ @param aStartIndex FAT start index of the region being invalidated
+ @param aNumEntries number of entries to invalidate
+ @return always KErrNone
+*/
+TInt CFat32LruCache::InvalidateRegion(TUint32 aStartIndex, TUint32 aNumEntries)
+ {
+ __PRINT2(_L("#-CFat32LruCache::InvalidateRegion() startIndex:%d, entries:%d"),aStartIndex, aNumEntries);
+ ASSERT(aStartIndex >= KFatFirstSearchCluster && aStartIndex < (FatSize() >> KFat32EntrySzLog2));
+
+ if(!aNumEntries)
+ {
+ ASSERT(0);
+ return KErrNone;
+ }
+
+ const TBool bIgnoreDirtyData = CheckInvalidatingDirtyCache();
+ const TUint KEntriesInPage = Pow2(PageSizeLog2() - KFat32EntrySzLog2);
+ const TUint KLastIndex = aStartIndex+aNumEntries;
+
+ TBool bCacheIsStillDirty = EFalse; //-- ETrue if the cache is still dirty after invalidating its region
+
+ for(TUint currIndex = aStartIndex; currIndex < KLastIndex; currIndex+=KEntriesInPage)
+ {
+ TPageIterator itr(iPageList);
+ for(;;)
+ {//-- iterate through all pages, invalidating required
+ CFat32LruCachePage* pPage = itr++;
+ if(!pPage)
+ break;
+
+ if(pPage->IsEntryCached(currIndex))
+ {
+ pPage->Invalidate(bIgnoreDirtyData);
+ }
+ else if(pPage->IsDirty()) //-- invalid page can't be ditry.
+ {
+ bCacheIsStillDirty = ETrue; //-- we have at least 1 dirty page
+ }
+ }
+ }
+
+ SetDirty(bCacheIsStillDirty);
+
+ return KErrNone;
+}
+
+//-----------------------------------------------------------------------------
+
+
+
+/**
+ Look for free FAT entry in the FAT sector that corresponds to the aFatEntryIndex.
+ Search is performed in both directions, the right one has more priority (FAT cluster chain needs to grow right).
+ See FindFreeEntryInCacheSector()
+*/
+TBool CFat32LruCache::FindFreeEntryInCacheSectorL(TUint32& aFatEntryIndex)
+ {
+ if(ReadEntryL(aFatEntryIndex) == KSpareCluster)
+ return ETrue;
+
+ //-- look for free FAT entries in the FAT cache sector corresponting to the aStartIndex.
+ //-- use the same approach as in CFatTable::FindClosestFreeClusterL()
+ const TUint32 coeff = SectorSizeLog2()-KFat32EntrySzLog2;
+ const TUint32 numEntriesInSector = Pow2(coeff); //-- number of FAT32 entries in FAT cache sector
+
+ TUint32 MinIdx = (aFatEntryIndex >> coeff) << coeff;
+ TUint32 MaxIdx = MinIdx+numEntriesInSector-1;
+
+ if(MinIdx == 0)
+ {//-- correct values if this is the first FAT sector; FAT[0] & FAT[1] are reserved
+ MinIdx += KFatFirstSearchCluster;
+ }
+
+ //-- actual number of usable FAT entries can be less than deducted from number of FAT sectors.
+ MaxIdx = Min(MaxIdx, iMaxFatEntries-1);
+
+ //-- look in both directions starting from the aFatEntryIndex
+ //-- but in one FAT cache page sector only
+ TBool canGoRight = ETrue;
+ TBool canGoLeft = ETrue;
+
+ TUint32 rightIdx=aFatEntryIndex;
+ TUint32 leftIdx=aFatEntryIndex;
+
+ for(TUint i=0; i<numEntriesInSector; ++i)
+ {
+ if(canGoRight)
+ {
+ if(rightIdx < MaxIdx)
+ ++rightIdx;
+ else
+ canGoRight = EFalse;
+ }
+
+ if(canGoLeft)
+ {
+ if(leftIdx > MinIdx)
+ --leftIdx;
+ else
+ canGoLeft = EFalse;
+ }
+
+ if(!canGoRight && !canGoLeft)
+ return EFalse; //-- no free entries in this sector
+
+ if(canGoRight && ReadEntryL(rightIdx) == KSpareCluster)
+ {
+ aFatEntryIndex = rightIdx;
+ return ETrue;
+ }
+
+ if (canGoLeft && ReadEntryL(leftIdx) == KSpareCluster)
+ {
+ aFatEntryIndex = leftIdx;
+ return ETrue;
+ }
+ }//for(TUint i=0; i<numEntriesInSector; ++i)
+
+ return EFalse;
+ }
+
+
+
+//#################################################################################################################################
+// CFat32LruCachePage implementation
+//#################################################################################################################################
+
+
+CFat32LruCachePage::CFat32LruCachePage(CFatPagedCacheBase& aCache)
+ :CFatCachePageBase(aCache)
+ {
+
+ ASSERT(IsPowerOf2(EntriesInPage()));
+ }
+
+
+/**
+ Factory function.
+ @param aCache reference to the owning cache.
+ @return pointer to the constructed object or NULL on error
+*/
+CFat32LruCachePage* CFat32LruCachePage::NewL(CFatPagedCacheBase& aCache)
+ {
+
+ CFat32LruCachePage* pSelf = NULL;
+ pSelf = new (ELeave) CFat32LruCachePage(aCache);
+
+ CleanupStack::PushL(pSelf);
+
+ pSelf->iData.CreateMaxL(pSelf->PageSize()); //-- allocate memory for the page
+
+ CleanupStack::Pop();
+
+ return pSelf;
+ }
+
+
+//-----------------------------------------------------------------------------
+
+/**
+ Get a pointer to the FAT32 entry in the page buffer.
+ The page 's data shall be valid and the entry shall belong to this page.
+
+ @param aFatIndex absolute FAT index (from the FAT start) of the entry
+ @return pointer to the FAT32 entry in the page buffer.
+*/
+TFat32Entry* CFat32LruCachePage::GetEntryPtr(TUint32 aFatIndex) const
+ {
+
+ ASSERT(IsValid() && IsEntryCached(aFatIndex));
+
+ const TUint KEntryIndexInPage = aFatIndex & (EntriesInPage()-1); //-- number of entries in page is always a power of 2
+
+ TFat32Entry* pEntry = ((TFat32Entry*)iData.Ptr()) + KEntryIndexInPage;
+ return pEntry;
+ }
+
+//-----------------------------------------------------------------------------
+
+/**
+ Read FAT32 entry from the cache.
+
+ 1. If the entry at aFatIndex doesn't belong to this page, returns EFalse
+ 2. If page's data are valid and the entry is cached just extracts data from the page buffer.
+ 3. If page's data are invalid but the entry's index belongs to this page, firstly reads data from the media and goto 2
+
+ @param aFatIndex entry's absolute FAT index (from the FAT start)
+ @param aResult on sucess there will be FAT32 entry value
+ @return ETrue if the entry at aFatIndex belongs to this page (cached) and in this case aResult will contain this entry.
+ EFalse if the entry isn't cached.
+
+*/
+TBool CFat32LruCachePage::ReadCachedEntryL(TUint32 aFatIndex, TUint32& aResult)
+ {
+ if(!IsEntryCached(aFatIndex))
+ return EFalse; //-- the page doesn't contain required index
+
+ if(IsValid())
+ {//-- read entry directly from page buffer, the cached data are valid
+ aResult = (*GetEntryPtr(aFatIndex)) & KFat32EntryMask;
+ }
+ else
+ {//-- aFatIndex belongs to this page, but the page is invalid and needs to be read from the media
+ __PRINT1(_L("#-CFat32LruCachePage::ReadCachedEntry(%d) The page is invalid, reading from the media"), aFatIndex);
+ aResult = ReadFromMediaL(aFatIndex);
+ }
+
+ return ETrue;
+ }
+
+//-----------------------------------------------------------------------------
+
+/**
+ Read the FAT32 cache page from the media and return required FAT32 entry.
+
+ @param aFatIndex entry's absolute FAT index (from the FAT start)
+ @return entry value at aFatIndex.
+*/
+TUint32 CFat32LruCachePage::ReadFromMediaL(TUint32 aFatIndex)
+ {
+ //__PRINT1(_L("#-CFat32LruCachePage::ReadFromMediaL() FAT idx:%d"), aFatIndex);
+
+ const TUint KFat32EntriesInPageLog2 = iCache.PageSizeLog2()-KFat32EntrySzLog2; //-- number of FAT32 entries in page is always a power of 2
+
+ //-- find out index in FAT this page starts from
+ iStartIndexInFAT = (aFatIndex >> KFat32EntriesInPageLog2) << KFat32EntriesInPageLog2;
+
+ SetState(EInvalid); //-- mark the page as invalid just in case if the read fails.
+
+ //-- read page from the media
+ const TUint32 pageStartPos = iCache.FatStartPos() + (iStartIndexInFAT << KFat32EntrySzLog2);
+ TInt nRes = iCache.ReadFatData(pageStartPos, iCache.PageSize(), iData);
+ if(nRes != KErrNone)
+ {
+ __PRINT1(_L("#-CFat32LruCachePage::ReadFromMediaL() failed! code:%d"), nRes);
+ User::Leave(nRes);
+ }
+
+ SetClean(); //-- mark this page as clean
+
+ const TFat32Entry entry = (*GetEntryPtr(aFatIndex)) & KFat32EntryMask;
+
+ return entry;
+ }
+
+//-----------------------------------------------------------------------------
+
+/**
+ Writes FAT cache page sector to the media (to all copies of the FAT)
+ @param aSector page sector number
+*/
+void CFat32LruCachePage::DoWriteSectorL(TUint32 aSector)
+ {
+ //__PRINT1(_L("#-CFat32LruCachePage::DoWriteContiguousSectorsL() startSec:%d"),aSector);
+
+ ASSERT(aSector < iCache.SectorsInPage());
+
+ const TUint CacheSecSzLog2=iCache.SectorSizeLog2();
+
+ TInt offset = 0;
+ if(iStartIndexInFAT == 0 && aSector == 0)
+ {//-- this is the very beginning of FAT32. We must skip FAT[0] & FAT[1] entries and do not write them to media.
+ offset = KFatFirstSearchCluster << KFat32EntrySzLog2;
+ }
+
+ const TUint8* pData = iData.Ptr()+offset+(aSector << CacheSecSzLog2);
+
+ TUint32 dataLen = (1 << CacheSecSzLog2) - offset;
+
+ const TUint32 mediaPosStart = iCache.FatStartPos() + (iStartIndexInFAT << KFat32EntrySzLog2) + (aSector << CacheSecSzLog2) + offset;
+ const TUint32 mediaPosEnd = mediaPosStart + dataLen;
+
+ //-- check if we are going to write beyond FAT. It can happen if the write granularity is bigger that the sector size.
+ const TUint32 posFatEnd = iCache.FatStartPos() + iCache.FatSize();
+ if(mediaPosEnd > posFatEnd)
+ {//-- correct the leength of the data to write.
+ dataLen -= (mediaPosEnd-posFatEnd);
+ }
+
+ TPtrC8 ptrData(pData, dataLen); //-- source data descriptor
+
+ TInt nRes = iCache.WriteFatData(mediaPosStart, ptrData);
+
+ if(nRes != KErrNone)
+ {
+ __PRINT1(_L("#-CFat32LruCachePage::DoWriteSectorsL() failed! code:%d"), nRes);
+ User::Leave(nRes);
+ }
+
+
+ //-- if we have FAT bit supercache and it is in consistent state, check if the entry in this cache differs from the data in dirty FAT cache sector.
+ CFatBitCache *pFatBitCache = iCache.BitCacheInterface();
+ if(pFatBitCache && pFatBitCache->UsableState())
+ {
+ //-- absolute FAT cache sector number corresponding aSector number in _this_ cache page
+ const TUint32 absSectorNum = (iStartIndexInFAT >> (CacheSecSzLog2-KFat32EntrySzLog2)) + aSector;
+
+ if(pFatBitCache->FatSectorHasFreeEntry(absSectorNum))
+ { //-- it means that the corresponding FAT cache sector may or may not contain free FAT entry.
+ //-- in this case we need to repopulate corresponding bit cache entry.
+
+ const TUint32 numEntries = dataLen >> KFat32EntrySzLog2; //-- amount of FAT entries in this sector
+ const TFat32Entry* pFat32Entry = (const TFat32Entry* )pData;
+
+ TBool bHasFreeFatEntry = EFalse;
+
+ for(TUint i=0; i<numEntries; ++i)
+ {//-- look for free entries in this particular FAT cache sector.
+ if(pFat32Entry[i] == KSpareCluster)
+ {
+ bHasFreeFatEntry = ETrue;
+ break;
+ }
+ }
+
+ if(!bHasFreeFatEntry)
+ { //-- FAT bit cache indicates that FAT sector absSectorNum has free entries, but it doesn't.
+ //-- this is because we can only set "has free entry" flag in CAtaFatTable::WriteL().
+ //-- correct FAT bit cache entry
+ pFatBitCache->SetFreeEntryInFatSector(absSectorNum, EFalse);
+
+ //__PRINT2(_L("#++ :DoWriteSectorL() Fixed FAT bit cache BitVec[%d]=%d"), absSectorNum, pFatBitCache->FatSectorHasFreeEntry(absSectorNum));
+ }
+
+ }
+ else //if(pBitCache->FatSectorHasFreeEntry(absSectorNum))
+ {//-- don't need to do anything. The corresponding FAT cache sector never contained free FAT entry and
+ //-- free FAT entry has never been written there in CAtaFatTable::WriteL().
+ }
+
+ }//if(pFatBitCache && pFatBitCache->UsableState())
+
+
+ }
+
+
+//-----------------------------------------------------------------------------
+/**
+ Write FAT32 entry at aFatIndex to the cache. Note that the data are not written to the media, only to the cache page.
+ Corresponding page sector is marked as dirty and will be flushed on FlushL() call later.
+
+ 1. If the entry at aFatIndex doesn't belong to this page, returns EFalse
+ 2. If page's data are valid and the entry is cached, copies data to the page buffer and marks sector as dirty.
+ 3. If page's data are invalid but the entry's index belongs to this page, firstly reads data from the media and goto 2
+
+ @param aFatIndex entry's absolute FAT index (from the FAT start)
+ @param aFatEntry FAT32 entry value
+ @return ETrue if the entry at aFatIndex belongs to this page (cached) and in this case aResult will contain this entry.
+ EFalse if the entry isn't cached.
+
+*/
+TBool CFat32LruCachePage::WriteCachedEntryL(TUint32 aFatIndex, TUint32 aFatEntry)
+ {
+
+ if(!IsEntryCached(aFatIndex))
+ return EFalse; //-- the page doesn't contain required index
+
+ if(!IsValid())
+ {//-- we are trying to write data to the page that has invalid data. //-- read the data from the media first.
+ ReadFromMediaL(aFatIndex);
+ }
+
+ //-- for FAT32 only low 28 bits are used, 4 high are reserved; preserve them
+ TFat32Entry* pEntry = GetEntryPtr(aFatIndex);
+ const TFat32Entry orgEntry = *pEntry;
+ *pEntry = (orgEntry & ~KFat32EntryMask) | (aFatEntry & KFat32EntryMask);
+
+ //-- mark corresponding sector of the cache page as dirty
+ const TUint entryIndexInPage = aFatIndex & (EntriesInPage()-1); //-- number of entries in page is always a power of 2
+ const TUint dirtySectorNum = entryIndexInPage >> (iCache.SectorSizeLog2() - KFat32EntrySzLog2);
+
+ ASSERT(dirtySectorNum < iCache.SectorsInPage());
+
+ iDirtySectors.SetBit(dirtySectorNum);
+ SetState(EDirty); //-- mark page as dirty.
+
+ return ETrue;
+ }
+
+
+
+//#################################################################################################################################
+// CFatBitCache implementation
+//#################################################################################################################################
+
+//-- define this macro for extra debugging facilities for the CFatBitCache
+//-- probably needs to be removed completely as soon as everything settles
+//#define FAT_BIT_CACHE_DEBUG
+
+//-----------------------------------------------------------------------------
+
+CFatBitCache::CFatBitCache(CFat32LruCache& aOnwerFatCache)
+ :iOwnerFatCache(aOnwerFatCache)
+ {
+ SetState(EInvalid);
+ DBG_STATEMENT(iPopulatingThreadId=0);
+ }
+
+CFatBitCache::~CFatBitCache()
+ {
+ Close();
+ }
+
+//-----------------------------------------------------------------------------
+/**
+ FAT bit supercache factory method
+ @return pointer to the created object or NULL if it coud not create or initialise it.
+*/
+CFatBitCache* CFatBitCache::New(CFat32LruCache& aOnwerFatCache)
+ {
+ __PRINT(_L("#++ CFatBitCache::New()"));
+
+ CFatBitCache* pSelf = NULL;
+ pSelf = new CFatBitCache(aOnwerFatCache);
+
+ if(!pSelf)
+ return NULL; //-- failed to create object
+
+ TInt nRes = pSelf->Initialise();
+ if(nRes != KErrNone)
+ {//-- failed to initialise the object
+ delete pSelf;
+ pSelf = NULL;
+ }
+
+ return pSelf;
+ }
+
+
+//-----------------------------------------------------------------------------
+
+/**
+ Initialisation.
+ Note that this cache suports FAT32 only.
+ @return KErrNone on success; otherwise standard error code.
+*/
+TInt CFatBitCache::Initialise()
+ {
+ __PRINT(_L("#++ CFatBitCache::Initialise()"));
+
+ Close();
+
+ //-- only FAT32 supported
+ if(iOwnerFatCache.FatType() != EFat32)
+ {
+ ASSERT(0);
+ Fault(EFatCache_BadFatType);
+ }
+
+ //-- create the bit vector. each bit position there represents one FAT cache sector (in FAT cache page terms, see FAT page structure)
+ const TUint fatSize = iOwnerFatCache.FatSize(); //-- FAT size in bytes
+ const TUint fatCacheSecSize = Pow2(iOwnerFatCache.SectorSizeLog2()); //-- FAT cache sector size
+ const TUint maxFatUsableCacheSectors = (fatSize + (fatCacheSecSize-1)) >> iOwnerFatCache.SectorSizeLog2(); //-- maximal number of usable fat cache sectors in whole FAT table
+
+ //-- create a bit vector
+ __PRINT1(_L("#++ CFatBitCache::Initialise() FAT supercache bits:%u"), maxFatUsableCacheSectors);
+
+ TInt nRes = iBitCache.Create(maxFatUsableCacheSectors);
+ if(nRes != KErrNone)
+ {
+ __PRINT1(_L("#++ Failed to create a bit vector! code:%d"), nRes);
+ return nRes;
+ }
+
+ //-- calculate the coefficient to be used to convet FAT index to FAT cache sector number (bit vector index).
+ iFatIdxToSecCoeff = iOwnerFatCache.SectorSizeLog2()-KFat32EntrySzLog2;
+ SetState(ENotPopulated);
+
+ return KErrNone;
+ }
+
+//-----------------------------------------------------------------------------
+/**
+ Closes the cache and deallocates bit vector memory.
+*/
+void CFatBitCache::Close()
+ {
+ __PRINT(_L("#++ CFatBitCache::Close()"));
+
+ //-- this method must not be called during populating (optionally by another thread)
+ ASSERT(State() != EPopulating);
+ ASSERT(iPopulatingThreadId == 0);
+
+ iBitCache.Close();
+ SetState(EInvalid);
+ }
+
+//-----------------------------------------------------------------------------
+
+/**
+ Tell the cache that we are starting to populate it.
+ N.B. Start, Finish and populating methods shall be called from the same thread.
+ Only one thread can be populating the bit vector;
+
+ @return ETrue on success. Efalse means that the cache is in the invalid state for some reason.
+*/
+TBool CFatBitCache::StartPopulating()
+ {
+ __PRINT2(_L("#++ CFatBitCache::StartPopulating(), State:%d, ThreadId:%d"), State(), (TUint)RThread().Id());
+
+ if(State() != ENotPopulated)
+ {//-- wrong state
+ ASSERT(0);
+ return EFalse;
+ }
+
+ ASSERT(iPopulatingThreadId == 0);
+
+ iBitCache.Fill(0);
+ SetState(EPopulating);
+
+ //-- store the the ID of the thread that starts populating the cache; it'll be checked later during populating.
+ DBG_STATEMENT(iPopulatingThreadId = RThread().Id());
+
+ return ETrue;
+ }
+
+//-----------------------------------------------------------------------------
+
+/**
+ Tell the cache that we have finished to populate it.
+
+ @return ETrue on success. EFalse means that the cache is in the invalid state for some reason.
+*/
+TBool CFatBitCache::FinishPopulating(TBool aSuccess)
+ {
+ __PRINT2(_L("#++ CFatBitCache::PopulatingFinished(), ThreadId:%d, success:%d"), (TUint)RThread().Id(), aSuccess);
+
+ if(State() != EPopulating)
+ {//-- wrong state
+ ASSERT(0);
+ return EFalse;
+ }
+
+ ASSERT(iPopulatingThreadId == RThread().Id()); //-- check that this method is called from the same thread that started populating
+ DBG_STATEMENT(iPopulatingThreadId = 0);
+
+ if(aSuccess)
+ SetState(EPopulated); //-- the cache is usable; populated OK
+ else
+ SetState(EInvalid); //-- the cache isn't populated properly, make it not usable
+
+ return ETrue;
+ }
+
+//-----------------------------------------------------------------------------
+/**
+ Tell FAT bit cache that there is a free entry at FAT aFatIndex.
+ Only this method can be used to populate the bit array (in EPopulating state).
+ Other methods can't access bit array in EPopulating state i.e. it is safe to populate the cache
+ from the thread other than FS drive thread (e.g within background FAT scan)
+
+ @param aFatIndex free FAT32 entry index
+ @return ETrue on success. EFalse means that the cache is in the invalid state for some reason.
+*/
+TBool CFatBitCache::SetFreeFatEntry(TUint32 aFatIndex)
+ {
+ //__PRINT3(_L("#++ ReportFreeFatEntry: idx:%d, state:%s, tid:%d"), aFatIndex, State(), (TUint)RThread().Id());
+
+ if(State() != EPopulating && State() != EPopulated)
+ {//-- wrong state, this can happen if someone forcedly invalidated this cache during populating
+ return EFalse;
+ }
+
+#if defined _DEBUG && defined FAT_BIT_CACHE_DEBUG
+ //-- This leads to serious performance degradation, so be careful with it.
+ if(State() == EPopulating)
+ {//-- check that this method is called from the same thread that started populating
+ if(iPopulatingThreadId != RThread().Id())
+ {
+ __PRINT3(_L("#++ !! ReportFreeFatEntry: Access from different thread!! idx:%d, state:%d, tid:%d"), aFatIndex, State(), (TUint)RThread().Id());
+ }
+ //ASSERT(iPopulatingThreadId == RThread().Id());
+ }
+#endif
+
+ //-- set bit to '1' which indicates that the FAT cache sector corresponding to the aFatIndex has at least one free FAT entry
+ const TUint32 bitNumber = FatIndexToCacheSectorNumber(aFatIndex); //-- index in the bit array corresponding FAT cache sector
+
+#if defined _DEBUG && defined FAT_BIT_CACHE_DEBUG
+ //-- This leads to serious performance degradation, so be careful with it.
+ TBool b = iBitCache[bitNumber];
+ if(!b && State()==EPopulated)
+ {//-- someone is reporting a free entry in the given cache sector.
+ __PRINT1(_L("#++ CFatBitCache::ReportFreeFatEntry BitVec[%d]=1"), bitNumber);
+ }
+#endif
+
+
+ iBitCache.SetBit(bitNumber);
+
+ return ETrue;
+ }
+
+//-----------------------------------------------------------------------------
+/**
+ Forcedly mark a part of the FAT bit super cache as containing free clusters (or not).
+
+ @param aStartFatIndex start FAT index of the range
+ @param aEndFatIndex end FAT index of the range
+ @param aAsFree if ETrue, the range will be marked as containing free clusters
+*/
+void CFatBitCache::MarkFatRange(TUint32 aStartFatIndex, TUint32 aEndFatIndex, TBool aAsFree)
+ {
+ __PRINT3(_L("#++ CFatBitCache::MarkFatRange(%d, %d, %d)"), aStartFatIndex, aEndFatIndex, aAsFree);
+
+ ASSERT(State() == EPopulating || State() == EPopulated);
+
+ const TUint32 bitNumberStart = FatIndexToCacheSectorNumber(aStartFatIndex);
+ const TUint32 bitNumberEnd = FatIndexToCacheSectorNumber(aEndFatIndex);
+
+ iBitCache.Fill(bitNumberStart, bitNumberEnd, aAsFree);
+ }
+
+
+//-----------------------------------------------------------------------------
+/**
+ Try to locate closest to the aFatIndex free FAT entry in the FAT32 LRU cache.
+ This is done by several steps:
+
+ 1. Try to find FAT cache sector containing free FAT entry (by using FAT sectors bitmap)
+ 2. locate free FAT entry within this sector.
+
+ @param aFatIndex in: absolute FAT entry index that will be used to start search from (we need to find the closest free entry to it)
+ out: may contain FAT index of the located free entry.
+
+ @return one of the completion codes:
+ KErrNone free entry found and its index is in aFatIndex
+ KErrNotFound FAT sector closest to the aFatIndex entry doesn't contain free FAT entries; the conflict is resolved, need to call this method again
+ KErrEof couldn't find any free sectors in FAT; need to fall back to the old search method
+ KErrCorrupt if the state of the cache is inconsistent
+*/
+TInt CFatBitCache::FindClosestFreeFatEntry(TUint32& aFatIndex)
+ {
+ const TUint32 startFatCacheSec = FatIndexToCacheSectorNumber(aFatIndex);
+
+ //__PRINT2(_L("#++ CFatBitCache::FindClosestFreeFatEntry() start idx:%d, start cache sec:%d"), aFatIndex, startFatCacheSec);
+
+ ASSERT(aFatIndex >= KFatFirstSearchCluster);
+ if(!UsableState())
+ {
+ ASSERT(0);
+ return KErrCorrupt;
+ }
+
+ TUint32 fatSeekCacheSec = startFatCacheSec; //-- FAT cache sector number that has free FAT entry, used for search .
+ TUint32 fatSeekIndex = aFatIndex; //-- FAT index to start search with
+
+ //-- 1. look if FAT sector that corresponds to the aStartFatIndex already has free entries.
+ //-- 2. if not, try to locate closest FAT cache sector that has by searching a bit vector
+ if(FatSectorHasFreeEntry(fatSeekCacheSec))
+ {
+ }
+ else
+ {//-- look in iBitCache for '1' entries nearest to the fatCacheSec, right side priority
+
+ if(!iBitCache.Find(fatSeekCacheSec, 1, RBitVector::ENearestR))
+ {//-- strange situation, there are no '1' bits in whole vector, search failed
+ __PRINT(_L("#++ CFatBitCache::FindClosestFreeFatEntry() bit vector search failed!"));
+ return KErrEof;
+ }
+
+ //-- bit cache found FAT sector(fatSeekCacheSec) that may have free FAT entries
+ //-- calculate FAT entry start index in this sector
+ fatSeekIndex = Max(KFatFirstSearchCluster, CacheSectorNumberToFatIndex(fatSeekCacheSec));
+ }
+
+ //-- here we have absolute FAT cache sector number, which may contain at least one free FAT entty
+ ASSERT(FatSectorHasFreeEntry(fatSeekCacheSec));
+
+ //-- ask FAT cache to find the exact index of free FAT entry in this particular FAT cache sector
+ TInt nRes;
+ TBool bFreeEntryFound=EFalse;
+
+ TRAP(nRes, bFreeEntryFound = iOwnerFatCache.FindFreeEntryInCacheSectorL(fatSeekIndex));
+ if(nRes != KErrNone)
+ {//-- it's possible on media read error
+ return KErrCorrupt;
+ }
+
+ if(bFreeEntryFound)
+ {//-- found free entry at aNewFreeEntryIndex
+ aFatIndex = fatSeekIndex;
+ return KErrNone;
+ }
+
+ //-- bit cache mismatch; its entry ('1') indicates that cache sector number fatCacheSec has free FAT entries,
+ //-- while in reality it doesnt. We need to fix the bit cache.
+ //__PRINT1(_L("#++ CFatBitCache::FindClosestFreeFatEntry fixing cache conflict; BitVec[%d]=0"), fatSeekCacheSec);
+ SetFreeEntryInFatSector(fatSeekCacheSec, EFalse);
+
+ return KErrNotFound;
+ }
+
+
+//-----------------------------------------------------------------------------
+/**
+ Print out the contents of the object. This is a debug only method
+*/
+void CFatBitCache::Dump() const
+{
+#if defined _DEBUG && defined FAT_BIT_CACHE_DEBUG
+
+ const TUint32 vecSz = iBitCache.Size();
+ __PRINT2(_L("#++ CFatBitCache::Dump(): state:%d, entries:%d"), State(), vecSz);
+
+
+ TBuf<120> printBuf;
+ const TUint KPrintEntries = 32;
+
+ TUint i;
+ printBuf.Append(_L(" "));
+ for(i=0; i<KPrintEntries; ++i)
+ {
+ printBuf.AppendFormat(_L("%02d "),i);
+ }
+
+ __PRINT(printBuf);
+ for(i=0; i<vecSz;)
+ {
+ printBuf.Format(_L("%03d: "), i);
+ for(TInt j=0; j<KPrintEntries; ++j)
+ {
+ if(i >= vecSz)
+ break;
+
+ printBuf.AppendFormat(_L("% d "), (iBitCache[i]!=0));
+ ++i;
+ }
+ __PRINT(printBuf);
+
+ }
+#endif
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+