userlibandfileserver/fileserver/sfile/sf_file_cache.cpp
changeset 43 96e5fb8b040d
child 109 b3a1d9898418
child 110 c734af59ce98
child 131 e880629062dd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/userlibandfileserver/fileserver/sfile/sf_file_cache.cpp	Thu Dec 17 09:24:54 2009 +0200
@@ -0,0 +1,2460 @@
+// Copyright (c) 2006-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\sfile\sf_file_cache.cpp
+// 
+//
+
+/**
+ @file
+ @internalTechnology
+*/
+
+#include <e32std.h>
+#include <e32std_private.h>
+#include "sf_std.h"
+#include <e32uid.h>
+#include <e32wins.h>
+#include <f32file.h>
+#include <hal.h>
+#include "sf_file_cache.h"
+#include "sf_cache_man.h"
+#include "sf_cache_client.h"
+#include "sf_file_cache_defs.h"
+
+
+// disables flushing of stale cachelines before each write
+#define LAZY_WRITE
+
+enum TFileCacheFault
+	{
+	ECacheSettingsInitFailed,
+	ECacheSettingsNotFound,
+	ECacheSettingGetFailed,
+	ECacheCodeFault,
+	ECacheBadOperationIndex,
+	ENoFileCache,
+	EFileAlreadyClosed,
+	EClosingDirtyFile,
+	ECompletingWriteWithDataRemaining,
+	EPosBeyondSize,
+	EMsgAlreadyCompleted,
+	EBadRetCode,
+	EInternalError,
+	ERequestUncancelled,
+	ELockAlreadyOpen,
+	EBadSegmentCount,
+	EReNewingOpenCache,
+	EClosingUnNamedFile,
+	EFileNameAlreadyOwned,
+	EClosedFileHasNoName,
+	EReOpeningUnNamedFile,
+	EStartingDirtyAWrongStage,
+	EUnexpectedReNewLFailure,
+	EDirtyDataOwnerNull,
+	EFlushingWithSessionNull,
+	};
+
+
+LOCAL_C void Fault(TFileCacheFault aFault)
+//
+// Report a fault in the file cache
+//
+	{
+	User::Panic(_L("FSFILECACHE"), aFault);
+	}
+
+const TInt KMinSequentialReadsBeforeReadAhead = 3;
+
+//************************************
+// CFileCache
+//************************************
+
+inline TBool ReadCachingEnabled(CFileShare& aShare)
+	{
+	TUint mode = aShare.iMode;
+	
+	TInt fileCacheFlags = TFileCacheSettings::Flags(aShare.File().Drive().DriveNumber());
+
+	if (((mode & EFileReadBuffered) && (fileCacheFlags & (EFileCacheReadEnabled | EFileCacheReadOn))) ||
+		(((mode & EFileReadDirectIO) == 0) && (fileCacheFlags & EFileCacheReadOn)))
+		{
+		return ETrue;
+		}
+	else
+		{
+		return EFalse;
+		}
+	}
+
+inline TBool WriteCachingEnabled(CFileShare& aShare)
+	{
+	TUint mode = aShare.iMode;
+	
+	TInt fileCacheFlags = TFileCacheSettings::Flags(aShare.File().Drive().DriveNumber());
+
+	if ((mode & EFileWrite) == 0)
+		{
+		return EFalse;
+		}
+	else if (((mode & EFileWriteBuffered) && (fileCacheFlags & (EFileCacheWriteEnabled | EFileCacheWriteOn))) ||
+		(((mode & EFileWriteDirectIO) == 0) && (fileCacheFlags & EFileCacheWriteOn)))
+		{
+		return ETrue;
+		}
+	else
+		{
+		return EFalse;
+		}
+	}
+
+void CFileCache::SetFileCacheFlags(CFileShare& aShare)
+	{
+	TInt fileCacheFlags = TFileCacheSettings::Flags(iDriveNum);
+
+	TUint& mode = aShare.iMode;
+
+	// enable/disable read ahead
+	if (((mode & EFileReadAheadOn) && (fileCacheFlags & (EFileCacheReadAheadEnabled | EFileCacheReadAheadOn))) ||
+		 (((mode & EFileReadAheadOff) == 0) && (fileCacheFlags & EFileCacheReadAheadOn)))
+		{
+		__CACHE_PRINT(_L("CACHEFILE: EFileCacheReadAhead ENABLED"));
+		mode|= EFileReadAheadOn;
+		}
+	else
+		{
+		__CACHE_PRINT(_L("CACHEFILE: EFileCacheReadAhead disabled"));
+		mode&= ~EFileReadAheadOn;
+		}
+
+	// enable/disable read caching 
+	if (ReadCachingEnabled(aShare))
+		{
+		__CACHE_PRINT(_L("CACHEFILE: EFileCacheRead ENABLED"));
+		mode|= EFileReadBuffered;
+		}
+	else
+		{
+		__CACHE_PRINT(_L("CACHEFILE: EFileCacheRead disabled"));
+		// if read caching is off, turn off read-ahead too
+		mode&= ~(EFileReadBuffered | EFileReadAheadOn);
+		}
+
+	// enable/disable write caching 
+	if (WriteCachingEnabled(aShare))
+		{
+		__CACHE_PRINT(_L("CACHEFILE: EFileCacheWrite ENABLED"));
+		mode|= EFileWriteBuffered;
+		}
+	else
+		{
+		__CACHE_PRINT(_L("CACHEFILE: EFileCacheWrite disabled"));
+		mode&= ~EFileWriteBuffered;
+		}
+
+	}
+
+void CFileCache::ConstructL(CFileShare& aShare)
+	{
+	iDrive = &aShare.File().Drive();
+	iDriveNum = iDrive->DriveNumber();
+	iMount=&iDrive->CurrentMount();
+
+	DoInitL(iDriveNum);	
+	
+
+	CCacheManager* manager = CCacheManagerFactory::CacheManager();
+
+	iCacheClient = manager->CreateClientL();
+	manager->RegisterClient(*iCacheClient);
+	
+
+	TInt segmentSize = SegmentSize();
+	TInt segmentSizeMask = I64LOW(SegmentSizeMask());
+	// Get file cache size
+	iCacheSize			= TFileCacheSettings::CacheSize(iDriveNum);
+	// must be at least one segment
+	iCacheSize			= Max(iCacheSize, segmentSize);
+	// round up to nearest whole segment
+	iCacheSize			= (iCacheSize + segmentSize - 1) & segmentSizeMask;
+
+	// Get max read-ahead length
+	iMaxReadAheadLen	= TFileCacheSettings::MaxReadAheadLen(iDriveNum);
+	// must be at least one segment
+	iMaxReadAheadLen	= Max(iMaxReadAheadLen, segmentSize);
+	// round up to nearest whole segment
+	iMaxReadAheadLen	= (iMaxReadAheadLen + segmentSize - 1) & segmentSizeMask;
+	// read-ahead should not be greater than the cache size minus one segment
+	iMaxReadAheadLen	= Min(iMaxReadAheadLen, iCacheSize - segmentSize);
+	// ... or greater than one cacheline (128K should be enough !)
+	iMaxReadAheadLen	= Min(iMaxReadAheadLen, iCacheClient->CacheLineSize());
+
+	iFileCacheReadAsync = TFileCacheSettings::FileCacheReadAsync(iDriveNum);
+
+	iClosedFileKeepAliveTime	= TFileCacheSettings::ClosedFileKeepAliveTime(iDriveNum);
+	iDirtyDataFlushTime			= TFileCacheSettings::DirtyDataFlushTime(iDriveNum);
+
+	// Calculate max number of segments to cache
+	TInt maxSegmentsToCache = iCacheSize >> SegmentSizeLog2();
+
+	__CACHE_PRINT1(_L("CACHEFILE: maxSegmentsToCache %d"), maxSegmentsToCache);
+
+	iCacheClient->SetMaxSegments(maxSegmentsToCache);
+
+	CFileCache* fileCache = ReNewL(aShare);
+	__ASSERT_ALWAYS(fileCache != NULL, Fault(EUnexpectedReNewLFailure));
+	}
+
+CFileCache* CFileCache::NewL(CFileShare& aShare)
+	{
+	__CACHE_PRINT(_L("CACHEFILE: CFileCache::NewL()"));
+
+	CFileCB* file = &aShare.File();
+	if ((CCacheManagerFactory::CacheManager() == NULL) ||
+		(!ReadCachingEnabled(aShare) && !WriteCachingEnabled(aShare)) ||
+		(FsThreadManager::IsDriveSync(file->Drive().DriveNumber(),EFalse)))
+		return NULL;
+
+	CFileCache* fileCache = new(ELeave) CFileCache();
+	CleanupClosePushL(*fileCache);
+	fileCache->ConstructL(aShare);
+	CleanupStack::Pop(1, fileCache);
+	return fileCache;
+	}
+
+CFileCache* CFileCache::ReNewL(CFileShare& aShare)
+	{
+	__CACHE_PRINT(_L("CACHEFILE: CFileCache::ReNewL()"));
+
+	// check not already open i.e. attached to a CFileCB
+	__ASSERT_DEBUG(iFileCB == NULL, Fault(EReNewingOpenCache));
+
+	// make sure the drive thread exists (the mount may have been mounted
+	// synchronously since the drive was last open)
+	const TInt r = FsThreadManager::GetDriveThread(iDriveNum, &iDriveThread);
+	if ((r!= KErrNone) || !iDriveThread)
+		return NULL;
+
+	// if re-opening in DirectIo mode, destroy the file cache 
+	if (!ReadCachingEnabled(aShare) && !WriteCachingEnabled(aShare))
+		{
+		Close();
+		return NULL;
+		}
+
+	SetFileCacheFlags(aShare);
+
+	// assign ownership of this object to aFileCB before any leaves occur
+	iFileCB = &aShare.File();
+	iFileCB->iBody->iFileCache = this;
+	
+	__ASSERT_DEBUG(iLock.Handle() == KNullHandle, Fault(ELockAlreadyOpen));
+	User::LeaveIfError(iLock.CreateLocal());
+
+	// delete the file name to save heap space (it's only needed 
+	// while the file is on the closed queue so that it can be matched)
+	delete iFileNameF;
+	iFileNameF = NULL;
+
+	return this;
+	}
+
+void CFileCache::Init(CFileShare& aShare)
+	{
+	SetFileCacheFlags(aShare);
+	}
+
+CFileCache::CFileCache() : 
+	iClosedTimer(ClosedTimerEvent, this),
+	iDirtyTimer(DirtyTimerEvent, this)
+	{
+	}
+
+CFileCache::~CFileCache()
+	{
+	iLock.Close();
+
+	// stop the owning CFileCB from pointing to an about-to-be deleted object
+	if (iFileCB && iFileCB->iBody)
+		iFileCB->iBody->iFileCache = NULL;
+
+	CCacheManagerFactory::CacheManager()->DeregisterClient(*iCacheClient);
+
+	delete iCacheClient;
+
+	delete iFileNameF;
+	}
+
+
+TInt CFileCache::ClosedTimerEvent(TAny* aFileCache)
+	{
+	TClosedFileUtils::Remove((CFileCache*) aFileCache);
+	return KErrNone;
+	}
+
+TInt CFileCache::DirtyTimerEvent(TAny* aFileCache)
+	{
+	// Cannot report errors here
+	// coverity [unchecked_value]
+	(void)((CFileCache*) aFileCache)->FlushDirty();
+
+	return KErrNone;
+	}
+
+
+
+void CFileCache::Close()
+	{
+	__CACHE_PRINT1(_L("CFileCache::Close() 0x%x"),this);
+
+	TInt r = KErrNone;
+
+#ifdef _DEBUG
+	if (iCacheClient)	// NB Object may not have been fully constructed
+		{
+		TInt64  pos;
+		TUint8* addr;
+		__ASSERT_DEBUG(((iCacheClient->FindFirstDirtySegment(pos, addr)) == 0), Fault(EClosingDirtyFile));
+		__ASSERT_DEBUG(!iDirtyDataOwner, Fault(EClosingDirtyFile));
+		}
+#endif
+		
+		
+	__CACHE_PRINT2(_L("CFileCache::Close() iFileCB %08X IsClosed %d"), 
+		iFileCB, TClosedFileUtils::IsClosed(this));
+	// if not already closed move to closed file queue
+	if (iFileCB != NULL && 
+		!iFileCB->DeleteOnClose() &&
+		!TClosedFileUtils::IsClosed(this) && 
+		IsDriveThread())
+		{
+		// add to ClosedFiles container
+		__CACHE_PRINT1(_L("CLOSEDFILES: Adding %S\n"), &iFileCB->FileNameF() );
+		TRAP(r, TClosedFileUtils::AddL(this, ETrue));
+
+		// Acquire ownership of the CFileCB's file name
+		__ASSERT_DEBUG(iFileCB->iFileNameF, Fault(EClosingUnNamedFile));
+		__ASSERT_DEBUG(iFileNameF == NULL, Fault(EFileNameAlreadyOwned));
+		iFileNameF = iFileCB->iFileNameF;
+		iNameHash = iFileCB->iNameHash;
+		iFileCB->iFileNameF = NULL;
+
+		// remove pointer to owning CFileCB as this is called from CFileCB's destructor
+		iFileCB = NULL;
+
+		// Successfully moved file !
+		if (r == KErrNone)
+			{
+			// close the RFastLock object here to prevent OOM kernel tests from failing
+			iLock.Close();
+			return;
+			}
+		}
+
+	iClosedTimer.Stop();
+
+	// if already closed, close for good. N.B. CFsObject::DoClose() will 
+	// cause it to be removed from the ClosedFiles container
+	CFsDispatchObject::Close();
+	}
+
+
+CMountCB& CFileCache::Mount() const
+	{
+	return *iMount;
+	}
+
+CFileCB* CFileCache::FileCB()
+	{
+	return iFileCB;
+	}
+
+
+
+TInt CFileCache::SegmentSize() const
+	{
+	return iCacheClient->SegmentSize();
+	}
+
+TInt CFileCache::SegmentSizeLog2() const
+	{
+	return iCacheClient->SegmentSizeLog2();
+	}
+
+TInt64 CFileCache::SegmentSizeMask() const
+	{
+	return iCacheClient->SegmentSizeMask();
+	}
+
+/**
+Sets the cached file size  
+
+@param aSize The size of the file.
+*/  
+void CFileCache::SetSize64(TInt64 aSize)
+	{
+	__e32_atomic_store_ord64(&iSize64, aSize);
+	}
+
+TDrive& CFileCache::Drive() const
+	{
+	return *iDrive;
+	}
+
+TUint32 CFileCache::NameHash() const
+	{
+	return(iNameHash);
+	}
+
+HBufC& CFileCache::FileNameF() const
+	{
+	__ASSERT_DEBUG(iFileNameF, Fault(EClosedFileHasNoName));
+	return(*iFileNameF);
+	}
+
+
+
+
+TBool CFileCache::IsDriveThread()
+	{
+	return FsThreadManager::IsDriveThread(iDriveNum, EFalse);
+	}
+
+
+void CFileCache::ResetReadAhead()
+	{
+//	iSequentialReads = 0;
+	iReadAheadLen = 0;
+	iReadAheadPos = 0;
+	}
+
+/**
+ReadAhead() - 
+dispatches a new message to fill the read cache if 
+(ReadAheadPos - ShareReadPos) <= (ReadAheadLen)
+
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+		^				^
+		|----- ReadAheadLen ----------------->
+		|				| 
+  ShareReadPos		ReadAheadPos	
+
+Every successful read-ahead doubles ReadAheadLen until it reaches the maximum 
+(KDefaultFileCacheMaxReadAheadLen). 
+*/
+void CFileCache::ReadAhead(CFsMessageRequest& aMsgRequest, TUint aMode)
+	{
+	if (!(aMode & EFileReadAheadOn) ||
+		(iSequentialReads < KMinSequentialReadsBeforeReadAhead) ||
+		iMaxReadAheadLen == 0)
+		return;
+
+
+	TInt segmentSize = SegmentSize();
+	TInt64 segmentSizeMask = SegmentSizeMask();
+	
+	// if the read-ahead pos has been reset to zero, then the read-ahead 
+	// position and length must be re-calculated
+
+	TBool resetting = (iReadAheadPos == 0)?(TBool)ETrue:(TBool)EFalse;
+	if (resetting)
+		{
+		iReadAheadPos = (iLastReadPos + segmentSize - 1) & segmentSizeMask;	
+
+		// ensure read ahead len at least as big as last read
+		iReadAheadLen = Max(iReadAheadLen, iLastReadLen);	
+			
+		// round up to a segment size
+		iReadAheadLen = (iReadAheadLen + segmentSize - 1) & (TInt) segmentSizeMask;
+
+		// ensure read ahead len at least as big as 1 segments 
+		iReadAheadLen = Max(iReadAheadLen, segmentSize);	
+			
+		// ensure read ahead len not greater than the maximum read ahead len
+		iReadAheadLen = Min(iReadAheadLen, iMaxReadAheadLen);
+
+		iReadAheadRequest = NULL;
+		}
+
+	TInt bytesBuffered = (TInt) (iReadAheadPos - iLastReadPos);
+	TInt bytesNotBuffered = iMaxReadAheadLen - bytesBuffered;
+
+
+	// if read-ahead buffer len > current read-ahead len OR
+	// read-ahead buffer is more than half full, do nothing
+	if ((iReadAheadRequest) ||
+		(bytesBuffered > iReadAheadLen) || 
+		(bytesBuffered > (iMaxReadAheadLen>>1)) ||
+		(iReadAheadPos >= iSize64))
+		{
+		return;
+		}
+
+	// double the read-ahead length - unless this is the first
+	if (!resetting)
+		iReadAheadLen<<= 1;
+
+	// ensure read ahead len not greater than the free space available in buffer
+	iReadAheadLen = Min(iReadAheadLen, bytesNotBuffered);
+
+	// round up to a segment size
+	iReadAheadLen = (iReadAheadLen + segmentSize - 1) & (TInt) segmentSizeMask;
+
+#if defined (_DEBUG_READ_AHEAD)
+	TInt64 oldReadAheadPos = iReadAheadPos;
+#endif
+
+	DoReadAhead(aMsgRequest, aMode);
+
+
+//	RDebug::Print(_L("Buffered: old %d new %d"), bytesBuffered, (iReadAheadPos == 0)?bytesBuffered:iReadAheadPos - iLastReadPos);
+
+#if defined (_DEBUG_READ_AHEAD)
+RDebug::Print(_L("Buffered: old %d new %d iLastReadPos %d ReadAheadPos old %d new %d iReadAheadLen %d"), 
+	bytesBuffered, 
+	(TInt) ((iReadAheadPos == 0)?bytesBuffered:iReadAheadPos - iLastReadPos),
+	I64LOW(iLastReadPos),
+	I64LOW(oldReadAheadPos),
+	I64LOW(iReadAheadPos),
+	iReadAheadLen);
+#endif	// (_DEBUG_READ_AHEAD)
+
+	
+	return;
+	}
+
+
+void CFileCache::DoReadAhead(CFsMessageRequest& aMsgRequest, TUint aMode)
+	{
+
+
+	if (iCacheClient->FindSegment(iReadAheadPos) != NULL)
+		{
+		// if read ahead pos is already cached, then synchronous reads have caught up,
+		// so reset read ahead pos.
+#if defined (_DEBUG_READ_AHEAD)
+		RDebug::Print(_L("ReadAhead: pos %d already cached"), I64LOW(iReadAheadPos));
+#endif
+		iReadAheadPos = 0;
+		return;
+		}
+
+
+	CFsClientMessageRequest* newRequest = NULL;
+	TInt r = AllocateRequest(newRequest, EFalse, aMsgRequest.Session());
+	if (r != KErrNone)
+		return;
+
+	r = newRequest->PushOperation(
+		iReadAheadPos, 
+		iReadAheadLen,
+		(TUint8*) NULL, 
+		0,						// aOffset 
+		NULL);					// aCallback
+	if (r != KErrNone)
+		{
+		newRequest->Free();
+		newRequest = NULL;
+		return;
+		}
+
+	__CACHE_PRINT2(_L("TFsFileRead: ReadAhead pos %ld len %d"), newRequest->CurrentOperation().iReadWriteArgs.iPos, newRequest->CurrentOperation().iReadWriteArgs.iTotalLength);
+
+//	RDebug::Print(_L("ReadH:\tpos %d\tlen %d"), I64LOW(newRequest->CurrentOperation().iReadWriteArgs.iPos), newRequest->CurrentOperation().iReadWriteArgs.iTotalLength);
+
+	r = ReadBuffered(*newRequest, aMode);
+	if (r != CFsRequest::EReqActionContinue)
+		{
+		// if read ahead pos is already cached, then synchronous reads have caught up,
+		// so reset read ahead pos.
+		if (r == CFsRequest::EReqActionComplete)
+			{
+#if defined (_DEBUG_READ_AHEAD)
+			RDebug::Print(_L("ReadAhead pos %d ALREADY DONE !!!"), I64LOW(iReadAheadPos));
+#endif
+			iReadAheadPos = 0;
+			}
+
+		newRequest->PopOperation();
+		newRequest->Free();
+		newRequest = NULL;
+		return;
+		}
+
+	iReadAheadPos = iReadAheadPos + iReadAheadLen;
+	iReadAheadRequest = newRequest;
+
+#if defined (_DEBUG_READ_AHEAD)
+	RDebug::Print(_L("Dispatching ReadAhead with %s priority"), iFileCacheReadAsync?_S16("HIGH"):_S16("LOW"));
+#endif
+	// If if media driver reads are synchronous (i.e. not interrupt driven) dispatch read-ahead 
+	// with low priority  so that it doesn't prevent client thread from running
+	newRequest->Dispatch(
+		EFalse,					// don't init
+		iFileCacheReadAsync?(TBool)EFalse:(TBool)ETrue, 
+		EFalse);				// dispatch to back
+	}
+
+TInt CFileCache::CompleteRead(CFsRequest* aRequest)
+	{
+	CFsMessageRequest& msgRequest = *(CFsMessageRequest*) aRequest;
+
+	__ASSERT_DEBUG(msgRequest.CurrentOperationPtr() != NULL, Fault(ECacheBadOperationIndex));
+
+	CFileShare* share;
+	CFileCB* file;
+	GetFileFromScratch(aRequest, share, file);
+	CFileCache* fileCache = file->FileCache();
+
+	__ASSERT_DEBUG(fileCache != NULL, Fault(ENoFileCache));
+#ifdef _DEBUG
+	TInt cancelling = (msgRequest.LastError() == KErrCancel)?(TBool)ETrue:(TBool)EFalse;
+#endif
+
+	TUint mode = share?share->iMode : EFileReadBuffered;
+	TInt r = fileCache->ReadBuffered(msgRequest, mode);
+
+	// if this request has been cancelled we mustn't dispatch it again - 
+	// we still need to call state machine however so that any locked segments can be unlocked
+
+
+	TInt lastError = msgRequest.LastError();
+
+#ifdef _DEBUG
+	__ASSERT_DEBUG(!cancelling || msgRequest.LastError() == KErrCancel, Fault(ERequestUncancelled));
+#endif
+
+	if (lastError == KErrCancel)
+		r = CFsRequest::EReqActionComplete;
+
+	return r;
+	}
+
+/**
+ReadBuffered - attempts to read from cache. 
+Called from TFsFileRead::Initialise() and CFileCache::CompleteRead()
+
+@return CFsRequest::EReqActionComplete if request entirely satisfied
+		CFsRequest::EReqActionContinue if request not yet complete. 
+			Results in transition from :
+				TFsFileRead::PostInitialise() to TFsFileRead::DoRequestL() or
+				CFileCache::CompleteRead() to TFsFileRead::DoRequestL()
+		CFsRequest::EReqActionBusy if filecache is "busy"
+*/
+TInt CFileCache::ReadBuffered(CFsMessageRequest& aMsgRequest, TUint aMode)
+	{
+	iLock.Wait();
+
+	CFsClientMessageRequest* newRequest = NULL;
+
+	__CACHE_PRINT2(_L("CFileCache::ReadBuffered()  pos %d, len %d"), I64LOW(aMsgRequest.CurrentOperation().iReadWriteArgs.iPos), aMsgRequest.CurrentOperation().iReadWriteArgs.iTotalLength);
+	TInt r = DoReadBuffered(aMsgRequest, aMode, newRequest);
+	
+	iLock.Signal();
+
+	if (newRequest)
+		newRequest->Dispatch();
+
+	return r;
+	}
+
+
+void CFileCache::UpdateSharePosition(CFsMessageRequest& aMsgRequest, TMsgOperation& aCurrentOperation)
+	{
+	// update the file share's position if this request came from client
+	if (aCurrentOperation.iClientRequest)
+		{
+		CFileShare* share = (CFileShare*)aMsgRequest.ScratchValue();
+		if (aMsgRequest.LastError() == KErrNone)
+			{
+			__e32_atomic_store_ord64(&share->iPos, aCurrentOperation.iReadWriteArgs.iPos);
+			}
+		}
+	}
+
+TInt CFileCache::DoReadBuffered(CFsMessageRequest& aMsgRequest, TUint aMode, CFsClientMessageRequest*& aNewRequest)
+	{
+	enum states
+		{
+		EStBegin=0,
+		EStReadFromCache,
+		EStReadFromDiskComplete,
+		EStCopyToClient,
+		EStReStart,
+		EStEnd
+		};
+
+
+	TInt retCode = CFsRequest::EReqActionComplete;
+	TInt& lastError = aMsgRequest.LastError();
+	TBool driveThread = IsDriveThread();
+
+	// temp storage for transition between EStReadFromCache / EStReadFromDiskComplete and EStCopyToClient
+	TInt readLen = 0;
+	TUint8* addr = NULL;
+	TInt64 segmentStartPos = 0;
+
+	TInt segmentSize = SegmentSize();
+	TInt segmentSizeLog2 = SegmentSizeLog2();
+	TInt64 segmentSizeMask = SegmentSizeMask();
+
+	for(;;)
+		{
+		TMsgOperation* currentOperation = &aMsgRequest.CurrentOperation();
+		TInt64& currentPos = currentOperation->iReadWriteArgs.iPos;
+		TInt& totalLen = currentOperation->iReadWriteArgs.iTotalLength;
+		TInt& currentOffset = currentOperation->iReadWriteArgs.iOffset;
+		TBool readAhead = (currentOperation->iReadWriteArgs.iData == NULL)?(TBool)ETrue:(TBool)EFalse;
+
+		switch(currentOperation->iState)
+			{
+			case EStBegin:
+
+
+				// if EFileReadDirectIO, flush write cache and read direct from  disk
+				if (!(aMode & EFileReadBuffered))
+					{
+					iLock.Signal();
+					TInt r = FlushDirty(&aMsgRequest);
+					iLock.Wait();
+					if (r == CFsRequest::EReqActionBusy)
+						return CFsRequest::EReqActionBusy;
+					return CFsRequest::EReqActionContinue;	// read uncached
+					}
+
+				if (currentPos > iSize64)
+					currentPos = iSize64;
+
+				currentOperation->iState = EStReadFromCache;
+
+				// count the number of sequential reads for read-ahead
+				if (currentOperation->iClientRequest)
+					{
+					if (currentPos == iLastReadPos)
+						{
+						iSequentialReads++;
+						}
+					else
+						{
+						iSequentialReads = 0;
+						ResetReadAhead();
+						}
+					iLastReadPos = currentPos + totalLen;
+					iLastReadLen = totalLen;
+					}
+
+				
+				// fall into...
+
+			case EStReadFromCache:
+				{
+				// reading past end of file ?
+				if (currentPos + totalLen > iSize64)
+					totalLen = (TInt) (iSize64 - currentPos);
+				
+
+				if (totalLen == 0 || lastError != KErrNone)
+					{
+					currentOperation->iState = EStEnd;
+					break;
+					}
+
+				segmentStartPos = currentPos & segmentSizeMask;
+
+			
+				TInt64 endPos = currentPos + totalLen;
+				TUint maxLenToRead = (TUint)Min((TInt64)(iCacheClient->CacheLineSize()), (endPos - segmentStartPos));
+				TInt maxSegments = (maxLenToRead + segmentSize - 1) >> segmentSizeLog2;
+
+
+				TInt segmentCount = maxSegments;
+				TInt filledSegmentCount;
+				TInt lockError;
+				addr = iCacheClient->FindAndLockSegments(segmentStartPos, segmentCount, filledSegmentCount, lockError, EFalse);
+
+
+#if defined (_DEBUG_READ_AHEAD)
+				if (addr && readAhead)
+					RDebug::Print(_L("READAHEAD CACHELINE ALREADY EXISTS POS %d"), I64LOW(segmentStartPos));
+#endif
+
+				// if cacheline contains filled and empty segments, deal with these seperately
+				// to simplify the code.....
+				if (filledSegmentCount > 0 && segmentCount > filledSegmentCount)
+					{
+					segmentCount = Min(segmentCount, filledSegmentCount);
+					}
+
+				if (lockError == KErrInUse)
+					{
+					// cacheline in use (by other thread):
+					// if this is a read-ahead, abandon it
+					// otherwise re-post the request
+					if (readAhead)
+						{
+						totalLen = 0;
+						retCode = CFsRequest::EReqActionComplete;
+						currentOperation->iState = EStEnd;
+						}
+					else
+						{
+#if defined (_DEBUG_READ_AHEAD)
+						RDebug::Print(_L("READC CACHELINE BUSY POS %d"), I64LOW(segmentStartPos));
+#endif
+
+						return CFsRequest::EReqActionBusy;
+						}
+					break;
+					}
+
+				// if not found, try to allocate as many contiguous segments 
+				// as possible so that the number of reads issued to the media 
+				// driver is kept to a minumum
+
+				if (addr == NULL)	// not found ?
+					{
+					// if read len > size of the cache, don't read excess 
+					// through the cache as this is wasteful
+					if (totalLen > iCacheSize)
+						{
+						TInt len = totalLen - iCacheSize;
+						TInt r = aMsgRequest.PushOperation(
+							currentPos, len, 
+							(TDesC8*) currentOperation->iReadWriteArgs.iData, currentOffset,
+							CompleteRead, 
+							EStReadFromCache);
+						if (r != KErrNone)
+							{
+							currentOperation->iState = EStReStart;
+							break;
+							}
+
+						currentOffset+= len;
+						currentPos+= len;
+						totalLen-= len;
+						return CFsRequest::EReqActionContinue;
+						}
+
+					// if position not cached postpone Initialise() to drive thread
+					// as there may be a read ahead already in the queue
+
+					if (!driveThread && !readAhead)
+						{
+#if defined (_DEBUG_READ_AHEAD)
+						RDebug::Print(_L("*** POSTING READ TO DRIVE THREAD POS %d ***"), I64LOW(segmentStartPos));
+#endif
+						return CFsRequest::EReqActionBusy;
+						}
+
+					segmentCount = maxSegments;
+					addr = iCacheClient->AllocateAndLockSegments(segmentStartPos, segmentCount, EFalse, !readAhead);
+					if (addr == NULL)
+						{
+						__CACHE_PRINT(_L("AllocateSegment failed"));
+						currentOperation->iState = EStReStart;
+						break;
+						}
+					}
+
+				
+				readLen = segmentCount << segmentSizeLog2;
+				__ASSERT_DEBUG(iSize64 > segmentStartPos, Fault(EPosBeyondSize));
+				readLen = (TInt)Min((TInt64)readLen, (iSize64 - segmentStartPos));
+
+				if (iCacheClient->SegmentEmpty(segmentStartPos))
+					{
+					// store readLen & addr in scratch area
+					currentOperation->iScratchValue0 = addr;						
+					currentOperation->iScratchValue1 = (TAny*) readLen;						
+
+					// read into cache segment(s)
+					__CACHE_PRINT2(_L("CACHEFILE: read pos %ld, readLen %d"), segmentStartPos, readLen);
+					TInt r = aMsgRequest.PushOperation(
+						segmentStartPos, readLen, addr, 0,
+						CompleteRead, 
+						EStReadFromDiskComplete);
+					if (r != KErrNone)
+						{
+						iCacheClient->UnlockSegments(segmentStartPos);
+						currentOperation->iState = EStReStart;
+						break;
+						}
+
+					return CFsRequest::EReqActionContinue;
+					}
+				else
+					{
+					currentOperation->iState = EStCopyToClient;
+					}
+				}
+				// fall into ...
+			
+			case EStCopyToClient:
+				{
+				TInt segmentsLocked = (readLen + segmentSize - 1) >> segmentSizeLog2;
+
+				if (lastError == KErrNone)
+					{
+					// copy to user buffer
+					TInt offset = iCacheClient->CacheOffset(currentPos);	// offset into segment
+					TInt len = Min(readLen - offset, totalLen);
+					
+					// if addr is NULL then this is a read ahead request
+					if (currentOperation->iReadWriteArgs.iData != NULL)
+						{
+						if (currentOperation->iClientRequest)
+							{
+							__ASSERT_DEBUG (aMsgRequest.Message().Handle() != NULL, Fault(EMsgAlreadyCompleted));
+							TPtrC8 ptr(addr+offset, len);
+							lastError = aMsgRequest.Write(0, ptr, currentOffset);
+							}
+						else
+							{
+							memcpy(((TUint8*) currentOperation->iReadWriteArgs.iData) + currentOffset, addr+offset, len);
+							}
+						}
+					else
+						{
+						iReadAheadRequest = NULL;
+						}
+
+					currentOffset+= len;
+					currentPos+= len;
+					totalLen-= len;
+					}
+
+				if (lastError == KErrNone)
+					iCacheClient->MarkSegmentsAsFilled(segmentStartPos, segmentsLocked);
+				iCacheClient->UnlockSegments(segmentStartPos);
+
+				if (lastError != KErrNone)
+					{
+					retCode = CFsRequest::EReqActionComplete;
+					currentOperation->iState = EStEnd;
+					break;
+					}
+
+				if (totalLen > 0 && lastError == KErrNone)
+					{
+					currentOperation->iState = EStReadFromCache;
+					break;
+					}
+				currentOperation->iState = EStEnd;
+				}
+			// fall into ...
+			
+
+			case EStEnd:
+				// update the file share's position if this request came from client
+				UpdateSharePosition(aMsgRequest, *currentOperation);
+				return retCode;
+
+			case EStReadFromDiskComplete:
+				{
+				// restore readLen etc from scratch area
+				segmentStartPos = currentPos & segmentSizeMask;
+				addr = (TUint8*) currentOperation->iScratchValue0;
+				readLen = (TInt) currentOperation->iScratchValue1;
+				
+				aMsgRequest.CurrentOperation().iState = EStCopyToClient;
+				}
+				break;
+
+			case EStReStart:
+
+				// ignore the failure if this is a read-ahead
+				if (readAhead)
+					{
+					totalLen = 0;
+					retCode = CFsRequest::EReqActionComplete;
+					currentOperation->iState = EStEnd;
+					break;
+					}
+
+				// We need to flush all dirty data to disk in case this read overlaps
+				// with any dirty data already in the file cache
+				if (aMode & EFileWriteBuffered)
+					{
+					TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, ETrue);
+					if (r == CFsRequest::EReqActionBusy || r != CFsRequest::EReqActionComplete)
+						return r;
+					}
+
+				retCode = CFsRequest::EReqActionContinue;	// read uncached
+				currentOperation->iState = EStEnd;
+
+				/*
+				We're now going to by-pass the file cache.
+				If we've already written something to the client's buffer then, in the flexible
+				memory model, the KDesWrittenShift bit will be set and so the descriptor length will 
+				updated in RMessageK::CallbackFunc() when the client thread runs. This (shorter) 
+				length will overwrite the descriptor length written by the local media subsystem.
+				To get round this problem, we set the descriptor length artificially by writing a 
+				zero-length descriptor at the end of the client's buffer.
+				*/
+				if (currentOffset > 0 && currentOperation->iClientRequest)
+					{
+					__ASSERT_DEBUG (aMsgRequest.Message().Handle() != NULL, Fault(EMsgAlreadyCompleted));
+					TPtrC8 ptr(NULL, 0);
+					TInt r = aMsgRequest.Write(0, ptr, currentOffset+totalLen);
+					__CACHE_PRINT3(_L("CFileCache::DoReadBuffered() failed at pos %lx offset %x totalLen %x\n"), currentPos, currentOffset, totalLen);
+					__CACHE_PRINT2(_L("CFileCache::DoReadBuffered() writing zero bytes at offset %x r %d\n"), currentOffset + totalLen, r);
+					if (r != KErrNone)
+						retCode = r;
+					}
+
+				aMsgRequest.ReStart();
+				UpdateSharePosition(aMsgRequest, *currentOperation);
+				return retCode;
+
+
+			};
+		}	// for (;;)
+
+	}
+
+
+
+/**
+WriteBuffered - attempts to write to cache. 
+Called from TFsFileRead::Initialise and TFsFileRead::DoRequestL
+
+@return CFsRequest::EReqActionComplete if request entirely satisfied
+		CFsRequest::EReqActionContinue if request not yet complete
+		CFsRequest::EReqActionBusy if filecache is busy
+*/
+TInt CFileCache::WriteBuffered(CFsMessageRequest& aMsgRequest, TUint aMode)
+	{
+	iLock.Wait();
+
+
+	CFsClientMessageRequest* newRequest = NULL;
+
+	__CACHE_PRINT2(_L("CFileCache::WriteBuffered()  pos %d, len %d"), I64LOW(aMsgRequest.CurrentOperation().iReadWriteArgs.iPos), aMsgRequest.CurrentOperation().iReadWriteArgs.iTotalLength);
+	TInt r = DoWriteBuffered(aMsgRequest, newRequest, aMode);
+	
+	iLock.Signal();
+
+	if (newRequest)
+		newRequest->Dispatch();
+
+	// completion ?
+	if (r == CFsRequest::EReqActionComplete)
+		{
+		TMsgOperation& currentOperation = aMsgRequest.CurrentOperation();
+		
+		__ASSERT_DEBUG(aMsgRequest.CurrentOperationPtr() != NULL, Fault(ECacheBadOperationIndex));
+
+		TFsFileWrite::CommonEnd(&aMsgRequest, r, iInitialSize, iSize64, currentOperation.iReadWriteArgs.iPos, EFalse);
+		}
+	
+	return r;
+	}
+
+
+
+
+TInt CFileCache::CompleteWrite(CFsRequest* aRequest)
+	{
+	CFsMessageRequest& msgRequest = *(CFsMessageRequest*) aRequest;
+
+	CFileShare* share = (CFileShare*)aRequest->ScratchValue();
+	CFileCB& file = share->File();
+	CFileCache* fileCache = file.FileCache(); //&file;
+	__ASSERT_DEBUG(fileCache != NULL, Fault(ENoFileCache));
+
+#ifdef _DEBUG
+	TInt cancelling = (msgRequest.LastError() == KErrCancel)?(TBool)ETrue:(TBool)EFalse;
+#endif
+	
+
+	TInt r = fileCache->WriteBuffered(msgRequest, share->iMode);
+
+#ifdef _DEBUG
+	__ASSERT_DEBUG(!cancelling || msgRequest.LastError() == KErrCancel, Fault(ERequestUncancelled));
+#endif
+
+	// if this request has been cancelled we mustn't dispatch it again - 
+	// we still need to call state machine however so that any locked segments can be unlocked
+	TInt lastError = msgRequest.LastError();
+	if (lastError == KErrCancel)
+		r = CFsRequest::EReqActionComplete;
+
+	return r;
+	}
+
+
+
+TInt CFileCache::DoWriteBuffered(CFsMessageRequest& aMsgRequest, CFsClientMessageRequest*& aNewRequest, TUint aMode)
+
+	{
+	enum states
+		{
+		EStBegin=0,
+		EStWriteToCache,
+		EStReadFromDisk,
+		EStReadFromDiskComplete,
+		EStWriteToCacheComplete,
+		EStCopyFromClient,
+		EStReStart,
+		EStEnd,
+		EStWriteThrough
+		};
+
+	enum flags
+		{
+		EReadFirstSegment	= 0x01,
+		EReadLastSegment	= 0x02
+		};
+
+	TBool cachingWrites = (aMode & EFileWriteBuffered)?(TBool)ETrue:(TBool)EFalse;
+	TInt& lastError = aMsgRequest.LastError();
+	TBool driveThread = IsDriveThread();
+	TInt segmentSize = SegmentSize();
+	TInt segmentSizeLog2 = SegmentSizeLog2();
+	TInt64 segmentSizeMask = SegmentSizeMask();
+
+	// temp storage for transition between EStWriteToCache / EStWriteToCacheComplete and EStCopyFromClient
+	TInt segmentCount = 0;
+	TUint8* addr = NULL;
+	TInt64 firstSegmentStartPos = 0;
+	TInt64 readPos = 0;
+	TUint8* readAddr = NULL;
+	TInt readSegmentCount = 0;
+
+	TInt readFlags = 0;
+	
+	for(;;)
+		{
+		TMsgOperation* currentOperation = &aMsgRequest.CurrentOperation();
+		TInt retCode = CFsRequest::EReqActionComplete;
+
+		TInt64& currentPos = currentOperation->iReadWriteArgs.iPos;
+		TInt& totalLen = currentOperation->iReadWriteArgs.iTotalLength;
+		TInt& currentOffset = currentOperation->iReadWriteArgs.iOffset;
+		switch(currentOperation->iState)
+			{
+			case EStBegin:
+				{				
+				// Report background flush errors back to client
+				CFileShare* share = (CFileShare*) aMsgRequest.ScratchValue(); 
+				if (share->iFlushError != KErrNone)
+					{
+					TInt r = share->iFlushError;
+					share->iFlushError = KErrNone;
+					return r;
+					}
+
+				if (currentPos > iSize64)
+					currentPos = iSize64;
+				iInitialSize = iSize64;
+
+				// if EFileWriteDirectIO OR 
+				// (caching writes and requested write len > size of the cache), 
+				// flush the write cache
+				if ((aMode & EFileWriteBuffered) == 0 || 
+					(cachingWrites && totalLen > iCacheSize))
+					{
+					TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, ETrue);
+					if (r == CFsRequest::EReqActionBusy || r != CFsRequest::EReqActionComplete)
+						return r;
+					}
+				// if EFileWriteDirectIO then overwrite any non-dirty data and 
+				// write direct to disk....
+
+				// if caching writes and requested write len > size of the cache, 
+				// don't write excess  through the cache as this is wasteful
+				if (cachingWrites && totalLen > iCacheSize)
+					{
+					// Destroy ALL cached data (dirty and non-dirty) to ensure 
+					// cache is consistent with data written to disk
+					iCacheClient->Purge(ETrue);
+
+					TInt len = totalLen - iCacheSize;
+					TInt r = aMsgRequest.PushOperation(
+						currentPos, len, 
+						(TDesC8*) currentOperation->iReadWriteArgs.iData, currentOffset,
+						CompleteWrite, 
+						EStWriteToCache);
+					if (r != KErrNone)
+						{
+						currentOperation->iState = EStReStart;
+						break;
+						}
+
+					currentOffset+= len;
+					currentPos+= len;
+					totalLen-= len;
+					return CFsRequest::EReqActionContinue;
+					}
+
+				currentOperation->iState = EStWriteToCache;
+				}		
+				break;
+
+			case EStWriteToCache:
+				__ASSERT_DEBUG(aMsgRequest.CurrentOperationPtr() != NULL, Fault(ECacheBadOperationIndex));
+				{
+				// NB we must carry on if we get an error to ensure cache is consistent with disk
+				if (totalLen == 0 || lastError == KErrCancel)
+					{
+					currentOperation->iState = EStWriteToCacheComplete;
+					break;
+					}
+
+				firstSegmentStartPos = currentPos & segmentSizeMask;
+				TInt64 endPos = currentPos + totalLen;
+
+				// Find the maximum number of contiguous segments we need to lock
+				// in this cacheline - when we unlock these will be marked as filled
+				const TInt64 dataRange = Min((TInt64)iCacheSize, (endPos + segmentSize - 1 - firstSegmentStartPos));
+				TInt maxSegmentCount =  (TInt)(dataRange >> segmentSizeLog2);
+				
+				segmentCount = maxSegmentCount;
+
+				TInt lockError;
+				TInt filledSegmentCount;
+				addr = iCacheClient->FindAndLockSegments(firstSegmentStartPos, segmentCount, filledSegmentCount, lockError, cachingWrites);
+				
+				if (lockError == KErrInUse)
+					return CFsRequest::EReqActionBusy;
+
+#ifdef LAZY_WRITE
+				if (cachingWrites && addr == NULL && lastError == KErrNone && iCacheClient->TooManyLockedSegments())
+					{
+					// Flush a single dirty cacheline (if there is one for this file)
+					// or all dirty data  on this drive (if there is any).
+					// If there's nothing to flush then the dirty (locked) data
+					// must belong to another drive, so there's not much we can do
+					// but write through 
+					TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, EFalse);
+					if (r == CFsRequest::EReqActionBusy)
+						return CFsRequest::EReqActionBusy;
+					if (r != KErrInUse)
+						{
+						// Need to switch to drive thread to flush all data on this drive
+						if (!driveThread)
+							return CFsRequest::EReqActionBusy;
+						iLock.Signal();
+						TInt r = iDrive->FlushCachedFileInfo();
+						iLock.Wait();
+						if (r == CFsRequest::EReqActionBusy)
+							return CFsRequest::EReqActionBusy;
+
+						lastError = KErrNoMemory;	// write through
+						}
+					}
+#else
+				// flush cache before allocating a new cacheline
+				if (cachingWrites && addr == NULL && lastError == KErrNone)
+					{
+					// if no segment available, flush a single dirty cacheline 
+					// or wait if already flushing
+					if (iFlushBusy)
+						return CFsRequest::EReqActionBusy;
+					TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, EFalse);
+					if (r != CFsRequest::EReqActionBusy && r != CFsRequest::EReqActionComplete)
+						lastError = r;
+					}
+#endif
+				// if no cacheline found & write caching is enabled, allocate a new cacheline
+				if (cachingWrites && addr == NULL && lastError == KErrNone)
+					{
+					// try to allocate up to write cache size - this may not 
+					// be possible if a segment in the range is already cached
+					segmentCount = maxSegmentCount;
+					addr = iCacheClient->AllocateAndLockSegments(currentPos, segmentCount, cachingWrites, ETrue);
+					
+					// continue if alloc failed
+					if (addr == NULL)
+						lastError = KErrNoMemory;
+				
+					}
+
+				if (addr == NULL)
+					{
+					if (cachingWrites && lastError == KErrNone)
+						lastError = KErrNoMemory;
+					segmentCount = 1;
+					currentOperation->iState = EStCopyFromClient;
+					break;
+					}
+
+				// if the first or last segment are empty then we'll have to read-before-write
+				TInt64 lastSegmentStartPos = firstSegmentStartPos + ((segmentCount-1) << segmentSizeLog2);
+				
+				TInt startPosSegOffset = iCacheClient->CacheOffset(currentPos);
+				TInt endPosSegOffset = iCacheClient->CacheOffset(endPos);
+
+				// partial first segment ?
+				if (startPosSegOffset != 0 && 
+					firstSegmentStartPos < iFileCB->Size64() &&
+					iCacheClient->SegmentEmpty(firstSegmentStartPos))
+					{
+					readFlags|= EReadFirstSegment;
+					}
+		
+				// partial last segment ? 
+				// NB this may be the first segment too !
+				if (endPosSegOffset != 0 && 
+					endPos < lastSegmentStartPos + segmentSize &&
+					endPos < iFileCB->Size64() &&
+					iCacheClient->SegmentEmpty(lastSegmentStartPos))
+					{
+					readFlags|= EReadLastSegment;
+					}
+
+				// read-before-write required ?
+				if (readFlags & EReadFirstSegment)
+					{
+					readFlags&= ~EReadFirstSegment;
+					readPos = firstSegmentStartPos;
+					readAddr = addr;
+					readSegmentCount = 1;
+					// if the last segment is empty and it's the same as the first or contiguous,
+					// then read that too 
+					if ((readFlags & EReadLastSegment) && (segmentCount <= 2))
+						{
+						readSegmentCount = segmentCount;
+						readFlags&= ~EReadLastSegment;
+						}
+					currentOperation->iState = EStReadFromDisk;
+					}
+				else if (readFlags & EReadLastSegment)
+					{
+					readFlags&= ~EReadLastSegment;
+					readPos = lastSegmentStartPos;
+					readAddr = addr + ((segmentCount-1) << segmentSizeLog2);
+					readSegmentCount = 1;
+					currentOperation->iState = EStReadFromDisk;
+					}
+				else
+					{
+					currentOperation->iState = EStCopyFromClient;
+					}
+				}
+				break;
+
+			case EStReadFromDisk:
+				{
+				// Save address & segmentCount in scratch area
+				currentOperation->iScratchValue0 = addr;	
+				__ASSERT_DEBUG(segmentCount < 0xFFFF, Fault(EBadSegmentCount));
+				currentOperation->iScratchValue1 = (TAny*) ((readFlags << 16) | segmentCount);
+
+				TInt maxReadLen = readSegmentCount << segmentSizeLog2;
+#ifdef __VC32__
+#pragma warning( disable : 4244 )  
+//Disable Compiler Warning (levels 3 and 4) C4244: 'conversion' conversion from 'type1' to 'type2', possible loss of data
+// maxReadLen is of 31 bits. Hence Min result is reduced to 31 bits
+#endif
+				TInt readLen = (TInt)Min((TInt64)maxReadLen, (iSize64 - readPos));
+#ifdef __VC32__
+#pragma warning( default : 4244 )
+#endif
+				__ASSERT_DEBUG (aMsgRequest.Message().Handle() != NULL, Fault(EMsgAlreadyCompleted));					
+				TInt r = aMsgRequest.PushOperation(
+					readPos, readLen, readAddr, 0, 
+					CompleteWrite,
+					EStReadFromDiskComplete,
+					EFsFileRead);
+				if (r != KErrNone)
+					{
+					lastError = KErrNoMemory;
+					currentOperation->iState = EStEnd;
+					iCacheClient->UnlockSegments(firstSegmentStartPos);
+					break;
+					}
+
+				// requeue this request
+				return CFsRequest::EReqActionContinue;
+				}
+
+			case EStReadFromDiskComplete:
+				{
+				__ASSERT_DEBUG(aMsgRequest.CurrentOperationPtr() != NULL, Fault(ECacheBadOperationIndex));
+				// restore addr & segmentCount etc from scratch area
+				firstSegmentStartPos = currentPos & segmentSizeMask;
+				addr = (TUint8*) currentOperation->iScratchValue0;
+				segmentCount = ((TInt) currentOperation->iScratchValue1) & 0xFFFF;
+				readFlags = ((TInt) currentOperation->iScratchValue1) >> 16;
+
+				if (readFlags & EReadLastSegment)
+					{
+					TInt64 lastSegmentStartPos = firstSegmentStartPos + ((segmentCount-1) << segmentSizeLog2);
+					readFlags&= ~EReadLastSegment;
+					readPos = lastSegmentStartPos;
+					readAddr = addr + ((segmentCount-1) << segmentSizeLog2);
+					readSegmentCount = 1;
+					currentOperation->iState = EStReadFromDisk;
+					break;
+					}
+				
+				aMsgRequest.CurrentOperation().iState = EStCopyFromClient;
+				}
+				break;
+
+			case EStCopyFromClient:
+				{
+				TInt writeLen = segmentCount << segmentSizeLog2;
+				TInt offset = iCacheClient->CacheOffset(currentPos);	// offset into segment
+				writeLen = Min(writeLen - offset, totalLen);
+
+				if (addr != NULL)
+					{
+					__ASSERT_DEBUG (aMsgRequest.Message().Handle() != NULL, Fault(EMsgAlreadyCompleted));
+					// copy from user buffer to cache buffer
+					TInt writeError = KErrNone;
+					if (currentOperation->iClientRequest)
+						{
+						TPtr8 ptr(addr+offset, writeLen, writeLen);
+						writeError = aMsgRequest.Read(0, ptr, currentOffset);
+						}
+					else
+						{
+						memcpy(addr+offset, ((TUint8*) currentOperation->iReadWriteArgs.iData) + currentOffset, writeLen);
+						}
+
+					// "writing" past end of file ?
+					if (currentPos + writeLen > iSize64 && cachingWrites && lastError == KErrNone && writeError == KErrNone)
+						{
+						__CACHE_PRINT2(_L("CACHEFILE: extending size old %ld new %ld"), iSize64, currentPos + totalLen);
+						iSize64 = currentPos + writeLen;
+						}
+
+					TInt anyError = (writeError != KErrNone)?writeError:lastError;
+
+					if (anyError == KErrNone)
+						iCacheClient->MarkSegmentsAsFilled(firstSegmentStartPos, segmentCount);
+
+					if (cachingWrites && anyError == KErrNone)
+						{
+						iCacheClient->MarkSegmentsAsDirty(firstSegmentStartPos, segmentCount);
+						// start dirty data timer
+						FileDirty(aMsgRequest);
+						}
+
+					// unlock if we're not buffering writes (segments won't be unlocked if dirty)
+					iCacheClient->UnlockSegments(firstSegmentStartPos);
+					}
+
+				currentOffset+= writeLen;
+				currentPos+= writeLen;
+				totalLen-= writeLen;
+
+				currentOperation->iState = EStWriteToCache;
+				break;
+				}
+
+			case EStWriteToCacheComplete:
+				{
+				__ASSERT_DEBUG(aMsgRequest.CurrentOperationPtr() != NULL, Fault(ECacheBadOperationIndex));
+
+				if (lastError == KErrCancel)
+					{
+					currentOperation->iState = EStEnd;
+					}
+				else if ((!cachingWrites) || (lastError != KErrNone))
+					{
+					// allow TFsFileWrite::DoRequestL() to proceed normally using original pos & len
+					__ASSERT_DEBUG(aMsgRequest.CurrentOperationPtr() != NULL, Fault(ECacheBadOperationIndex));
+	
+					currentOperation->iState = EStWriteThrough;
+					}
+				else
+					{
+					__CACHE_PRINT2(_L("CACHEFILE: write buffered pos %ld, len %d"), 
+						aMsgRequest.CurrentOperation().iReadWriteArgs.iPos, aMsgRequest.CurrentOperation().iReadWriteArgs.iOffset);
+					__ASSERT_DEBUG(totalLen == 0, Fault(ECompletingWriteWithDataRemaining));
+
+					currentOperation->iState = EStEnd;
+					}
+				break;
+				}
+
+			case EStWriteThrough:
+
+				if (lastError == KErrCancel)
+					{
+					currentOperation->iState = EStEnd;
+					break;
+					}
+
+				// we're going to issue an uncached write so clear any error
+				lastError = KErrNone;
+
+				if (cachingWrites)
+					{
+					TInt r = DoFlushDirty(aNewRequest, &aMsgRequest, ETrue);
+#ifdef _DEBUG
+					if (r == CFsRequest::EReqActionBusy)
+						CCacheManagerFactory::CacheManager()->Stats().iWriteThroughWithDirtyDataCount++;
+#endif
+					// Need to reset currentOperation.iReadWriteArgs.iTotalLength here to make sure 
+					// TFsFileWrite::PostInitialise() doesn't think there's no data left to process
+					aMsgRequest.ReStart();
+					if (r == CFsRequest::EReqActionBusy || r != CFsRequest::EReqActionComplete)
+						return r;
+					}
+
+				retCode = CFsRequest::EReqActionContinue;
+
+				// fall into...EStRestart
+				currentOperation->iState = EStReStart;
+
+			case EStReStart:
+
+				__ASSERT_DEBUG(retCode == CFsRequest::EReqActionBusy || retCode == CFsRequest::EReqActionContinue, Fault(EBadRetCode));
+
+				aMsgRequest.ReStart();
+				UpdateSharePosition(aMsgRequest, *currentOperation);
+				if (currentOperation->iClientRequest)
+					currentOperation->iReadWriteArgs.iPos = currentOperation->iClientPosition; // NB maybe KCurrentPosition64
+				return retCode;
+
+			case EStEnd:
+				return retCode;
+			}
+		}
+	}
+
+
+
+TInt CFileCache::AllocateRequest(CFsClientMessageRequest*& aNewRequest, TBool aWrite, CSessionFs* aSession)
+	{
+
+	RLocalMessage msgNew;
+	const TOperation& oP = OperationArray[aWrite?EFsFileWriteDirty:EFsFileRead];
+	TInt r = RequestAllocator::GetMessageRequest(oP, msgNew, aNewRequest);
+	if (r != KErrNone)
+		return r;
+
+	aNewRequest->Set(msgNew, oP, aSession);
+	aNewRequest->SetDrive(iDrive);
+	
+	// read-aheads and write-dirty requests should not be posted to plugins
+	// If there are data-modifying plugins, then these should sit above the file cache
+	aNewRequest->iCurrentPlugin = NULL;
+	aNewRequest->EnablePostIntercept(EFalse);
+
+	// Scratch value is a CFileCB pointer NOT a CFileShare for requests
+	// allocated by the file cache
+	aNewRequest->SetScratchValue64( MAKE_TINT64(EFalse, (TUint) iFileCB) );
+
+	// don't call Initialise(), don't call PostInitialise()
+	aNewRequest->SetState(CFsRequest::EReqStateDoRequest);
+	
+	// don't call Message().Complete()
+	aNewRequest->SetCompleted(EFalse);		
+
+	__ASSERT_DEBUG(aNewRequest->CurrentOperationPtr() == NULL, Fault(EBadOperationIndex));
+
+	return KErrNone;
+	}
+
+TInt CFileCache::FlushDirty(CFsRequest* aOldRequest)
+	{
+	iLock.Wait();
+
+	CFsClientMessageRequest* newRequest = NULL;
+
+	TInt r = DoFlushDirty(newRequest, aOldRequest, ETrue);
+
+	iLock.Signal();
+
+	if (newRequest)
+	    {
+		//To be used in notification framework.
+	    //newRequest->iUID = aOldRequest->Message().Identity();
+		newRequest->Dispatch();
+	    }
+
+	return r;
+	}
+
+
+void CFileCache::Purge(TBool aPurgeDirty)
+	{
+	iLock.Wait();
+
+	iCacheClient->Purge(aPurgeDirty);
+
+	iLock.Signal();
+	}
+
+void CFileCache::PropagateFlushErrorToAllFileShares()
+	{
+	FileShares->Lock();
+	TInt count = FileShares->Count();
+	while(count--)
+		{
+		CFileShare* share = (CFileShare*)(*FileShares)[count];
+		if (&share->File() == iFileCB)
+			{
+			share->iFlushError = iFlushError;
+			}
+		}
+	FileShares->Unlock();
+	}
+
+/**
+CFileCache::DoFlushDirty()
+
+@param aFlushAll. If EFalse then only a single cacheline will be flushed
+
+returns	CFsRequest::EReqActionBusy if a flush is in progress OR cacheline already in use
+		CFsRequest::EReqActionComplete if nothing to flush / flush completed 
+		KErrNoMemory if failed to allocate a request
+		or one of the system wide error codes
+
+ */
+TInt CFileCache::DoFlushDirty(CFsClientMessageRequest*& aNewRequest, CFsRequest* aOldRequest, TBool aFlushAll)
+	{
+	TInt64  pos;
+	TUint8* addr;
+
+	if (iFlushBusy)
+		return CFsRequest::EReqActionBusy;
+
+	TInt segmentCount = iCacheClient->FindFirstDirtySegment(pos, addr);
+
+	if (segmentCount == 0)
+		{
+		TInt flushError = iFlushError;
+		iFlushError = KErrNone;
+
+		// If this flush didn't originate from a client request return last error
+		if (!aOldRequest || !aOldRequest->Session())
+			return (flushError == KErrNone)? CFsRequest::EReqActionComplete : flushError;
+
+		// Return the last error from CFileShare::iFlushError 
+		// and then clear CFileShare::iFlushError 
+
+		CFileShare* share = (CFileShare*) SessionObjectFromHandle(
+			aOldRequest->Message().Int3(),
+			FileShares->UniqueID(),
+			aOldRequest->Session());
+
+		TInt r = KErrNone;
+		if (share)
+			{
+			r = share->iFlushError;
+			share->iFlushError = KErrNone;
+			}
+		if (r == KErrNone)
+			return CFsRequest::EReqActionComplete;
+
+		return r;
+		}
+
+
+	// NB aOldRequest->Session may be NULL - e.g for FileShareCloseOp
+	CSessionFs* session = aOldRequest && aOldRequest->Session() ? aOldRequest->Session() : iDirtyDataOwner;
+
+	__ASSERT_ALWAYS(session, Fault(EFlushingWithSessionNull));
+
+	TInt r = AllocateRequest(aNewRequest, ETrue, session);
+	if (r != KErrNone)
+		return r;
+	
+	r = aNewRequest->PushOperation(0, 0, (TUint8*) NULL, 0);
+
+	if (r != KErrNone)
+		{
+		aNewRequest->Free();
+		aNewRequest = NULL;
+		return r;
+		}
+
+	// set the number of segments to flush - either all dirty data or the equivalent of one full cacheline
+	aNewRequest->CurrentOperation().iScratchValue0 = 
+		(TAny*) (aFlushAll?KMaxTInt:iCacheClient->CacheLineSizeInSegments());
+
+
+	// issue flush request
+	r = FlushDirtySm(*aNewRequest);
+
+	// We should only get three possible return codes from FlushDirtySm() :
+	// CFsRequest::EReqActionContinue	- a write request (aNewRequest) needs to be dispatched
+	// CFsRequest::EReqActionComplete	- completed already - this can happen if dirty data was beyond file end
+	// CFsRequest::EReqActionBusy		- first dirty cacheline is already in use.
+	__ASSERT_DEBUG(	r == CFsRequest::EReqActionContinue || r == CFsRequest::EReqActionComplete || r == CFsRequest::EReqActionBusy, Fault(EBadRetCode));
+	if (r == CFsRequest::EReqActionContinue || r == CFsRequest::EReqActionBusy)
+		{
+		// requeue the caller's request (aOldRequest) if there is one
+		return CFsRequest::EReqActionBusy;
+		}
+	else	// CFsRequest::EReqActionComplete
+		{
+		aNewRequest->PopOperation();
+		aNewRequest->Free();
+		aNewRequest = NULL;
+		return r;
+		}
+	
+	}
+
+
+TInt TFsFileWriteDirty::PostInitialise(CFsRequest* aRequest)
+	{
+	return CFileCache::CompleteFlushDirty(aRequest);
+	}
+
+TInt CFileCache::CompleteFlushDirty(CFsRequest* aRequest)
+	{
+	CFsMessageRequest& msgRequest = *(CFsMessageRequest*) aRequest;
+
+	CFileShare* share;
+	CFileCB* file;
+	GetFileFromScratch(aRequest, share, file);
+	CFileCache* fileCache = file->FileCache();
+
+	__ASSERT_DEBUG(fileCache != NULL, Fault(ENoFileCache));
+
+#ifdef _DEBUG
+	TInt cancelling = (msgRequest.LastError() == KErrCancel)?(TBool)ETrue:(TBool)EFalse;
+#endif
+
+	fileCache->iLock.Wait();
+	TInt r = fileCache->FlushDirtySm(msgRequest);
+	fileCache->iLock.Signal();
+
+#ifdef _DEBUG
+	__ASSERT_DEBUG(!cancelling || msgRequest.LastError() == KErrCancel, Fault(ERequestUncancelled));
+#endif
+
+	// if this request has been cancelled we mustn't dispatch it again - 
+	// we still need to call state machine however so that any locked segments can be unlocked
+	if (msgRequest.LastError() == KErrCancel)
+		r = CFsRequest::EReqActionComplete;
+
+	return r;
+	}
+
+TInt CFileCache::FlushDirtySm(CFsMessageRequest& aMsgRequest)
+	{
+	enum states
+		{
+		EStBegin=0,
+		EStWriteToDisk,
+		EStWriteToDiskComplete,
+		EStMarkAsClean,
+		EStEnd
+		};
+
+	TMsgOperation* currentOperation = &aMsgRequest.CurrentOperation();
+	TInt& lastError = aMsgRequest.LastError();
+	TInt64 pos = 0;
+
+	for(;;)
+		{
+
+		switch(currentOperation->iState)
+			{
+			case EStBegin:
+				iFlushBusy = ETrue;
+
+				// fall into...
+
+			case EStWriteToDisk:
+				{
+				currentOperation->iState = EStWriteToDisk;
+
+				TUint8* addr;
+				TInt segmentCount = iCacheClient->FindFirstDirtySegment(pos, addr);
+				
+				// keep track of how many segments we've written
+				currentOperation->iScratchValue0 = (TAny*) ((TInt) currentOperation->iScratchValue0 - segmentCount);
+
+				// if no more dirty segments of if a genuine error then finish
+				// NB if the request has been cancelled then we still need to proceed and mark all the
+				// segments as clean, otherwise they will never get freed !
+				if (segmentCount == 0 || (lastError != KErrNone && lastError != KErrCancel))
+					{
+					currentOperation->iState = EStEnd;
+					break;
+					}
+
+				TInt  len = segmentCount << SegmentSizeLog2();
+
+				if (pos < iSize64)
+					// Result of Min shall be of size TInt
+					// Hence to suppress warning
+					len = (TInt)(Min(iSize64 - pos, (TInt64)len));
+				else
+					len = 0;
+
+
+				// if writing past end of file or this request has been cancelled, just mark as clean
+				if (len == 0 || lastError == KErrCancel)
+					{
+					currentOperation->iState = EStMarkAsClean;
+					break;
+					}
+
+				__CACHE_PRINT3(_L("CACHEFILE: FlushDirty first dirty pos %d, addr %08X count %d"), I64LOW(pos), addr, segmentCount);
+
+				TInt filledSegmentCount;
+				TInt lockError;
+				addr = iCacheClient->FindAndLockSegments(pos, segmentCount, filledSegmentCount, lockError, ETrue);
+
+				// If cacheline is busy, we need to post request to back of drive queue
+				// To dispatch to drive thread and intercept before DoRequestL() we must call iPostInitialise(), 
+				// so set the state back to CFsRequest::EReqStatePostInitialise
+				if (lockError == KErrInUse)
+					{
+					__CACHE_PRINT(_L("FlushDirtySm() - cacheline BUSY !"));
+					aMsgRequest.SetState(CFsRequest::EReqStatePostInitialise);
+					return CFsRequest::EReqActionBusy;
+					}
+				else if (lockError != KErrNone)
+					{
+					iFlushBusy = EFalse;
+					lastError = lockError;
+					return CFsRequest::EReqActionComplete;
+					}
+
+				currentOperation->Set(pos, len, addr, 0, EStWriteToDiskComplete);
+				if (pos < iSize64)
+					{
+					TInt r = aMsgRequest.PushOperation(
+						pos, len, addr, 0,
+						CompleteFlushDirty, 
+						EStWriteToDiskComplete);
+					if (r != KErrNone)
+						{
+						iCacheClient->UnlockSegments(pos);
+						iFlushBusy = EFalse;
+						lastError = r;
+						return CFsRequest::EReqActionComplete;
+						}
+
+					return CFsRequest::EReqActionContinue;	// continue on to TFsFileWrite::DoRequestL()()
+					}
+				}
+
+			case EStWriteToDiskComplete:
+				{
+#ifdef _DEBUG
+				// simulate a media eject to test critical error server
+				if (CCacheManagerFactory::CacheManager()->SimulateWriteFailureEnabled())
+					{
+					lastError = KErrNotReady;
+					iDrive->Dismount();
+					}
+#endif
+				pos = currentOperation->iReadWriteArgs.iPos;
+				iCacheClient->UnlockSegments(pos);
+				}
+				// fall into...
+
+			case EStMarkAsClean:
+				{
+				// NB pos must be set by EStWriteToDiskComplete or EStWriteToDisk
+
+				if (lastError != KErrNone)
+					{
+					__CACHE_PRINT1(_L("CACHEFILE: WriteThrough FAILED %d"), lastError);
+					
+					lastError = HandleWriteDirtyError(lastError);
+
+					// retry ?
+					if (lastError == KErrNone)
+						{
+						// clear error and try again
+						currentOperation->iState = EStWriteToDisk;
+						break;
+						}
+
+					iFlushError = lastError;
+					PropagateFlushErrorToAllFileShares();
+
+					__CACHE_PRINT2(_L("CACHEFILE: Resetting size from %ld to %ld"), iSize64, iFileCB->Size64());
+ 					SetSize64(iFileCB->Size64());
+					}
+
+				if (lastError != KErrNone)
+					{
+					// Destroy ALL cached data (dirty and non-dirty) !
+					iCacheClient->Purge(ETrue);
+					}
+				else
+					{
+					// Mark segment as clean
+					iCacheClient->MarkSegmentsAsClean(pos);
+					}
+
+
+				if (TInt(currentOperation->iScratchValue0) > 0)
+					currentOperation->iState = EStWriteToDisk;
+				else
+					currentOperation->iState = EStEnd;
+
+
+				}
+				break;
+
+			case EStEnd:
+				{
+				iFlushBusy = EFalse;
+
+				TUint8* addr;
+				MarkFileClean();
+				// Re-start dirty data timer if there is still some dirty data
+				if (iCacheClient->FindFirstDirtySegment(pos, addr) != 0)
+					FileDirty(aMsgRequest);
+
+				return CFsRequest::EReqActionComplete;
+				}
+			}
+		}
+	}
+
+/**
+Handle a dirty data write error
+
+Returns aError if dirty data should be thrown away or
+		KErrNone if write should be retried 
+*/
+TInt CFileCache::HandleWriteDirtyError(TInt aError)
+	{
+	__THRD_PRINT3(_L(">TRACE: CFileCache::HandleWriteDirtyError() aError %d mounted %d changed %d"), aError, iDrive->IsMounted(), iDrive->IsChanged());
+
+	// normally the disk change will have been detected by TDrive::CheckMount() but occasionally,
+	// the change will occur while writing - in which case we need to mimick what TDrive::CheckMount does,
+	// to make sure we are in a consisten state i.e. dismount the drive and set iCurrentMount to NULL
+	if (iDrive->IsChanged())
+		{
+		iDrive->SetChanged(EFalse);
+		if (iDrive->IsMounted())		//	Dismount the mount if it is still marked as mounted
+            iDrive->Dismount();
+		}
+
+	// if error didn't occur because of a media eject, do nothing 
+	if (aError == KErrNotReady && !iDrive->IsMounted())
+		{
+	
+		TLocaleMessage line1;
+		TLocaleMessage line2;
+		line1=EFileServer_PutTheCardBackLine1;
+		line2=EFileServer_PutTheCardBackLine2;
+
+		//-- create Notifier  
+		CAsyncNotifier* notifier = CAsyncNotifier::New(); 
+		if( !notifier )
+			{
+			return aError;
+			}
+
+		notifier->SetMount(iMount);
+
+		
+		// While this (drive) thread is calling the notifier server (& effectively suspended), 
+		// the main thread may call TFsFileRead::PostInitialise() or TFsFileWrite::PostInitialise() 
+		// which would cause dead-lock unless we release the lock here. We also need to release 
+		// the lock before calling CDriveThread::CompleteReadWriteRequests().
+		iLock.Signal();	
+
+		FOREVER
+			{
+			TInt buttonVal;
+
+			TInt ret = notifier->Notify(
+				TLocaleMessageText(line1),
+				TLocaleMessageText(line2),
+				TLocaleMessageText(EFileServer_Button1),
+				TLocaleMessageText(EFileServer_Button2),
+				buttonVal);
+			if (ret!=KErrNone)
+				break; 
+			if (buttonVal!=1)
+				break; // Abort
+
+
+			// Wait up to 3 seconds for a disk change - this is because there is often a substantial 
+			// (one/two second) delay between inserting a card & getting a notification that the card is ready;
+			// if we give up too soon and fire of the notifier again, it's not very user-friendly !
+			const TTimeIntervalMicroSeconds32 KHalfSecond(500000);
+			const TInt KRetries = 6;;
+			for (TInt n=0; !iDrive->IsChanged() && n<KRetries; n++)
+				User::After(KHalfSecond);
+
+
+
+			// Without this code, retry will indiscriminately write over whatever disk happens to be present.
+			// However if the write error is to the bootsector remounting will always fail because the boot
+			// sector will have changed and hence the disk is useless.
+			// 
+			TRACE1(UTF::EBorder, UTraceModuleFileSys::ECMountCBReMount, EF32TraceUidFileSys, DriveNumber());
+
+			TInt remountSuccess = iDrive->ReMount(*iMount);
+
+			TRACE1(UTF::EBorder, UTraceModuleFileSys::ECMountCBReMountRet, EF32TraceUidFileSys, remountSuccess);
+			if (!remountSuccess)
+				continue;
+			
+			
+			iMount->Drive().SetChanged(EFalse);
+
+			aError = KErrNone; // Retry
+			break;
+			}
+
+		delete notifier;
+
+
+		// Cancel hung state 
+		FsThreadManager::SetDriveHung(DriveNumber(), EFalse);
+
+		
+		// media had been removed and NOT replaced: so destroy ALL cached data 
+		// (dirty and non-dirty) for all filecaches on this drive
+		if (aError != KErrNone)
+			{
+			CDriveThread* pT=NULL;
+			TInt r=FsThreadManager::GetDriveThread(DriveNumber(), &pT);
+			if(r==KErrNone)
+				{
+				pT->CompleteReadWriteRequests();
+				iDrive->PurgeDirty(*iMount);
+				}
+			}
+
+		iLock.Wait();
+		}
+
+
+	return aError;
+	}
+
+
+/**
+Mark file as dirty
+*/
+void CFileCache::FileDirty(CFsMessageRequest& aMsgRequest)
+	{
+	CSessionFs* session = aMsgRequest.Session();
+	__ASSERT_ALWAYS(session, Fault(EDirtyDataOwnerNull));
+
+	// Remember the last session which caused the file to become dirty
+	// Always record whether any session has reserved access so the CheckDiskSpace() behaves correctly
+	if (iDirtyDataOwner == NULL || session->ReservedAccess(iDriveNum))
+		iDirtyDataOwner = session;
+
+	// start a timer after which file will be flushed
+	CDriveThread* driveThread=NULL;
+	TInt r = FsThreadManager::GetDriveThread(iDriveNum, &driveThread);
+	if(r == KErrNone && driveThread != NULL)
+		iDirtyTimer.Start(driveThread, iDirtyDataFlushTime);
+	}
+
+//----------------------------------------------------------------------------
+/**
+    Mark the file as clean and stop dirty data timer
+*/
+void CFileCache::MarkFileClean()
+	{
+	iDirtyDataOwner = NULL;
+
+	if (!iDriveThread)
+		return;
+
+	iDirtyTimer.Stop();
+	}
+
+
+
+//************************************
+// TFileCacheSettings
+//************************************
+
+RArray<TFileCacheSettings::TFileCacheConfig>* TFileCacheSettings::iFileCacheSettings = NULL;
+
+
+
+const TInt KDriveCacheSettingsArrayGranularity = 4;
+
+
+TFileCacheSettings::TFileCacheConfig::TFileCacheConfig(TInt aDrive) : 
+	iDrive(aDrive), 
+	iFileCacheReadAsync(KDefaultFileCacheReadAsync),
+	iFairSchedulingLen(KDefaultFairSchedulingLen << KByteToByteShift),
+	iCacheSize(KDefaultFileCacheSize << KByteToByteShift),
+	iMaxReadAheadLen(KDefaultFileCacheMaxReadAheadLen << KByteToByteShift),
+	iClosedFileKeepAliveTime(KDefaultClosedFileKeepAliveTime  << KByteToByteShift),	// convert milliSecs -> microSecs (approximately)
+	iDirtyDataFlushTime(KDefaultDirtyDataFlushTime  << KByteToByteShift)				// convert milliSecs -> microSecs (approximately)
+	{
+	iFlags = ConvertEnumToFlags(KDefaultFileCacheRead, KDefaultFileCacheReadAhead, KDefaultFileCacheWrite);
+	}
+
+
+TFileCacheFlags TFileCacheSettings::TFileCacheConfig::ConvertEnumToFlags(const TInt aFileCacheRead, const TInt aFileCacheReadAhead, const TInt aFileCacheWrite) 
+	{
+	TInt flags(0);
+
+	// read caching
+	if (aFileCacheRead == EFileCacheFlagEnabled)
+		flags|= EFileCacheReadEnabled;
+	else if (aFileCacheRead == EFileCacheFlagOn)
+		flags|= EFileCacheReadEnabled | EFileCacheReadOn;
+
+	// read ahead
+	if (aFileCacheReadAhead == EFileCacheFlagEnabled)
+		flags|= EFileCacheReadAheadEnabled;
+	else if (aFileCacheReadAhead == EFileCacheFlagOn)
+		flags|= EFileCacheReadAheadEnabled | EFileCacheReadAheadOn;
+
+	// write caching
+	if (aFileCacheWrite == EFileCacheFlagEnabled)
+		flags|= EFileCacheWriteEnabled;
+	else if (aFileCacheWrite == EFileCacheFlagOn)
+		flags|= EFileCacheWriteEnabled | EFileCacheWriteOn;
+
+	return TFileCacheFlags(flags);
+	}
+
+
+	
+_LIT8(KLitSectionNameDrive,"Drive%C");
+
+static const TPtrC8 KCacheFlagEnumStrings[]=
+	{
+	_S8("OFF"),
+	_S8("ENABLED"),
+	_S8("ON"),
+	_S8(""),	// terminator
+	};
+const TInt KMaxEnumLen = 7;
+
+void TFileCacheSettings::ReadEnum(const TDesC8& aSection, const TDesC8& aProperty, TInt32& aEnumVal, const TPtrC8* aEnumStrings)
+	{
+	TBuf8<KMaxEnumLen> buf;
+	if (!F32Properties::GetString(aSection, aProperty, buf))
+		return;
+	TInt n;
+	const TPtrC8* enumString;
+	for (enumString=aEnumStrings, n=0; enumString->Length()!= 0; enumString++, n++)
+		{
+		if (buf.LeftTPtr(enumString->Length()).MatchF(*enumString) == 0)
+			{
+			aEnumVal = n;
+			break;
+			}
+		}
+	}
+	
+TInt TFileCacheSettings::ReadPropertiesFile(TInt aDriveNumber)
+	{
+	Init();
+
+
+	if (!TGlobalFileCacheSettings::Enabled())
+		return KErrNone;
+
+	TFileCacheConfig* driveCacheSettings;
+	TInt r = GetFileCacheConfig(aDriveNumber, driveCacheSettings);
+	if (r != KErrNone)
+		return r;
+
+	// restore default settings in case they've been changed by SetFlags()
+	driveCacheSettings->iFlags = TFileCacheConfig::ConvertEnumToFlags(KDefaultFileCacheRead, KDefaultFileCacheReadAhead, KDefaultFileCacheWrite);
+
+
+	// Get file cache configuration settings for this drive
+	// N.B. Size/length values are specified in Kilobytes, timer values in Milliseconds
+	TBuf8<8> sectionName;
+	sectionName.Format(KLitSectionNameDrive, 'A' + aDriveNumber);
+
+	TInt32 val;
+	//  Read FileCacheSize
+	if (F32Properties::GetInt(sectionName, _L8("FileCacheSize"),		val))
+		driveCacheSettings->iCacheSize = val << KByteToByteShift;
+
+	// ensure read-ahead len is not greater than the cache size
+	driveCacheSettings->iMaxReadAheadLen = 
+		Min(driveCacheSettings->iMaxReadAheadLen, driveCacheSettings->iCacheSize);
+
+	// Read FileCacheReadAsync
+	TBool bVal;
+	if (F32Properties::GetBool(sectionName, _L8("FileCacheReadAsync"), bVal))
+		driveCacheSettings->iFileCacheReadAsync = bVal;
+
+	// Read FairSchedulingLen
+	if (F32Properties::GetInt(sectionName, _L8("FairSchedulingLen"),	val))
+		driveCacheSettings->iFairSchedulingLen = val << KByteToByteShift;
+
+	// Read ClosedFileKeepAliveTime - convert miliSecs to microSecs (approximately)
+	if (F32Properties::GetInt(sectionName, _L8("ClosedFileKeepAliveTime"),	val))
+		driveCacheSettings->iClosedFileKeepAliveTime = val << KByteToByteShift;
+
+	// Read DirtyDataFlushTime - convert miliSecs to microSecs (approximately)
+	if (F32Properties::GetInt(sectionName, _L8("DirtyDataFlushTime"),	val))
+		driveCacheSettings->iDirtyDataFlushTime = val << KByteToByteShift;
+
+	// get read, read-ahead and write states
+	TInt32 readVal = KDefaultFileCacheRead;
+	TInt32 readAheadVal = KDefaultFileCacheReadAhead;
+	TInt32 writeVal = KDefaultFileCacheWrite;
+
+	ReadEnum(sectionName, _L8("FileCacheRead"), readVal, &KCacheFlagEnumStrings[0]);
+	ReadEnum(sectionName, _L8("FileCacheReadAhead"), readAheadVal, &KCacheFlagEnumStrings[0]);
+	ReadEnum(sectionName, _L8("FileCacheWrite"), writeVal, &KCacheFlagEnumStrings[0]);
+	driveCacheSettings->iFlags = TFileCacheConfig::ConvertEnumToFlags(readVal, readAheadVal, writeVal);
+
+	__CACHE_PRINT7(_L("ReadPropertiesFile() drive %C flags %08X CacheSize %d FileCacheReadAsync %d iFairSchedulingLen %d iClosedFileKeepAliveTime %d iDirtyDataFlushTime %d\n"),
+		aDriveNumber + 'A', 
+		driveCacheSettings->iFlags, 
+		driveCacheSettings->iCacheSize, 
+		driveCacheSettings->iFileCacheReadAsync,
+		driveCacheSettings->iFairSchedulingLen,
+		driveCacheSettings->iClosedFileKeepAliveTime,
+		driveCacheSettings->iDirtyDataFlushTime);
+	
+	return KErrNone;
+	}
+
+
+TInt TFileCacheSettings::GetFileCacheConfig(TInt aDrive, TFileCacheConfig*& aConfig)
+	{
+	Init();
+
+	aConfig = NULL;
+
+	TFileCacheConfig driveCacheSettingsToMatch(aDrive);
+	
+	TInt index = iFileCacheSettings->FindInUnsignedKeyOrder(driveCacheSettingsToMatch);
+	if (index == KErrNotFound)
+		{
+		// create a new entry. 
+		TInt r = iFileCacheSettings->InsertInUnsignedKeyOrder(driveCacheSettingsToMatch);
+		if (r != KErrNone)
+			return r;
+		index = iFileCacheSettings->FindInUnsignedKeyOrder(driveCacheSettingsToMatch);
+		__ASSERT_ALWAYS(index != KErrNotFound, Fault(ECacheSettingsNotFound));
+		}
+
+	aConfig = &(*iFileCacheSettings)[index];
+	return KErrNone;
+	}
+
+void TFileCacheSettings::SetFlags(TInt aDrive, TFileCacheFlags aFlags)
+	{
+	TFileCacheConfig* driveCacheSettings;
+	TInt r = GetFileCacheConfig(aDrive, driveCacheSettings);
+	__ASSERT_ALWAYS(r == KErrNone && driveCacheSettings != NULL, Fault(ECacheSettingGetFailed));
+
+	driveCacheSettings->iFlags = aFlags;
+	}
+
+TFileCacheFlags TFileCacheSettings::Flags(TInt aDrive)
+	{
+	TFileCacheConfig* driveCacheSettings;
+	TInt r = GetFileCacheConfig(aDrive, driveCacheSettings);
+	__ASSERT_ALWAYS(r == KErrNone && driveCacheSettings != NULL, Fault(ECacheSettingGetFailed));
+
+	return driveCacheSettings->iFlags;
+	}
+
+void TFileCacheSettings::Init()
+	{
+	if (iFileCacheSettings == NULL)
+		{
+		iFileCacheSettings = new RArray<TFileCacheConfig>(KDriveCacheSettingsArrayGranularity, _FOFF(TFileCacheConfig, iDrive));
+		__ASSERT_ALWAYS(iFileCacheSettings != NULL, Fault(ECacheSettingsInitFailed));
+		}
+	}
+
+
+TBool TFileCacheSettings::FileCacheReadAsync(TInt aDrive)
+	{
+	TFileCacheConfig* driveCacheSettings;
+	TInt r = GetFileCacheConfig(aDrive, driveCacheSettings);
+	__ASSERT_ALWAYS(r == KErrNone && driveCacheSettings != NULL, Fault(ECacheSettingGetFailed));
+
+	return driveCacheSettings->iFileCacheReadAsync;
+	}
+
+TInt TFileCacheSettings::FairSchedulingLen(TInt aDrive)
+	{
+	TFileCacheConfig* driveCacheSettings;
+	TInt r = GetFileCacheConfig(aDrive, driveCacheSettings);
+	__ASSERT_ALWAYS(r == KErrNone && driveCacheSettings != NULL, Fault(ECacheSettingGetFailed));
+
+	return driveCacheSettings->iFairSchedulingLen;
+	}
+
+TInt TFileCacheSettings::MaxReadAheadLen(TInt aDrive)
+	{
+	TFileCacheConfig* driveCacheSettings;
+	TInt r = GetFileCacheConfig(aDrive, driveCacheSettings);
+	__ASSERT_ALWAYS(r == KErrNone && driveCacheSettings != NULL, Fault(ECacheSettingGetFailed));
+
+	return driveCacheSettings->iMaxReadAheadLen;
+	}
+
+TInt TFileCacheSettings::CacheSize(TInt aDrive)
+	{
+	TFileCacheConfig* driveCacheSettings;
+	TInt r = GetFileCacheConfig(aDrive, driveCacheSettings);
+	__ASSERT_ALWAYS(r == KErrNone && driveCacheSettings != NULL, Fault(ECacheSettingGetFailed));
+
+	return driveCacheSettings->iCacheSize;
+	}
+
+TInt TFileCacheSettings::ClosedFileKeepAliveTime(TInt aDrive)
+	{
+	TFileCacheConfig* driveCacheSettings;
+	TInt r = GetFileCacheConfig(aDrive, driveCacheSettings);
+	__ASSERT_ALWAYS(r == KErrNone && driveCacheSettings != NULL, Fault(ECacheSettingGetFailed));
+
+	return driveCacheSettings->iClosedFileKeepAliveTime;
+	}
+
+
+TInt TFileCacheSettings::DirtyDataFlushTime(TInt aDrive)
+	{
+	TFileCacheConfig* driveCacheSettings;
+	TInt r = GetFileCacheConfig(aDrive, driveCacheSettings);
+	__ASSERT_ALWAYS(r == KErrNone && driveCacheSettings != NULL, Fault(ECacheSettingGetFailed));
+
+	return driveCacheSettings->iDirtyDataFlushTime;
+	}
+
+//************************************
+// TClosedFileUtils
+//************************************
+
+CFsObjectCon* TClosedFileUtils::iClosedFiles = NULL;
+
+void TClosedFileUtils::InitL()
+	{
+	iClosedFiles = TheContainer->CreateL();
+	if (iClosedFiles == NULL)
+		User::LeaveIfError(KErrNoMemory);
+
+	}
+
+
+TBool TClosedFileUtils::IsClosed(CFileCache* aFileCache)
+	{
+	return (aFileCache->Container() == iClosedFiles);
+	}
+
+TInt TClosedFileUtils::Count()
+	{
+	return iClosedFiles->Count();
+	}
+
+CFileCache* TClosedFileUtils::At(TInt aIndex)
+	{
+	return (CFileCache*) (*iClosedFiles)[aIndex];
+	}
+
+
+// Add a closed file to closed file container
+void TClosedFileUtils::AddL(CFileCache* aFileCache, TBool aLock)
+	{
+	if (aFileCache->iDriveThread)
+		{
+		iClosedFiles->AddL(aFileCache, aLock);
+
+		// start a timer after which file will be removed from closed queue
+		CDriveThread* driveThread=NULL;
+		TInt r = FsThreadManager::GetDriveThread(aFileCache->iDriveNum, &driveThread);
+		if(r == KErrNone && driveThread != NULL)
+			aFileCache->iClosedTimer.Start(driveThread, aFileCache->iClosedFileKeepAliveTime);
+		}
+	}
+
+
+
+// Remove a closed file from closed file container so that it can be re-opened
+void TClosedFileUtils::ReOpen(CFileCache* aFileCache, TBool aLock)
+	{
+	// get the drive thread in case it has changed since the file was last open
+	const TInt r = FsThreadManager::GetDriveThread(aFileCache->iDriveNum, &aFileCache->iDriveThread);
+	if ((r == KErrNone) && aFileCache->iDriveThread)
+		aFileCache->iClosedTimer.Stop();
+
+	iClosedFiles->Remove(aFileCache, aLock);
+	}
+
+// Remove all closed files from closed file container and close them for good
+void TClosedFileUtils::Remove()
+	{
+	RemoveFiles(NULL, NULL);
+	}
+
+// Remove all closed files belonging to a particular TDrive from closed file container and close them for good
+void TClosedFileUtils::Remove(TInt aDrvNumber)
+	{
+	RemoveFiles(&TClosedFileUtils::TestDrive, (TAny*) aDrvNumber);
+	}
+
+// Remove a closed file from closed file container and close it for good
+void TClosedFileUtils::Remove(CFileCache* aFileCache)
+	{
+	RemoveFiles(&TClosedFileUtils::TestFile, (TAny*) aFileCache);
+	}
+
+void TClosedFileUtils::RemoveFiles(TTestFunc aTestFunc, TAny* aTestVal)
+	{
+	Lock();
+
+	TInt count = TClosedFileUtils::Count();
+	while(count--)
+		{
+		CFileCache& file = *(CFileCache*)(*iClosedFiles)[count];
+		if ((aTestFunc == NULL) || ((*aTestFunc)(file, aTestVal)))
+			{
+			iClosedFiles->Remove(&file, EFalse);
+			file.Close();
+			}
+		}
+
+	Unlock();
+	}
+
+
+TBool TClosedFileUtils::TestDrive(CFileCache& aFileCache, TAny* aVal)
+	{
+	TBool r = (aFileCache.iDriveNum == (TInt) aVal)?(TBool) ETrue: (TBool) EFalse;
+	if (r)
+		{
+		__CACHE_PRINT1(_L("CLOSEDFILES: TestDrive() closing %S\n"), &aFileCache.FileNameF() );
+		}
+	return r;
+	}
+TBool TClosedFileUtils::TestFile(CFileCache& aFileCache, TAny* aVal)
+	{
+	TBool r = (&aFileCache == ((CFileCache*) aVal))?(TBool)ETrue:(TBool)EFalse;
+	if (r)
+		{
+		__CACHE_PRINT1(_L("CLOSEDFILES: TestFile() closing %S\n"), &aFileCache.FileNameF() );
+		}
+	return r;
+	}
+
+	
+	
+void TClosedFileUtils::Lock()
+	{
+	iClosedFiles->Lock();
+	}
+
+void TClosedFileUtils::Unlock()
+	{
+	iClosedFiles->Unlock();
+	}
+	
+