userlibandfileserver/fileserver/sfile/sf_request.cpp
changeset 0 a41df078684a
child 2 4122176ea935
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/userlibandfileserver/fileserver/sfile/sf_request.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,2475 @@
+// Copyright (c) 2002-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_request.cpp
+// 
+//
+
+#include "sf_std.h"
+#include "sf_file_cache.h"
+#ifdef SYMBIAN_F32_ENHANCED_CHANGE_NOTIFICATION	
+#include "sf_notifier.h"
+#endif //SYMBIAN_F32_ENHANCED_CHANGE_NOTIFICATION	
+
+
+TParse dummyP;
+RMessage2 dummyM;
+
+CFsClientMessageRequest* RequestAllocator::iFreeHead;				
+CFsClientMessageRequest* RequestAllocator::iCloseHead;
+TInt RequestAllocator::iAllocNum;
+TInt RequestAllocator::iAllocNumOperation;
+TMsgOperation* RequestAllocator::iFreeHeadSupOp;
+
+#if defined(_USE_CONTROLIO) || defined(_DEBUG) || defined(_DEBUG_RELEASE)
+TInt RequestAllocator::iAllocated;
+#endif
+RFastLock RequestAllocator::iCacheLock;
+
+void RequestAllocator::Initialise()
+	{	
+	iFreeHead=NULL;  
+	iCloseHead=NULL; 
+	iAllocNum=0; 
+#if defined(_USE_CONTROLIO) || defined(_DEBUG) || defined(_DEBUG_RELEASE)
+	iAllocated=0;
+#endif
+	iAllocNumOperation=0;
+	iFreeHeadSupOp=NULL;
+	}
+
+TInt RequestAllocator::AllocRequest(TInt aNum)
+//
+//	Allocates a group of request objects
+//
+	{
+    TInt i;
+	if(iAllocNum < KMaxRequestAllocated)
+		{
+		__CACHE_PRINT(_L("RequestAllocator::AllocRequest() Not reached the limit"));
+		CFsClientMessageRequest* list;
+		CFsClientMessageRequest* start;
+		list = new CFsClientMessageRequest[KAllocReqBlock];
+		start = list;
+		if(!list)
+			return KErrNoMemory;
+		
+		// Make sure the constructors are called for every element in the array
+		// - some compilers don't do this
+		for(TInt j=0; j<KAllocReqBlock; j++)
+			{
+			CFsClientMessageRequest* request = &list[j];
+			new(request) CFsClientMessageRequest();
+			}
+
+		iAllocNum += KAllocReqBlock;
+		CFsClientMessageRequest* last;
+		for(i=1;i<KAllocReqBlock;i++)
+			{
+			last = list;
+			list++;
+			last->iNext = list;
+			}
+		list->iNext = iFreeHead;
+		iFreeHead = start;
+		return KErrNone;
+		}
+	else
+		{
+		__CACHE_PRINT1(_L("RequestAllocator::AllocRequest() Limit exceeded Count = %d"),aNum);
+		CFsClientMessageRequest* request;
+		for(i=0;i<aNum;i++)
+			{
+			request=new CFsClientMessageRequest;
+			if(!request)
+				return KErrNoMemory;
+#if defined(_USE_CONTROLIO) || defined(_DEBUG) || defined(_DEBUG_RELEASE)
+			iAllocated++;
+#endif
+			request->SetAllocated();
+			request->iNext=iFreeHead;
+			iFreeHead=request;
+			}
+		return KErrNone;
+		}
+	}
+
+void RequestAllocator::FreeRequest(CFsClientMessageRequest* aRequest)
+//
+//free request 
+//
+	{
+	__CACHE_PRINT1(_L("PLUGIN: RequestAllocator::FreeRequest for %x"), aRequest);
+	if(aRequest->IsAllocated())
+		{
+		__CACHE_PRINT(_L("RequestAllocator::FreeRequest() Allocated request"));
+		delete(aRequest);
+#if defined(_USE_CONTROLIO) || defined(_DEBUG) || defined(_DEBUG_RELEASE)
+		iAllocated--;
+#endif
+		return;
+		}
+	
+	__CACHE_PRINT(_L("RequestAllocator::FreeRequest() returning to free list"));
+	iCacheLock.Wait();
+	aRequest->iNext = iFreeHead;
+	iFreeHead=aRequest;
+	aRequest->SetSubstedDrive(NULL);
+	iCacheLock.Signal();
+	}
+
+void RequestAllocator::OpenSubFailed(CSessionFs* aSession)
+//
+//	Move requst from closed list to free list
+//
+	{
+	__ASSERT_DEBUG(iCloseHead!=NULL,Fault(ERequestAllocatorOpenSubFailed)); // On arriving here Close Queue is supposed to be empty
+	__ASSERT_ALWAYS(aSession!=NULL,Fault(ERequestAllocatorOpenSubFailed));
+	if (iCloseHead==NULL)
+		{
+		return;
+		}
+	iCacheLock.Wait();
+	CFsClientMessageRequest* rp = iCloseHead;
+	iCloseHead = rp->iNext;
+	
+	// dec the number of closed requests owned by this session
+	aSession->CloseRequestCountDec();
+
+	rp->iNext = NULL;
+	if(rp->IsAllocated())
+		{
+		__CACHE_PRINT(_L("RequestAllocator::OpenSubFailed() Allocated request"));
+		delete(rp);
+#if defined(_USE_CONTROLIO) || defined(_DEBUG) || defined(_DEBUG_RELEASE)
+		iAllocated--;
+#endif
+		}
+	else
+		{
+		__CACHE_PRINT(_L("RequestAllocator::OpenSubFailed()"));
+		if(iFreeHead)
+			{
+			rp->iNext = iFreeHead;
+			}
+		else
+			{
+			rp->iNext = NULL;
+			}
+
+		iFreeHead = rp;		
+		}
+	iCacheLock.Signal();
+	}
+
+TInt RequestAllocator::GetMessageRequest(const TOperation& aOperation,const RMessage2& aMessage,CFsClientMessageRequest* &aRequest)
+//
+//	tries to get a pre allocated message from the cache. Failing that allocates one indivisualy 
+//	
+	{
+	if(aOperation.IsOpenSubSess())
+		{
+		__CACHE_PRINT(_L("++RequestAllocator::GetMessageRequest() Open sub-sess"));
+		iCacheLock.Wait();
+		if(iFreeHead == NULL || iFreeHead->iNext == NULL)
+			{
+			if(AllocRequest(2)!= KErrNone)
+				{
+				iCacheLock.Signal();
+				return KErrNoMemory;
+				}
+			}
+		aRequest= iFreeHead;						//get our request from free head
+		iFreeHead = iFreeHead->iNext->iNext;	//set next but one as new free head read for next
+
+		aRequest->iNext->iNext = NULL;				//seperate our request and close from free list
+		CFsClientMessageRequest* CRp = aRequest->iNext;
+		aRequest->iNext = NULL;
+		if(iCloseHead)
+			{
+			CRp->iNext = iCloseHead;		//set second one as a reserved (tail) close request
+			iCloseHead = CRp;
+			}
+		else
+			iCloseHead = CRp;
+		
+		((CSessionFs*) aMessage.Session())->CloseRequestCountInc();
+		}
+	else if(aOperation.IsCloseSubSess())
+		{
+		__CACHE_PRINT(_L("++RequestAllocator::GetMessageRequest() Close sub-sess"));
+
+		CFsObject* pO = SessionObjectFromHandle(aMessage.Int3(),0,reinterpret_cast<CSessionFs*>(aMessage.Session()));
+		if(!pO)
+			return KErrBadHandle;
+
+		CObjPromotion* pm = (CObjPromotion*)pO;
+		TInt function = aMessage.Function();
+		switch(function & KIpcFunctionMask)
+			{
+			case EFsFormatSubClose:
+				{
+				if(pm->UniqueID() != Formats->UniqueID())
+					{
+					return KErrBadHandle;
+					}
+				break;
+				}
+			case EFsDirSubClose:
+				{
+				if(pm->UniqueID() != Dirs->UniqueID())
+					{
+					return KErrBadHandle;
+					}
+				break;
+				}
+			case EFsFileSubClose:
+				{
+				if(pm->UniqueID() != FileShares->UniqueID())
+					{
+					return KErrBadHandle;
+					}
+				break;
+				}
+			case EFsRawSubClose:
+				{
+				if(pm->UniqueID() != RawDisks->UniqueID())
+					{
+					return KErrBadHandle;
+					}
+				break;
+				}
+			case EFsPluginSubClose:
+				{
+				if(pm->UniqueID() != FsPluginManager::iPluginConns->UniqueID())
+					{
+					return KErrBadHandle;
+					}
+				break;
+				}
+#ifdef SYMBIAN_F32_ENHANCED_CHANGE_NOTIFICATION					
+			case EFsNotificationSubClose:
+				{
+				if(pm->UniqueID() != FsNotificationManager::iNotifyRequests->UniqueID())
+					{
+					return KErrBadHandle;
+					}
+				break;
+				}
+#endif //SYMBIAN_F32_ENHANCED_CHANGE_NOTIFICATION					
+			default:
+				Fault(ECloseSubBadMessage);
+			}
+
+		iCacheLock.Wait();
+		aRequest = iCloseHead;
+		iCloseHead = aRequest->iNext;
+		((CSessionFs*) aMessage.Session())->CloseRequestCountDec();
+		aRequest->iNext = NULL;
+
+		if((function & KIpcFunctionMask)!= EFsPluginSubClose &&
+			(function & KIpcFunctionMask)!= EFsNotificationSubClose)
+			{
+			aRequest->SetDrive(&TheDrives[((CFsDispatchObject*)pO)->DriveNumber()]);
+			}
+
+		aRequest->SetScratchValue((TUint)pO);
+		}
+	else
+		{
+		__CACHE_PRINT(_L("++RequestAllocator::GetMessageRequest() "));
+		iCacheLock.Wait();
+		if(!iFreeHead)
+			{
+			if(AllocRequest(1) != KErrNone)
+				{
+				iCacheLock.Signal();
+				return KErrNoMemory; 
+				}
+			}
+		aRequest = iFreeHead;						
+		iFreeHead = aRequest->iNext;
+		aRequest->iNext= NULL;
+		}
+
+	aRequest->Init();
+
+	iCacheLock.Signal();
+
+	// pre-allocate any TParse objects needed by this request
+	if (aRequest->AllocParseObjects(aOperation) != KErrNone)
+		{
+		aRequest->Free();
+		return KErrNoMemory; 
+		}
+
+	__CACHE_PRINT1(_L("--RequestAllocator::GetMessageRequest() allocated %08x"), aRequest);
+
+	return KErrNone;
+	}	
+
+
+#if defined(_USE_CONTROLIO) || defined(_DEBUG) || defined(_DEBUG_RELEASE)
+TInt RequestAllocator::CloseCount()
+	{TInt count=0;
+	CFsClientMessageRequest* list=iCloseHead;
+	while(list!=NULL)
+		{
+		count++;
+		list=list->iNext;
+		}
+	return(count);
+	} 
+TInt RequestAllocator::FreeCount()
+	{
+	TInt count=0;
+	CFsClientMessageRequest* list=iFreeHead;
+	while(list!=NULL)
+		{
+		count++;
+		list=list->iNext;
+		}
+	return(count);}
+#endif
+
+TInt RequestAllocator::AllocOperation()
+//
+//	Allocates a group of TMsgOperation objects
+//
+// Must be called with iCacheLock held
+	{
+    TInt i;
+	if(iAllocNumOperation < KMaxOperationAllocated)
+		{
+		__CACHE_PRINT(_L("RequestAllocator::AllocOperation() Not reached the limit"));
+		TMsgOperation* list;
+		TMsgOperation* start;
+		list = new TMsgOperation[KAllocReqBlock];
+		start = list;
+		if(!list)
+			return KErrNoMemory;
+		
+		for(TInt j=0; j<KAllocReqBlock; j++)
+			{
+			TMsgOperation* request = &list[j];
+			request->iIsAllocated = EFalse;
+			}
+
+		iAllocNumOperation += KAllocReqBlock;
+		TMsgOperation* last;
+		for(i=1;i<KAllocReqBlock;i++)
+			{
+			last = list;
+			list++;
+			last->iNext = list;
+			}
+		list->iNext = iFreeHeadSupOp;
+		iFreeHeadSupOp = start;
+		return KErrNone;
+		}
+	else
+		{
+		__CACHE_PRINT(_L("RequestAllocator::AllocOperation() Limit exceeded"));
+		TMsgOperation* request;
+
+		request=new TMsgOperation;
+		if(!request)
+			return KErrNoMemory;
+#if defined(_USE_CONTROLIO) || defined(_DEBUG) || defined(_DEBUG_RELEASE)
+		iAllocated++;
+#endif
+		request->iIsAllocated = ETrue;
+		request->iNext=iFreeHeadSupOp;
+		iFreeHeadSupOp=request;
+
+		return KErrNone;
+		}
+	}
+TInt RequestAllocator::GetOperation(TMsgOperation* &aOperation)
+//
+//	tries to get a pre allocated subop from the cache. Failing that allocates one individualy 
+//	
+	{
+
+	__CACHE_PRINT(_L("RequestAllocator::GetOperation() "));
+	iCacheLock.Wait();
+	if(!iFreeHeadSupOp)
+		{
+		if(AllocOperation() != KErrNone)
+			{
+			iCacheLock.Signal();
+			return KErrNoMemory; 
+			}
+		}
+	aOperation = iFreeHeadSupOp;						
+	iFreeHeadSupOp = aOperation->iNext;
+	aOperation->iNext = aOperation->iPrev = NULL;
+
+	iCacheLock.Signal();
+	return KErrNone;
+	}	
+
+void RequestAllocator::FreeOperation(TMsgOperation* aOperation)
+//
+// free Operation
+//
+	{
+	if(aOperation->iIsAllocated)
+		{
+		__CACHE_PRINT(_L("RequestAllocator::FreeOperation() Allocated subop"));
+		delete(aOperation);
+#if defined(_USE_CONTROLIO) || defined(_DEBUG) || defined(_DEBUG_RELEASE)
+		iAllocated--;
+#endif
+		return;
+		}
+	
+	__CACHE_PRINT(_L("RequestAllocator::FreeOperation() returning to free list"));
+	iCacheLock.Wait();
+	aOperation->iNext = iFreeHeadSupOp;	// NB backward link only used when request in in use
+	iFreeHeadSupOp = aOperation;
+
+	iCacheLock.Signal();
+	}
+
+
+CFsRequest::CFsRequest()
+//
+//
+//
+	: iOperation(NULL),iDriveNumber(KDriveInvalid)
+	{}
+
+CFsRequest::~CFsRequest()
+//
+//
+//
+	{}
+
+void CFsRequest::Set(const TOperation& aOperation,CSessionFs* aSession)
+//
+//
+//
+	{
+
+	SetState(EReqStateInitialise);
+
+	iOperation     = const_cast<TOperation*>(&aOperation);
+	iSession       = aSession;
+	iIsCompleted   = aOperation.IsCompleted();
+	iError         = KErrNone;
+	iDriveNumber   = KDriveInvalid;
+	iCurrentPlugin = NULL;
+	iOwnerPlugin   = NULL;
+	iDirectToDrive = EFalse;
+	iClientThreadId= 0;
+	iFlags &= ~(EFreeChanged | EPostInterceptEnabled | EPostOperation | EFsObjectOpen);
+	iScratchValue = 0;
+	}
+
+void CFsRequest::Set(CSessionFs* aSession)
+//
+//
+//
+	{
+	__ASSERT_DEBUG(iOperation,Fault(EBaseRequestSet1));
+
+	SetState(EReqStateInitialise);
+
+	iSession       = aSession;
+	iIsCompleted   = iOperation->IsCompleted();
+	iError         = KErrNone;
+	iDriveNumber   = KDriveInvalid;
+	iCurrentPlugin = NULL;
+	iOwnerPlugin   = NULL;
+	iDirectToDrive = EFalse;
+	iClientThreadId= 0;
+	iFlags &= ~(EFreeChanged | EPostInterceptEnabled | EPostOperation | EFsObjectOpen);
+	iScratchValue = 0;
+	}
+
+
+TParse& CFsRequest::Src()
+//
+//
+//
+	{
+	Fault(EBaseRequestSrc);
+	return(dummyP);
+	}
+
+TParse& CFsRequest::Dest()
+//
+//
+//
+	{
+	Fault(EBaseRequestDest);
+	return(dummyP);
+	}
+
+TDrive* CFsRequest::Drive()
+//
+//
+//
+	{
+	Fault(EBaseRequestDrive);
+	return(NULL);
+	}
+
+TDrive* CFsRequest::SubstedDrive()
+//
+//
+//
+	{
+	Fault(EBaseRequestSubstedDrive);
+	return(NULL);
+	}
+
+void CFsRequest::SetDrive(TDrive* /*aDrive*/)
+//
+//
+//
+	{
+	Fault(EBaseRequestSetDrive);
+	}
+
+void CFsRequest::SetSubstedDrive(TDrive* /*aDrive*/)
+//
+//
+//
+	{
+	Fault(EBaseRequestSetSubstedDrive);
+	}
+
+TInt CFsRequest::GetSlot(TFsPluginRequest::TF32ArgType aType)
+	{
+	if(iOperation->Arg(0) == aType) return 0;
+	if(iOperation->Arg(1) == aType) return 1;
+	if(iOperation->Arg(2) == aType) return 2;
+	if(iOperation->Arg(3) == aType) return 3;
+	
+	return(-1);
+	}
+
+
+TBool CFsMessageRequest::IsNotifierSpecific() const
+	{
+#ifndef SYMBIAN_F32_ENHANCED_CHANGE_NOTIFICATION
+	return EFalse;
+#else
+	TInt function = iOperation->iFunction;
+	return(	function == EFsNotificationAdd      || 
+			function == EFsNotificationBuffer ||
+			function == EFsNotificationCancel ||
+			function == EFsNotificationOpen ||
+			function == EFsNotificationRemove ||
+			function == EFsNotificationRequest ||
+			function == EFsNotificationSubClose);
+#endif //SYMBIAN_F32_ENHANCED_CHANGE_NOTIFICATION
+	}
+
+
+TBool CFsMessageRequest::IsNotifierSupported() const
+	{
+#ifndef SYMBIAN_F32_ENHANCED_CHANGE_NOTIFICATION
+	return EFalse;
+#else
+	TInt function = iOperation->iFunction;
+	return(	function == EFsDelete || 
+			function == EFsRmDir || 
+			function == EFsMkDir || 
+			function == EFsFormatNext || 
+			function == EFsFileCreate ||
+			function == EFsFileReplace ||
+			function == EFsFileRename ||
+			function == EFsRename ||
+			function == EFsReplace ||
+			function == EFsSetDriveName ||
+			function == EFsSetVolume ||
+			function == EFsSetEntry ||
+			function == EFsFileSetAtt ||
+			function == EFsFileSet ||
+			function == EFsMountFileSystem ||
+			function == EFsDismountFileSystem ||
+			function == EFsFileSetSize ||
+			function == EFsFileWrite ||
+			function == EFsFileWriteDirty ||
+			function == EFsRawDiskWrite ||
+			function == EFsMountFileSystemScan);
+#endif //SYMBIAN_F32_ENHANCED_CHANGE_NOTIFICATION
+	}
+
+
+TInt CFsRequest::Read(TFsPluginRequest::TF32ArgType aType, TInt& aVal)
+	{
+	if(iOperation->Arg(0) == aType) { aVal = Message().Int0(); return KErrNone; }
+	if(iOperation->Arg(1) == aType) { aVal = Message().Int1(); return KErrNone; }
+	if(iOperation->Arg(2) == aType) { aVal = Message().Int2(); return KErrNone; }
+	if(iOperation->Arg(3) == aType) { aVal = Message().Int3(); return KErrNone; }
+	
+	return KErrNotSupported;
+	}
+
+
+TInt CFsRequest::Read(TFsPluginRequest::TF32ArgType aType, TUint& aVal)
+	{
+	if(iOperation->Arg(0) == aType) { aVal = (TUint)Message().Int0(); return KErrNone; }
+	if(iOperation->Arg(1) == aType) { aVal = (TUint)Message().Int1(); return KErrNone; }
+	if(iOperation->Arg(2) == aType) { aVal = (TUint)Message().Int2(); return KErrNone; }
+	if(iOperation->Arg(3) == aType) { aVal = (TUint)Message().Int3(); return KErrNone; }
+	
+	return KErrNotSupported;
+	}
+
+TInt CFsRequest::Read(TFsPluginRequest::TF32ArgType aType, TInt64& aVal)
+	{
+	TPckg<TInt64> pkVal(aVal);
+	// EFsFileLock, EFsFileUnLock, EFsFileSeek and EFsFileSetSize need special treatment
+	if(iOperation->Arg(0) == aType)
+		{
+		if((iOperation->Function()==EFsFileLock)   ||
+		   (iOperation->Function()==EFsFileUnLock)   )
+			{
+			if(IsDescData(KMsgPtr0))
+				Read(KMsgPtr0,pkVal);
+			else
+				aVal = MAKE_TINT64(0, Message().Int0());	// Position is unsigned value
+			return KErrNone;
+			}
+		
+		if((iOperation->Function()==EFsFileSeek)    ||		// Seek offset (input paramater)
+		   (iOperation->Function()==EFsFileSize)    ||		// Size (output paramater)
+		   (iOperation->Function()==EFsFileSetSize)   )		// Size to be set (input paramater)
+			{
+			if(IsDescData(KMsgPtr0))
+				Read(KMsgPtr0,pkVal);
+			else
+				aVal = Message().Int0();	// Seek offset / Size is signed value
+			return KErrNone;
+			}
+		
+		aVal = Message().Int0();
+		return KErrNone; 
+		}
+	
+	// EFsFileLock and EFsFileUnLock need special treatment
+	if(iOperation->Arg(1) == aType) 
+		{
+		if((iOperation->Function()==EFsFileLock)   ||
+		   (iOperation->Function()==EFsFileUnLock)   )
+			{
+			if(IsDescData(KMsgPtr1))
+				Read(KMsgPtr1,pkVal);
+			else
+				aVal = MAKE_TINT64(0, Message().Int1());	// Length is unsigned value
+			return KErrNone; 
+			}
+		
+		aVal = Message().Int1();
+		return KErrNone; 
+		}
+	
+	// EFsFileRead, EFsFileWrite, EFsFileSeek and EFsReadFileSection need special treatment
+	if(iOperation->Arg(2) == aType) 
+		{
+		if((iOperation->Function()==EFsFileRead)  ||
+		   (iOperation->Function()==EFsFileWrite)   )
+			{
+			if(IsDescData(KMsgPtr2))
+				Read(KMsgPtr2,pkVal);
+			else
+				{
+				if(Message().Int2() == (TInt)I64LOW(KCurrentPosition64))
+					aVal = KCurrentPosition64;				// Position is KCurrentPosition64 (-1)
+				else
+					aVal = MAKE_TINT64(0,Message().Int2());	// Position is unsigned value
+				}
+			return KErrNone; 
+			}
+		
+		if(iOperation->Function()==EFsFileSeek)
+			{
+			if(IsDescData(KMsgPtr2))
+				Read(KMsgPtr2,pkVal);
+			else
+				aVal = Message().Int2();	// New position is signed value
+			return KErrNone;
+			}
+		
+		if(iOperation->Function()==EFsReadFileSection)
+			{
+			if(IsDescData(KMsgPtr2))
+			Read(KMsgPtr2,pkVal);
+			else
+			aVal = MAKE_TINT64(0,Message().Int2()); // Position is unsigned value
+			return KErrNone;
+			}
+		
+		aVal = Message().Int2();		
+		return KErrNone; 
+		}
+	
+	if(iOperation->Arg(3) == aType) 
+		{
+		aVal = Message().Int3();
+		return KErrNone;
+		}
+	
+	return KErrNotSupported;
+	}
+
+void CFsRequest::SetAndOpenScratchValue(const TInt64& aValue)
+	{
+	if (IsFsObjectOpen())
+		{
+		((CFsDispatchObject*) I64LOW(iScratchValue))->Close();
+		SetFsObjectOpen(EFalse);
+		}
+	if (I64LOW(aValue) && iOperation && (iOperation->iFlags & EFileShare))
+		{
+		((CFsDispatchObject*) I64LOW(aValue))->Open();
+		SetFsObjectOpen(ETrue);
+		}
+	iScratchValue = aValue;
+	}
+
+
+TInt CFsRequest::Read(TFsPluginRequest::TF32ArgType aType, TDes8& aDes, TInt aOffset)
+	{
+	TInt slot = GetSlot(aType);
+	if(slot >= 0)
+		return Read(slot, aDes, aOffset);
+	return KErrNotSupported;
+	}
+
+
+TInt CFsRequest::Read(TFsPluginRequest::TF32ArgType aType, TDes16& aDes, TInt aOffset)
+	{
+	TInt slot = GetSlot(aType);
+	if(slot >= 0)
+		return Read(slot, aDes, aOffset);
+	return KErrNotSupported;
+	}
+
+
+TInt CFsRequest::Write(TFsPluginRequest::TF32ArgType aType, const TDesC8& aDes, TInt aOffset)
+	{
+	TInt slot = GetSlot(aType);
+	if(slot >= 0)
+		return Write(slot, aDes, aOffset);
+	return KErrNotSupported;
+	}
+
+
+TInt CFsRequest::Write(TFsPluginRequest::TF32ArgType aType, const TDesC16& aDes, TInt aOffset)
+	{
+	TInt slot = GetSlot(aType);
+	if(slot >= 0)
+		return Write(slot, aDes, aOffset);
+	return KErrNotSupported;
+	}
+
+
+TInt CFsRequest::Read(const TInt aMsgPtr,TDes8 &aDes)
+	{
+	if (Message().Handle() == KLocalMessageHandle)
+		{
+		TDesC8* pDes = (TDesC8*) ((const RLocalMessage*) &Message())->Arg(aMsgPtr);
+		if (aDes.MaxLength() < pDes->Length())
+			return KErrTooBig;
+		aDes.Copy(*pDes);
+		return KErrNone;
+		}
+
+	return Message().Read(aMsgPtr,aDes,0);
+	}
+
+
+TInt CFsRequest::Read(const TInt aMsgPtr,TDes8 &aDes,TInt anOffset)
+	{
+	if (Message().Handle() == KLocalMessageHandle)
+		{
+		TDesC8* pDes = (TDesC8*) ((const RLocalMessage*) &Message())->Arg(aMsgPtr);
+		if (aDes.MaxLength() < pDes->Length() + anOffset)
+			return KErrTooBig;
+		aDes.SetLength(pDes->Length() + anOffset);
+		aDes.MidTPtr(anOffset).Copy(*pDes);
+		return KErrNone;
+		}
+
+	return Message().Read(aMsgPtr,aDes,anOffset);
+	}
+
+
+TInt CFsRequest::Read(const TInt aMsgPtr,TDes16 &aDes)
+	{
+	if (Message().Handle() == KLocalMessageHandle)
+		{
+		TDesC16* pDes = (TDesC16*) ((const RLocalMessage*) &Message())->Arg(aMsgPtr);
+		if (aDes.MaxLength() < pDes->Length())
+			return KErrTooBig;
+		aDes.Copy(*pDes);
+		return KErrNone;
+		}
+
+	return Message().Read(aMsgPtr,aDes,0);
+	}
+
+
+TInt CFsRequest::Read(const TInt aMsgPtr,TDes16 &aDes,TInt anOffset)
+	{
+	if (Message().Handle() == KLocalMessageHandle)
+		{
+		TDesC16* pDes = (TDesC16*) ((const RLocalMessage*) &Message())->Arg(aMsgPtr);
+		if (aDes.MaxLength() < pDes->Length() + anOffset)
+			return KErrTooBig;
+		aDes.SetLength(pDes->Length() + anOffset);
+		aDes.MidTPtr(anOffset).Copy(*pDes);
+		return KErrNone;
+		}
+
+	return Message().Read(aMsgPtr,aDes,anOffset);
+	}
+
+
+TInt CFsRequest::Write(const TInt aMsgNum,const TDesC8 &aDes)
+	{
+	if (Message().Handle() == KLocalMessageHandle)
+		{
+		TDes8* pDes = (TDes8*) ((const RLocalMessage*) &Message())->Arg(aMsgNum);
+		if (pDes->MaxLength() < aDes.Length())
+			return KErrTooBig;
+		pDes->Copy(aDes);
+		return KErrNone;
+		}
+
+	return Message().Write(aMsgNum,aDes,0);
+	}
+
+
+TInt CFsRequest::Write(const TInt aMsgNum,const TDesC8 &aDes,TInt anOffset)
+	{
+	if (Message().Handle() == KLocalMessageHandle)
+		{
+		TDes8* pDes = (TDes8*) ((const RLocalMessage*) &Message())->Arg(aMsgNum);
+		if (pDes->MaxLength() < aDes.Length() + anOffset)
+			return KErrTooBig;
+		pDes->SetLength(aDes.Length() + anOffset);
+		pDes->MidTPtr(anOffset).Copy(aDes);
+		return KErrNone;
+		}
+
+	return Message().Write(aMsgNum,aDes,anOffset);
+	}
+
+
+TInt CFsRequest::Write(const TInt aMsgNum,const TDesC16 &aDes)
+	{
+	if (Message().Handle() == KLocalMessageHandle)
+		{
+		TDes16* pDes = (TDes16*) ((const RLocalMessage*) &Message())->Arg(aMsgNum);
+		if (pDes->MaxLength() < aDes.Length())
+			return KErrTooBig;
+		pDes->Copy(aDes);
+		return KErrNone;
+		}
+
+	return Message().Write(aMsgNum,aDes,0);
+	}
+
+
+TInt CFsRequest::Write(const TInt aMsgNum,const TDesC16 &aDes,TInt anOffset)
+	{
+	if (Message().Handle() == KLocalMessageHandle)
+		{
+		TDes16* pDes = (TDes16*) ((const RLocalMessage*) &Message())->Arg(aMsgNum);
+		if (pDes->MaxLength() < aDes.Length() + anOffset)
+			return KErrTooBig;
+		pDes->SetLength(aDes.Length() + anOffset);
+		pDes->MidTPtr(anOffset).Copy(aDes);
+		return KErrNone;
+		}
+
+	return Message().Write(aMsgNum,aDes,anOffset);
+	}
+
+
+void CFsRequest::ReadL(const TInt aMsgPtr,TDes8 &aDes)
+	{ User::LeaveIfError(Read(aMsgPtr,aDes)); }
+
+void CFsRequest::ReadL(const TInt aMsgPtr,TDes8 &aDes,TInt anOffset)
+	{ User::LeaveIfError(Read(aMsgPtr,aDes,anOffset)); }
+
+void CFsRequest::ReadL(const TInt aMsgPtr,TDes16 &aDes)
+	{ User::LeaveIfError(Read(aMsgPtr,aDes)); }
+
+void CFsRequest::ReadL(const TInt aMsgPtr,TDes16 &aDes,TInt anOffset)
+	{ User::LeaveIfError(Read(aMsgPtr,aDes,anOffset)); }
+
+void CFsRequest::WriteL(const TInt aMsgNum,const TDesC8 &aDes)
+	{ User::LeaveIfError(Write(aMsgNum,aDes)); }
+
+void CFsRequest::WriteL(const TInt aMsgNum,const TDesC8 &aDes,TInt anOffset)
+	{ User::LeaveIfError(Write(aMsgNum,aDes,anOffset)); }
+
+void CFsRequest::WriteL(const TInt aMsgNum,const TDesC16 &aDes)
+	{ User::LeaveIfError(Write(aMsgNum,aDes)); }
+
+void CFsRequest::WriteL(const TInt aMsgNum,const TDesC16 &aDes,TInt anOffset)
+	{ User::LeaveIfError(Write(aMsgNum,aDes,anOffset)); }
+
+
+/**
+    @param  aMsgNum message argument index 
+    @return client side descriptor length 
+*/
+TInt CFsRequest::GetDesLength(const TInt aMsgNum)
+	{
+	if (Message().Handle() == KLocalMessageHandle)
+		{
+		TDes8* pDes = (TDes8*) ((const RLocalMessage*) &Message())->Arg(aMsgNum);
+		return pDes->Length();
+		}
+	else
+		{
+		return Message().GetDesLength(aMsgNum);
+		}
+	}
+
+const RMessage2& CFsRequest::Message()
+//
+//
+//
+	{
+	Fault(EBaseRequestMessage);
+	return(dummyM);
+	}
+
+
+
+void CFsMessageRequest::Set(const RMessage2& aMessage,CSessionFs* aSession)
+//
+// For reuseable request
+//
+	{
+	iMessage=aMessage;
+	iDrive=NULL;
+	iSubstedDrive=NULL;
+	CFsRequest::Set(aSession);
+	SetFreeChanged(EFalse);
+	EnablePostIntercept(ETrue);
+	}
+
+	
+void CFsMessageRequest::Set(const RMessage2& aMessage,const TOperation& aOperation,CSessionFs* aSession)
+//
+//
+//
+	{
+	iCurrentPlugin=NULL;
+	iMessage=aMessage;
+	iDrive=NULL;
+	iSubstedDrive=NULL;
+	CFsRequest::Set(aOperation,aSession);
+	SetFreeChanged(EFalse);
+	EnablePostIntercept(ETrue);
+	}
+
+void CFsMessageRequest::Set(const TOperation& aOperation)
+	{
+	iOperation=const_cast<TOperation*>(&aOperation);
+	}
+
+void CFsMessageRequest::Process()
+//
+// Process the request - (passing to a plugin or a drive thread)
+//
+	{
+	__THRD_PRINT3(_L("CFsMessageRequest::Process() req %08x state %d plugin 0x%x"), this, iReqState, iCurrentPlugin);
+	
+	// initialise request - if not initialised already
+	if (iReqState == EReqStateInitialise)
+		{
+		TInt r = DoInitialise();
+		if (r == EReqActionComplete)
+			{
+			return;
+			}
+		else if (r == EReqActionBusy)	// request postponed ?
+			{
+			SetState(EReqStateInitialise);
+			Dispatch(EFalse);
+			return;
+			}
+		else if (r == KErrNone && iCurrentPlugin)		// dispatch to plugin thread ?
+			{
+			Dispatch(EFalse);
+			return;
+			}
+		}
+
+	if(iCurrentPlugin)
+		{
+		if (IsPostOperation())
+			{
+			ProcessPostOperation();
+			return;
+			}
+			
+		if(!IsPluginSpecific())
+			{
+			ProcessPreOperation();
+			return;
+			}
+		}
+
+	
+	// Is there a PostInitialise function ?
+	if (iReqState == EReqStatePostInitialise)
+		{
+		TInt r = PostInitialise();
+		if (r == EReqActionComplete)
+			{
+			return;
+			}
+		else if (r == EReqActionBusy)	// request postponed ?
+			{
+			SetState(EReqStatePostInitialise);
+			Dispatch(EFalse);
+			return;
+			}
+		}
+
+	ProcessDriveOperation();
+	}
+	
+void CFsMessageRequest::ProcessPostOperation()
+//
+// Process the message in post operation mode (handled by the current plugin)
+//
+	{
+	TInt err = KErrNone;
+	if(!iCurrentPlugin->IsPluginThread(*this))
+		{
+		// The request hasn't come from this plugin so it's safe to dispatch		
+		TFsPluginRequest request(this);
+		TRAPD(leaveValue, err = iCurrentPlugin->DoRequestL(request));
+		if(leaveValue != KErrNone)
+			{
+			Panic(KFsClient,leaveValue);
+			if(iOperation->IsOpenSubSess())		
+				RequestAllocator::OpenSubFailed(Session());
+			Free();
+			return;
+			}
+
+		if(!IsExpectedResult(err))
+			{
+			Complete(err);
+			return;
+			}
+		}
+	
+	// Find the previous plugin in the chain and dispatch
+	//  - If no more plugins are interested in this message, then complete
+	FsPluginManager::PrevPlugin(iCurrentPlugin, this, ETrue);
+	if(iCurrentPlugin == NULL)
+		{
+		Complete(GetError());
+		return;
+		}
+
+	Dispatch();
+	return;
+	}
+	
+
+void CFsMessageRequest::ProcessPreOperation()
+//
+// Process the message in pre operation mode (handled by the current plugin)
+//
+	{
+	TInt err = KErrNone;
+	if(!iCurrentPlugin->IsPluginThread(*this))
+		{
+		// The request hasn't come from this plugin so it's safe to dispatch		
+		TFsPluginRequest request(this);		
+		TRAPD(leaveValue, err = iCurrentPlugin->DoRequestL(request));
+		__PLUGIN_PRINT1(_L("PLUGIN: CFsMessageRequest:: %x processed by plugin"), this);
+
+		if((iOperation->Function() == EFsDismountPlugin) && (err !=  KErrPermissionDenied))
+			{
+			TRAP(leaveValue, err = iOperation->DoRequestL(this));
+			}
+
+		if(leaveValue != KErrNone)
+			{
+			Panic(KFsClient,leaveValue);
+			if(iOperation->IsOpenSubSess())		//this should be close subsession
+				RequestAllocator::OpenSubFailed(Session());	//need a close subsession fail
+			Free();
+			return;
+			}
+		}
+
+	if(err == KErrNone)
+		{
+		// Find the next plugin in the chain and dispatch
+		//  - If no more plugins are interested in this message, 
+		//	  then Dispatch() will process the request in drive/main thread context.
+		FsPluginManager::NextPlugin(iCurrentPlugin, this,(TBool)ETrue);
+		if(iCurrentPlugin && IsPostOperation())
+			SetPostOperation(EFalse);
+		Dispatch();
+		return;
+		}
+	// KErrCompletion may be returned by the plugin to 
+	// indicate that it has process the message itself (do post-intercept)
+	else if (err == KErrCompletion)
+		{
+		// Find the previous plugin in the chain and dispatch
+		//  - If no more plugins are interested in this message, then complete
+		FsPluginManager::PrevPlugin(iCurrentPlugin, this,(TBool)ETrue);
+		if(iCurrentPlugin != NULL)
+			{
+			SetPostOperation(ETrue);
+			err = KErrNone;
+			Dispatch();
+			return;
+			}
+		else
+			{
+			err = KErrNone;	
+			}
+		}
+		
+	Complete(err);
+	return;
+	}
+
+
+void CFsMessageRequest::ProcessDriveOperation()
+//
+// Process the message in drive (or main thread) context
+//
+	{
+
+	// A new request is to be processed - kick off the inactivity/finalisation timer...
+	FsThreadManager::StartFinalisationTimer(iDriveNumber);
+
+	TInt err = KErrNone;
+	
+	TRAPD(leaveValue, err = iOperation->DoRequestL(this));
+
+	// Cancel hung state if a request from the drive thread has finished
+	FsThreadManager::SetDriveHung(DriveNumber(), EFalse);
+
+	if(leaveValue != KErrNone)
+		{
+		Panic(KFsClient,leaveValue);
+		if(iOperation->IsOpenSubSess()) 
+			RequestAllocator::OpenSubFailed(Session());
+		Free();
+		return;
+		}
+		
+	// request postponed ? (e.g. if a page used by file cache is busy,
+	// or because of fair scheduling).
+	if (err == EReqActionBusy)
+		{
+		Dispatch();
+		return;
+		}
+
+	iLastError = err;
+
+	if(!IsExpectedResult(err) || IsPluginSpecific())
+		{
+		// no need to call DoNotify here since that requires err==KErrNone
+		Complete(err);
+		return;
+		}
+
+	SetError(err);
+
+
+	// Start issuing the post-operation requests starting from the bottom of the chain
+	iCurrentPlugin = NULL;
+	if (PostInterceptEnabled())
+		{
+		FsPluginManager::PrevPlugin(iCurrentPlugin, this,(TBool)ETrue);
+		if(iCurrentPlugin && !iCurrentPlugin->IsPluginThread(*this))
+			{
+			SetPostOperation(ETrue);
+			if (DispatchToPlugin())
+				return;
+			}
+		}		
+
+	Complete(GetError());
+	return;
+	}
+
+void CFsMessageRequest::Complete(TInt aError)
+//
+//
+//
+	{
+	__THRD_PRINT2(_L("----- CFsMessageRequest::Complete() req %08x with %d"), this, aError);
+
+	if (aError==KErrNoMemory)
+		{
+		if (iDrive)	// Not all message requests are associated with a drive!
+			{
+			TDriveInfo di;
+			iDrive->DriveInfo(di);
+			if (di.iType == EMediaRam)
+				aError = KErrNoMemory;
+			}
+		}
+	if(aError!=KErrNone)
+		{
+		if(iOperation->IsOpenSubSess())
+			RequestAllocator::OpenSubFailed(Session());
+		}
+
+	iLastError = aError;
+
+	// Call the current MessageOp's completion routine - if this returns EReqActionComplete, 
+	// then pop the request and call the next MessageOps's completion routine.
+	// Do this until there are no more MessageOp's on the stack.
+	//
+	// Completion return codes:
+	// EReqActionOwnedByPlugin:			DON'T dispatch message again, DON'T complete message 
+	// EReqActionComplete:				DON'T dispatch message again, DO complete message
+	// EReqActionContinue:				DO dispatch message again, DON'T complete message
+	// EReqActionBusy					DO dispatch message again, DON'T complete message, 
+	TInt completeErr;
+
+	for (completeErr = EReqActionComplete; CurrentOperationPtr() != NULL && completeErr == EReqActionComplete; )
+		{
+		// Get the completion routine from the current TMsgOperation and then pop it
+		TFsRequestFunc doComplete = CurrentOperation().iComplete;
+		PopOperation();
+		
+		// Check that a completion routine is supplied if this isn't the last TMsgOperation
+		__ASSERT_ALWAYS(iCurrentOperation == NULL || doComplete != NULL, Fault(EBadOperationIndex));
+
+		// Call the completion routine
+		if (doComplete)
+			completeErr = doComplete(this);
+
+		// Check return code is valid
+		__ASSERT_DEBUG(completeErr == EReqActionContinue || completeErr == EReqActionBusy || completeErr == EReqActionComplete || completeErr == EReqActionOwnedByPlugin, Fault(EBadOperationCompletionCode));
+		}
+
+	// a set of flags to determine what actions to take for each return code
+	enum 
+		{
+		EDispatch			= 0x01,	// dispatch request
+		EComplete			= 0x02,	// complete request
+		EFree				 =0x04,	// free request
+		EResetPostInitialised	= 0x08,	// call iPostInitialise again
+		EDispatchToFront	= 0x10	// dispatch to back of drive thread queue
+		};
+	const TUint actions[8] = 
+		{
+		// 0 - EReqActionContinue
+		EDispatch,											
+		// 1 - unused
+		0,
+		// 2 - unused
+		0,
+		// 3 - unused
+		0,
+		// 4 - EReqActionCompleteAndDispatch
+		EDispatch | EComplete | EResetPostInitialised | EDispatchToFront,
+		// 5 - EReqActionOwnedByPlugin
+		0,													
+		// 6 - EReqActionBusy
+		EDispatch | EResetPostInitialised,					
+		// 7 - EReqActionComplete
+		EComplete | EFree,
+		};
+
+	TUint actionFlags = actions[((TUint32) completeErr) & 0x00000007];
+
+	// To dispatch to drive thread and intercept before DoRequestL() 
+	// we must re-call iPostInitialise(), so reset iPostInitialised flag
+	if (actionFlags & EResetPostInitialised)
+		{
+		// can only postpone the request if there's a PostInitialise() function
+		__ASSERT_DEBUG(iOperation->iPostInitialise , Fault(EBadOperationCompletionCode));
+		SetState(EReqStatePostInitialise);
+		}
+
+
+
+	TInt dispatchError = KErrNone;
+	TBool completeMessage = (iLastError != KErrNone) || (iIsCompleted && (actionFlags & EComplete));
+
+	// Need to lock drive to prevent both file server and drive thread from completing the same message (!)
+	// or the dispatched thread from freeing the message before the first thread has completed it
+	TDrive* drive = iDrive;
+	if (drive) 
+		drive->Lock();
+
+	// don't go back up plugin chain once message has been completed
+	if (completeMessage)
+		EnablePostIntercept(EFalse);
+
+	if (actionFlags & EDispatch)
+		{
+		__ASSERT_DEBUG(((actionFlags & EFree) == 0), Fault(EInvalidCompletionFlags));
+		__ASSERT_DEBUG (iCurrentPlugin == NULL, Fault(EInvalidMsgState));
+		dispatchError = DispatchToDrive(EFalse, actionFlags & EDispatchToFront);
+		}
+
+	
+	if ((actionFlags & EComplete) && IsExpectedResult(iLastError) && !IsPluginSpecific() && !IsNotifierSpecific())
+		DoNotify(KErrNone);
+
+
+	if (completeMessage)
+		{
+		iIsCompleted = EFalse;
+		TInt msgHandle = Message().Handle();
+		if ((msgHandle != KLocalMessageHandle) && (msgHandle != 0))
+			Message().Complete(iLastError);
+		if (IsPluginRequest())
+			CFsPlugin::Complete(this, iLastError);
+		}
+
+	if(iOperation->Function() == EFsFileSubClose)
+		{
+		HBufC* pFileName = (HBufC*) I64HIGH(ScratchValue64());
+		if(pFileName)
+			{
+			delete pFileName;
+			SetScratchValue(NULL);
+			}
+		}
+
+	if (drive) 
+		drive->UnLock();
+
+	if (actionFlags & EFree)
+		Free();
+
+	// if the dispatch failed, then continue with popping MessageOps from the stack.
+	if (dispatchError != KErrNone)
+		Complete(dispatchError);
+	}
+
+void CFsMessageRequest::DoNotify(TInt aError)
+//
+//
+//
+	{
+	__PRINT1(_L("----- CFsMessageRequest::DoNotify() with %d"),aError);
+
+	TInt driveNumber = DriveNumber();
+
+	if(aError==KErrNone)
+		{
+		if(!(FsNotify::IsChangeQueEmpty(driveNumber)))
+			FsNotify::HandleChange(this,driveNumber);	
+		if ((driveNumber != KDriveInvalid) && !(FsNotify::IsDiskSpaceQueEmpty(driveNumber)))
+			FsNotify::HandleDiskSpace(this, DriveNumber());
+	
+#ifdef SYMBIAN_F32_ENHANCED_CHANGE_NOTIFICATION
+		if 	(iOperation->iFunction == EFsFileWrite)
+			{
+			CFileShare* share = (CFileShare*) this->ScratchValue();
+			CFileCB* file = &share->File();
+			CFileCache* fileCache = file->FileCache();
+			
+			// Manage notifications for write with no cache or a write-through
+			if (!fileCache || !fileCache->IsDirty())
+				{
+				FsNotificationManager::HandleChange((CFsClientMessageRequest&)*this);
+				}
+			}
+		else if((iOperation->iFunction == EFsFileWriteDirty) && FsNotificationManager::IsInitialised())
+			{
+			CFileShare* share;
+			CFileCB* file;
+			GetFileFromScratch(this, share, file);
+
+			TFileName path;
+			path.Append(file->DriveNumber() + 'A');
+			path.Append(':');
+			path.Append(file->FileName().Des());
+			
+			// Manage notifications for write with caching enabled
+			FsNotificationManager::HandleChange((CFsClientMessageRequest*)this, path, TFsNotification::EFileChange);
+			}
+		else if(IsNotifierSupported())
+			{
+			FsNotificationManager::HandleChange((CFsClientMessageRequest&)*this);
+			}
+#endif //SYMBIAN_F32_ENHANCED_CHANGE_NOTIFICATION
+		}
+	}
+
+
+void CFsMessageRequest::Free()
+//
+//
+//
+	{
+	__THRD_PRINT1(_L("CFsMessageRequest::Free() isAllocated=%d"), IsAllocated());
+
+	SetScratchValue(0);	// this should close the CFsObject
+
+	if(!IsAllocated())
+		delete(this);
+	else iOperation = NULL;
+
+	}
+
+TInt CFsMessageRequest::DoInitialise()
+//
+// returns KErrNone if Initialise succeeded normally
+// or      EReqActionComplete if request completed already
+// or      EReqActionBusy if request needs dispatching & initialising again
+//
+	{
+	TInt r = KErrNone;
+
+	SetState(iOperation->iPostInitialise ? EReqStatePostInitialise : EReqStateDoRequest);
+	r=Initialise();
+	if(r==KErrBadHandle)
+		{
+		// bad subsession handle so panic client
+		_LIT(KPanic,"Panic");
+		Panic(KPanic,r);
+		if(iOperation->IsOpenSubSess())			//this should be close subsession
+			RequestAllocator::OpenSubFailed(Session());	//need a close subsession fail
+		Free();
+		r = EReqActionComplete;
+		}
+	else if (r == EReqActionComplete)	// completed synchronously in Initialise() function ?
+		{
+		Complete(KErrNone);
+		}
+	else if (r == EReqActionBusy || r == EReqActionPending)	// request postponed or owned by file share ?
+		{
+		}
+	else if (r != KErrNone)			// error 
+		{
+		Complete(r);
+		r = EReqActionComplete;
+		}
+
+	return r;
+	}
+
+TInt CFsMessageRequest::PostInitialise()
+//
+// returns KErrNone if PostInitialise() succeeded normally
+// or      EReqActionComplete if request completed already
+// or      EReqActionBusy if request needs dispatching & initialising again
+//
+	{
+	TInt r = KErrNone;
+
+	SetState(EReqStateDoRequest);
+	if (iOperation->iPostInitialise)
+		r = iOperation->iPostInitialise(this);
+	if (r == EReqActionComplete)		// completed early ?
+		{
+
+		// Start issuing the post-operation requests starting from the bottom of the chain
+		iCurrentPlugin = NULL;
+		if (PostInterceptEnabled())
+			{
+			FsPluginManager::PrevPlugin(iCurrentPlugin, this,(TBool)ETrue);
+			if(iCurrentPlugin && !iCurrentPlugin->IsPluginThread(*this))
+				{
+				SetPostOperation(ETrue);
+				Dispatch();
+				return r;	// EReqActionComplete
+				}
+			}		
+
+		Complete(KErrNone);
+		}
+	else if (r == EReqActionBusy)	// request postponed ?
+		{
+		}
+	else if (r != KErrNone)			// error 
+		{
+		Complete(r);
+		r = EReqActionComplete;
+		}
+
+	return r;
+	}
+
+
+// CFsMessageRequest::Dispatch()
+//
+// If aInitialise is EFalse, just disptach request to appropriate thread - 
+// don't call DoInitialise() or PostInitialise()
+//
+void CFsMessageRequest::Dispatch(TBool aInitialise, TBool aLowPriority, TBool aDispatchToFront)
+	{
+	__THRD_PRINT1(_L("CFsMessageRequest::Dispatch() req %08x"), this);
+
+	TInt r = KErrNone;
+
+	if (iReqState == EReqStateInitialise && aInitialise)
+		{
+		r = DoInitialise();
+		if (r == EReqActionComplete)
+			{
+			return;
+			}
+		else if (r == EReqActionBusy)		// request postponed ?
+			{
+			SetState(EReqStateInitialise);	// reinitialize when request is next processed
+			}
+		else if (r == EReqActionPending)	// owned by file share ?
+			{
+			SetState(EReqStateInitialise);	// reinitialize when request is next processed
+			return;
+			}
+		if(!IsPluginSpecific() && (iOwnerPlugin == NULL))
+			{
+			iCurrentPlugin = NULL;
+			iClientThreadId = 0;
+			FsPluginManager::NextPlugin(iCurrentPlugin, this, (TBool)ETrue);
+
+			// find out whether there is a plugin registered to post intercept this message
+			CFsPlugin* postInterceptPlugin = NULL;
+			if (iCurrentPlugin == NULL)
+				FsPluginManager::PrevPlugin(postInterceptPlugin, this, (TBool)ETrue);
+
+			// Save the client's thread Id for subsequent testing by CFsPlugin::IsPluginThread() - doing so on the fly 
+			// is risky because some messages are completed early in which case Message().Client() will result in a panic
+			if ((iCurrentPlugin || postInterceptPlugin) && Message().Handle() != NULL && Message().Handle() != KLocalMessageHandle)
+				{
+				RThread thread;
+				Message().Client(thread, EOwnerThread);
+				iClientThreadId = thread.Id();
+				thread.Close();
+				}
+			} 
+		} // initialise
+		
+
+		// dispatch to plugin thread if initialised (otherwise dispatch to drive thread)
+	if (iReqState > EReqStateInitialise && DispatchToPlugin())
+		{
+		__PLUGIN_PRINT1(_L("PLUGIN: CFsMessageRequest %x dispatched to plugin (async)"), this);
+		// The request has been delivered to the plugin thread
+		//  - leave the main thread now and await asynchronous completion
+		return;
+		}
+
+
+	// Is there a PostInitialise function ?
+	if (iReqState ==  EReqStatePostInitialise && aInitialise && r == KErrNone)
+		{
+		TInt r = PostInitialise();
+		if (r == EReqActionComplete)
+			return;
+		else if (r == EReqActionBusy)				// request postponed ?
+			SetState(EReqStatePostInitialise);		// reinitialize when request is next processed
+		}
+
+
+	if(!IsSeparateThread() || FsThreadManager::IsDriveSync(DriveNumber(),EFalse))
+		{
+		__PLUGIN_PRINT1(_L("PLUGIN: CFsMessageRequest %x dispatched to plugin (sync)"), this);
+		FsPluginManager::DispatchSync(this);
+		return;
+		}
+
+	r = DispatchToDrive(aLowPriority, aDispatchToFront);
+	if (r != KErrNone)
+		Complete(r);
+	}
+
+inline TInt CFsMessageRequest::DispatchToDrive(TBool aLowPriority, TBool aDispatchToFront)
+	{
+	TInt drivenumber = DriveNumber();
+	
+	if (drivenumber < EDriveA || drivenumber > EDriveZ)
+		return KErrNotSupported;
+	
+	FsThreadManager::LockDrive(drivenumber);
+
+	CDriveThread* dT=NULL;
+	TInt r = FsThreadManager::GetDriveThread(drivenumber,&dT);
+
+	if(r == KErrNone)
+		{
+		CRequestThread* pT = (CRequestThread*)dT;
+
+		if (FsThreadManager::IsDriveHung(drivenumber))
+			r = KErrNotReady;
+		else if (aDispatchToFront)
+			pT->DeliverFront(this);
+		else
+			pT->DeliverBack(this, aLowPriority);
+		}
+	else if (r == KErrAccessDenied)
+		{
+		Process();
+		}
+
+	FsThreadManager::UnlockDrive(drivenumber);
+
+	return r;
+	}
+
+void CFsMessageRequest::Dispatch()
+	{
+	Dispatch(ETrue);
+	}
+
+TBool CFsMessageRequest::DispatchToPlugin()
+//
+//
+//
+	{
+	TInt drivenumber = DriveNumber();
+	if(iCurrentPlugin)
+		{		
+		FOREVER
+			{
+			// if we've reached a plugin which is waiting for Complete(), stop going up the plugin chain
+			if(drivenumber >= 0 && IsPostOperation() && CurrentPluginWaiting())
+				{
+				// No more plug-ins need to process this request
+				iCurrentPlugin = NULL;
+				}
+
+			while(iCurrentPlugin && iCurrentPlugin->IsPluginThread(*this))
+				{
+				// Skip the current plugin if the request originated from the plugin
+				if(IsPostOperation())
+					{
+					FsPluginManager::PrevPlugin(iCurrentPlugin, this,(TBool)ETrue);
+					}
+				else
+					{
+					FsPluginManager::NextPlugin(iCurrentPlugin, this,(TBool)ETrue);
+					}
+				}
+				
+			if(iCurrentPlugin)
+				{
+				TFsPluginRequest request(this);
+				 __THRD_PRINT1(_L("CFsMessageRequest::DispatchToPlugin() req %08x"), this);
+
+				TInt err = iCurrentPlugin->Deliver(request);
+				if(err == KErrNone)
+					{
+
+					// The request has been delivered to the plugin thread
+					//  - leave the main thread now
+					return(ETrue);
+					}
+				else if(err < KErrNone)
+					{
+					// An error has occurred
+					//  - complete the message
+					Complete(err);
+					return(ETrue);
+					}
+				else if(err == KPluginMessageForward)
+					{
+					// The plugin has processed synchronously (case 1)
+					//  - Pass the message on to the next plugin
+					FsPluginManager::NextPlugin(iCurrentPlugin, this,(TBool)ETrue);
+					continue;
+					}
+				else if(err == KPluginMessageComplete)
+					{
+					// The plugin has processed synchronously (case 2)
+					//  - Pass the message back up the stack
+					SetPostOperation(ETrue);
+					FsPluginManager::PrevPlugin(iCurrentPlugin, this,(TBool)ETrue);
+					continue;
+					}
+				_LIT(KPanic,"Panic: F32-BAD-PLUGIN-ERROR");
+				Panic(KPanic, err);
+				}
+			else if (IsPostOperation())
+				{
+				// No more plugins are interested in this request
+				//	 - If in post operation, complete the request
+				Complete(GetError());
+				return(ETrue);
+				}
+			else
+				{
+				// No plugin is registered for pre-operation interception this message
+				//	- pass the request onto the drive thread.
+				return EFalse;
+				}
+			}			
+		}
+
+	if(iOperation->Function() == EFsDismountPlugin)
+		{
+		// Don't pass plugin dismounts to the drive thread
+		Process();
+		return(ETrue);
+		}
+		
+	return EFalse;
+	}
+
+TDrive* CFsMessageRequest::Drive()
+//
+//
+//
+	{
+	return(iDrive);
+	}
+
+TDrive* CFsMessageRequest::SubstedDrive()
+//
+//
+//
+	{
+	return(iSubstedDrive);
+	}
+
+void CFsMessageRequest::SetDrive(TDrive* aDrive)
+//
+//
+//
+	{
+	iDrive=aDrive;
+	if(aDrive)
+		{
+		iDriveNumber=aDrive->DriveNumber();
+		}
+	}
+
+void CFsMessageRequest::SetSubstedDrive(TDrive* aDrive)
+//
+//
+//
+	{
+	iSubstedDrive=aDrive;
+	}
+
+const RMessage2& CFsMessageRequest::Message()
+//
+//
+//
+	{
+	return(iMessage);
+	}
+
+
+
+void CFsMessageRequest::ReStart()
+	{
+	__ASSERT_ALWAYS(CurrentOperationPtr() != NULL, Fault(EBadOperationIndex));
+
+	// restore original settings :
+	TMsgOperation& currentOperation = CurrentOperation();
+	TInt offset = currentOperation.iReadWriteArgs.iOffset;
+	currentOperation.iReadWriteArgs.iPos-= offset;
+	currentOperation.iReadWriteArgs.iTotalLength+= offset;
+	currentOperation.iReadWriteArgs.iLength = currentOperation.iReadWriteArgs.iTotalLength;
+	currentOperation.iReadWriteArgs.iOffset = 0;
+	currentOperation.iState = 0;
+	TInt function = iOperation->Function();
+	__ASSERT_ALWAYS(function == EFsFileRead || function == EFsFileWrite,  Fault(EMsgRestartBadFunction));
+	}
+
+
+void CFsMessageRequest::SetOperationFunc(TInt aFunction)
+	{
+	const TOperation& oP = OperationArray[aFunction];
+	Set(oP);
+	// modified because some requests were set to PostInitialise. They are set to DoRequest in Dispatch anyway, so this is possibly ok?
+	//__ASSERT_ALWAYS(iReqState == EReqStateDoRequest, Fault(EInvalidMsgState));
+	}
+
+TInt CFsMessageRequest::PushOperation(TFsRequestFunc aCallback, TInt aNextState, TInt aFunction)
+	{
+	TMsgOperation* nextOperation;
+	TInt r = RequestAllocator::GetOperation(nextOperation);
+	if (r != KErrNone)
+		return r;
+
+	
+	// Store the caller's state etc in the CURRENT TMsgOperation (if there is one)
+	if (iCurrentOperation != NULL)
+		{
+		__ASSERT_ALWAYS(aCallback, Fault(EPushOpNoCallback));
+		iCurrentOperation->iState = aNextState;
+		iCurrentOperation->iCurrentPlugin = iCurrentPlugin;
+		iCurrentOperation->iFunction = iOperation->Function();
+		iCurrentOperation->iNext = nextOperation;
+		nextOperation->iPrev = iCurrentOperation;
+		}
+
+	// store the call-back routine in the NEW TMsgOperation
+	nextOperation->iComplete = aCallback;
+	nextOperation->iState = 0;
+	if (aFunction != KOperationFunctionUnaltered && aFunction != iOperation->Function())
+		SetOperationFunc(aFunction);
+
+
+	iCurrentOperation = nextOperation;
+	
+	// reset post operation state: as a plugin may push more than one TMsgOperation
+	SetPostOperation(EFalse);
+
+	return KErrNone;
+	}
+
+/**
+PushOperation()
+
+Pushes a new TMsgOperation onto this request's "stack". After this function completes the new 
+TMsgOperation object becomes the "current" one (pointed at by CFsMessageRequest::iCurrentOperation). 
+
+After the request has been dispatched to the drive thread, TOperation::DoRequestL() will be called.
+When this has completed, CFsMessage::Complete() is called which then pops the new TMsgOperation 
+off the stack; and calls the callback routine (aCallback).
+
+@param aPos			The position to read / write. Stored in the NEW TMsgOperation.
+
+@param aLength		The length to read / write. Stored in the NEW TMsgOperation.
+
+@param aData		A buffer to read the data from / write the data to. Stored in the NEW TMsgOperation.
+					The buffer must belong to the file server or to a plugin
+
+@param aOffset		The offset onto the buffer to read from / write to. Stored in the NEW TMsgOperation.
+
+@param aCallback	An optional callback routine. Stored in the OLD TMsgOperation.
+					MUST be supplied if there are one or more TMsgOperation's already on the stack
+
+@param aNextState	State information private to the caller. Defaults to zero. Stored in the OLD TMsgOperation.
+
+@param aFunction	An optional TFsMessage. Supplied if the caller wishes to change the TOperation
+					associated with this request. When the new TMsgOperation is popped off the stack, the
+					previous TOperation is restored. Used primarally for read-modify-write.
+					
+*/
+TInt CFsMessageRequest::PushOperation(TInt64 aPos, TInt aLength, TUint8* aData, TInt aOffset, TFsRequestFunc aCallback, TInt aNextState, TInt aFunction)
+	{
+	TInt r = PushOperation(aCallback, aNextState, aFunction);
+	if (r == KErrNone)
+		CurrentOperation().Set(aPos, aLength, aData, aOffset, 0);
+	return r;
+	}
+
+
+/**
+PushOperation()
+
+Pushes a new TMsgOperation onto this request's "stack". After this function completes the new 
+TMsgOperation object becomes the "current" one (pointed at by CFsMessageRequest::iCurrentOperation). 
+
+After the request has been dispatched to the drive thread, TOperation::DoRequestL() will be called.
+When this has completed, CFsMessage::Complete() is called which then pops the new TMsgOperation 
+off the stack; and calls the callback routine (aCallback).
+
+@param aPos			The position to read / write. Stored in the NEW TMsgOperation.
+
+@param aLength		The length to read / write. Stored in the NEW TMsgOperation.
+
+@param aData		A buffer to read the data from / write the data to. Stored in the NEW TMsgOperation.
+					The buffer belongs to a client of the file server on the other side of an IPC boundary.
+
+@param aOffset		The offset onto the buffer to read from / write to. Stored in the NEW TMsgOperation.
+
+@param aCallback	An optional callback routine. Stored in the OLD TMsgOperation.
+					MUST be supplied if there are one or more TMsgOperation's already on the stack
+
+@param aNextState	State information private to the caller. Defaults to zero. Stored in the OLD TMsgOperation.
+
+@param aFunction	An optional TFsMessage. Supplied if the caller wishes to change the TOperation
+					associated with this request. When the new TMsgOperation is popped off the stack, the
+					previous TOperation is restored. Used primarally for read-modify-write.
+					
+*/
+TInt CFsMessageRequest::PushOperation(TInt64 aPos, TInt aLength, TDesC8* aData, TInt aOffset, TFsRequestFunc aCallback, TInt aNextState, TInt aFunction)
+	{
+	TInt r = PushOperation(aCallback, aNextState, aFunction);
+	if (r == KErrNone)
+		CurrentOperation().Set(aPos, aLength, aData, aOffset, 0);
+	return r;
+	}
+
+void CFsMessageRequest::PopOperation()
+	{
+	__ASSERT_ALWAYS(iCurrentOperation != NULL, Fault(EInvalidOperationIndex));
+	
+	TMsgOperation* currentOperation = iCurrentOperation;
+	
+	if (iCurrentOperation->iPrev == NULL)
+		{
+		iCurrentOperation = NULL;
+		}
+	else
+		{
+		TMsgOperation* prevOperation = iCurrentOperation->iPrev;
+		prevOperation->iNext = NULL;
+		iCurrentOperation = prevOperation;
+
+		iCurrentPlugin = iCurrentOperation->iCurrentPlugin;
+		
+		if (iCurrentOperation->iFunction != iOperation->Function())
+			SetOperationFunc(iCurrentOperation->iFunction);
+		}
+
+	RequestAllocator::FreeOperation(currentOperation);
+	}
+
+TMsgOperation& CFsMessageRequest::CurrentOperation()
+	{
+	__ASSERT_ALWAYS(iCurrentOperation != NULL, Fault(EInvalidOperationIndex));
+	return *iCurrentOperation;
+	}
+
+
+CFsClientMessageRequest::CFsClientMessageRequest()
+: iPoolSrc (0),
+  iPoolDest(0)
+	{
+	}
+
+// Return ETrue if there are ANY TMsgOperation's in the stack which were pushed by iCurrentPlugin
+// If there are any, then it's NOT OK to dispatch this message to the plugin thread as it will be 
+// waiting on a semaphore which is only cleared by Complete()
+TBool CFsMessageRequest::CurrentPluginWaiting()
+	{
+	if (iOwnerPlugin == iCurrentPlugin)
+		return ETrue;
+
+	// find out which plugin (if any) was the last to push a TMsgOperation
+	TMsgOperation* currentOperation;
+
+	for (currentOperation = iCurrentOperation ? iCurrentOperation->iPrev : NULL;
+		 currentOperation != NULL;
+		 currentOperation = currentOperation->iPrev)
+			{
+			if (currentOperation->iCurrentPlugin == iCurrentPlugin)
+				return ETrue;
+			}
+
+	return EFalse;
+	}
+
+
+TBool CFsMessageRequest::IsPluginRequest()
+	{
+	if(iOwnerPlugin) // Check the previous operation had a plugin)
+		{
+		return ETrue;
+		}
+	return EFalse;
+	}
+
+
+void CFsClientMessageRequest::Free()
+//
+//
+//
+	{
+	__THRD_PRINT(_L("CFsClientMessageRequest::Free()"));
+	__ASSERT_ALWAYS(CurrentOperationPtr() == NULL, Fault(EFreeingMsgWithMsgOp));
+	if (iPoolSrc)
+		{
+		TParsePool::Release(iPoolSrc);
+		iPoolSrc = 0;
+		}
+
+	if (iPoolDest)
+		{
+		TParsePool::Release(iPoolDest);
+		iPoolDest = 0;
+		}
+
+	SetScratchValue(0);	// this should close the CFsObject
+	iOperation = NULL;
+	RequestAllocator::FreeRequest(this);
+	}
+
+
+TInt CFsClientMessageRequest::AllocParseObjects(const TOperation& aOperation)
+	{
+	__ASSERT_ALWAYS(iPoolSrc == NULL && iPoolDest == NULL, Fault(ETParsePoolGet));
+
+	if (aOperation.iFlags & EParseSrc)
+		{
+		iPoolSrc = TParsePool::Get();
+		if (iPoolSrc == NULL)
+			return KErrNoMemory;
+		}
+	if (aOperation.iFlags & EParseDst)
+		{
+		iPoolDest = TParsePool::Get();
+		if (iPoolDest == NULL)
+			return KErrNoMemory;
+		}
+	return KErrNone;
+	}
+
+TParse& CFsClientMessageRequest::Src()
+//
+//
+	{
+	__ASSERT_ALWAYS(iPoolSrc, Fault(ETParsePoolGet));
+	return (iPoolSrc->GetObject());
+	}
+
+TParse& CFsClientMessageRequest::Dest()
+//
+//
+//
+	{
+	__ASSERT_ALWAYS(iPoolDest, Fault(ETParsePoolGet));
+	return (iPoolDest->GetObject());
+	}
+
+
+
+TParsePool* TParsePool::iFreeHead   = 0;
+TParsePool* TParsePool::iClosedHead = 0;
+TInt        TParsePool::iCountFree  = 0;
+RFastLock	TParsePool::iLock;
+
+
+TParsePool::TParsePool()
+:	iNext   (iFreeHead),
+	iPrev   (0),
+	iFree   (ETrue)
+	{
+	}
+
+TInt TParsePool::Init()
+	{
+	return iLock.CreateLocal();
+	}
+
+TParsePool* TParsePool::Get()
+// Gets a TParsePool object from the free list. If the free list does 
+// not contain any TParsePool objects, additional objects are manufactured.
+// Returns NULL if unable to allocate a new object
+	{
+	iLock.Wait();
+
+	TParsePool* pObject = 0;
+
+	// If we don't have anything in the free list, allocate some more...
+	if (iCountFree < 1)
+		{    
+		__ASSERT_DEBUG(iFreeHead == NULL, Fault(ETParsePoolGet));
+		for (TInt i = 0; i < KEBlockSize; i++)
+			{
+			// if allocation fails break out of the loop.
+			pObject = new TParsePool;
+			if (pObject == NULL)
+				break;
+			
+			iCountFree++;
+			
+			if (iFreeHead)
+				iFreeHead->iPrev = pObject;
+
+			iFreeHead = pObject;
+			}
+		}
+
+	// if we failed to allocate even a single object, return NULL
+	if (iCountFree < 1)
+		{
+		iLock.Signal();
+		return NULL;
+		}
+
+
+	__ASSERT_ALWAYS(iFreeHead != NULL, Fault(ETParsePoolGet));
+
+	pObject = iFreeHead;
+	pObject->iFree = EFalse;
+
+
+	// Remove the head of the free list...
+	iFreeHead = pObject->iNext;
+	if (iFreeHead)
+		iFreeHead->iPrev = 0;
+	iCountFree--;
+
+	// ... and add it to the closed list
+	pObject->iNext = iClosedHead;
+	pObject->iPrev = 0;
+	if (iClosedHead)
+		iClosedHead->iPrev = pObject;
+	iClosedHead = pObject;
+
+	iLock.Signal();
+
+	return pObject;
+	}
+
+
+void TParsePool::Release(TParsePool* aObject)
+//
+// Release a sub-sequence of TParsePool objects back to the free list.
+//
+	{
+	__ASSERT_ALWAYS(aObject != NULL && !aObject->iFree, Fault(ETParsePoolGet));
+
+	iLock.Wait();
+
+	aObject->iFree = ETrue;
+
+	// Get the objects either side of the one we're interested in
+	TParsePool* pPrevious = aObject->iPrev;
+	TParsePool* pNext     = aObject->iNext;
+
+	// Remove it from the closed list
+	if (pPrevious)
+		pPrevious->iNext = pNext;
+	else
+		iClosedHead = pNext;
+
+	if (pNext)
+		pNext->iPrev = pPrevious;
+
+
+	// Now add it to the free list
+	aObject->iNext = iFreeHead;
+	aObject->iPrev = 0;
+	if (iFreeHead)
+		iFreeHead->iPrev = aObject;
+
+	iFreeHead = aObject;
+	iCountFree++;
+
+	iLock.Signal();
+	}
+
+
+CFsInternalRequest::CFsInternalRequest()
+//
+//
+//
+	{
+	// iStatus=NULL;
+	// iIsAllocated=EFalse;
+	}
+
+void CFsInternalRequest::Set(const TOperation& aOperation,CSessionFs* aSession)
+//
+//
+// 
+	{
+	CFsRequest::Set(aOperation,aSession);
+	}
+
+void CFsInternalRequest::Process()
+//
+//
+//
+	{
+	__THRD_PRINT(_L("CFsInternalRequest::Process()"));
+	TInt r=KErrNone;
+	TRAPD(leaveValue,r=iOperation->DoRequestL(this));
+	// internal requests should never fail
+	__ASSERT_ALWAYS(leaveValue==KErrNone && (r==KErrNone||r==EReqActionBusy),Fault(EInternalRequestProcess));
+
+	// request postponed ? (e.g. if a page used by file cache is busy).
+	if (r == EReqActionBusy)
+		{
+		Dispatch();
+		return;
+		}
+
+	Complete(r);
+	}
+
+
+void CFsInternalRequest::Complete(TInt aError)
+//
+//
+//
+	{
+	__PRINT1(_L("CFsInternalRequest::Complete() with %d"),aError);
+	TInt func = Operation()->Function();
+	if(func==KCancelSession || func==KCancelPlugin || func==KFlushDirtyData)
+		{
+		__ASSERT_DEBUG(ThreadHandle()!=0 && !FsThreadManager::IsDisconnectThread(),Fault(EInternalRequestComplete1));
+		RThread t;
+		t.SetHandle(ThreadHandle());
+		TRequestStatus* s=&Status();
+		t.RequestComplete(s,aError);
+		Free();
+		}
+	else if(func == KDispatchObjectClose)
+		{
+		TFsCloseObject::Complete(this);
+		Free();
+		}
+	else if(func==KFileShareClose)
+		{
+		if (aError == EReqActionBusy)
+			{
+			Dispatch();
+			}
+		else
+			{
+			TFsCloseFileShare::Complete(this);
+			Free();
+			}
+		}
+	else
+		Fault(EInternalRequestComplete3);
+	}
+
+void CFsInternalRequest::Dispatch()
+//
+//
+//
+	{
+	__THRD_PRINT(_L("CFsInternalRequest::Dispatch()"));
+	__ASSERT_ALWAYS(Initialise()==KErrNone,Fault(EInternalRequestDispatch1));
+
+	if(iCurrentPlugin && Operation()->Function() == KCancelPlugin)
+		{
+		TFsPluginRequest request(this);
+		TInt r = iCurrentPlugin->Deliver(request);
+		__ASSERT_ALWAYS(r == KErrNone, Fault(EInternalRequestDispatchCancelPlugin));
+		}
+	else
+		{
+		TInt drivenumber = DriveNumber();
+		FsThreadManager::LockDrive(drivenumber);
+		// shouldn't dispath if no drive available
+		__ASSERT_ALWAYS(FsThreadManager::IsDriveAvailable(drivenumber,EFalse) && !FsThreadManager::IsDriveSync(drivenumber,EFalse),Fault(EInternalRequestDispatch2));
+		CDriveThread* dT=NULL;
+		TInt r=FsThreadManager::GetDriveThread(drivenumber,&dT);
+		__THRD_PRINT2(_L("deliver to thread 0x%x, drive number %d"),dT,drivenumber);
+		__ASSERT_ALWAYS(r==KErrNone && dT,Fault(EInternalRequestDispatch3));
+		CRequestThread* pT = (CRequestThread*)dT;
+		TInt func = Operation()->Function();
+		if(func == KDispatchObjectClose || func == KFileShareClose || func == KFlushDirtyData)
+			pT->DeliverBack(this);
+		else
+			pT->DeliverFront(this);
+		FsThreadManager::UnlockDrive(drivenumber);
+		}
+	}
+
+void CFsInternalRequest::Free()
+//
+//
+//
+	{
+	__THRD_PRINT1(_L("CFsInternalRequest::Free() isAllocated=%d"),IsAllocated());
+
+	SetScratchValue(0);	// this should close the CFsObject
+
+	if(!IsAllocated())
+		delete(this);
+	}
+
+void CFsDisconnectRequest::Dispatch()
+//
+//
+//
+	{
+	__THRD_PRINT(_L("CFsDisconnectRequest::Dispatch()"));
+	// no need to lock
+	TInt r=Initialise();
+	__ASSERT_ALWAYS(r==KErrNone,Fault(EDisconnectRequestDispatch1));
+	CRequestThread* pT=FsThreadManager::GetDisconnectThread();
+	__ASSERT_ALWAYS(pT,Fault(EDisconnectRequestDispatch2));
+	pT->DeliverBack(this);
+	}
+
+void CFsDisconnectRequest::Process()
+//
+//
+//
+	{
+	__THRD_PRINT(_L("CFsDisconnectRequest::Process()"));
+	TInt r=KErrNone;
+	TRAPD(leaveValue,r=iOperation->DoRequestL(this));
+	leaveValue=leaveValue; // just to make compiler happy
+	__ASSERT_DEBUG(leaveValue==KErrNone && r==KErrNone,Fault(EDisonncectRequestProcess));
+	Complete(r);
+	}
+
+void CFsDisconnectRequest::Complete(TInt aError)
+//
+//
+//
+	{
+	__PRINT1(_L("CFsDisconnectRequest::Complete() with %d"),aError);
+	__ASSERT_ALWAYS(aError==KErrNone,Fault(EDisconnectRequestComplete));
+	// set session disconnect reqeust to NULL
+	// will be freed in CFsMessageRequest::Free()
+	Session()->iDisconnectRequest=NULL;
+	// now delete session
+	TheFileServer->SessionQueueLockWait();
+	delete(Session());
+	TheFileServer->SessionQueueLockSignal();
+	// NB Must complete the message AFTER the session has been deleted...
+	Message().Complete(aError);
+	delete(this);	
+	}
+
+
+/**
+Create a new synchronous message scheduler
+*/
+CFsSyncMessageScheduler* CFsSyncMessageScheduler::NewL()
+	{
+	__PRINT(_L("CFsSyncMessageScheduler::NewL()"));
+
+	CFsSyncMessageScheduler* pSelf = new(ELeave)CFsSyncMessageScheduler();
+	
+	CleanupStack::PushL(pSelf);
+	pSelf->ConstructL();
+	CleanupStack::Pop();
+	
+	return(pSelf);
+	}
+
+
+/**
+Construct a synchronous message scheduler
+*/
+CFsSyncMessageScheduler::CFsSyncMessageScheduler()
+ : CActive(EPriorityHigh),
+   iList(_FOFF(CFsRequest,iLink))
+	{
+	__PRINT(_L("CFsSyncMessageScheduler::CFsSyncMessageScheduler()"));
+	}
+
+
+/**
+Second-phase constructor
+*/
+
+void CFsSyncMessageScheduler::ConstructL()
+	{
+	__PRINT(_L("CFsSyncMessageScheduler::ConstructL()"));
+	
+	User::LeaveIfError(iLock.CreateLocal());
+	User::LeaveIfError(iThread.Open(RThread().Id()));
+	
+	CActiveScheduler::Add(this);
+	}
+	
+	
+/**
+Synchronous message scheduler 
+	- dispatch any synchronous requests that were queued via ::Dispatch
+*/
+void CFsSyncMessageScheduler::RunL()
+	{
+	__PRINT(_L(">> CFsSyncMessageScheduler::RunL()"));
+	
+	FOREVER
+		{
+		iLock.Wait();
+		if(iList.IsEmpty())
+			{
+			break;
+			}
+		else
+			{
+			CFsRequest* request = iList.First();
+			request->iLink.Deque();
+			iLock.Signal();
+			__PRINT1(_L("   CFsSyncMessageScheduler - Dispatching message %08x"), request);
+			request->Process();
+			}
+		}
+	
+	iSignalled = EFalse;
+	iStatus = KRequestPending;
+	SetActive();
+	iLock.Signal();
+	
+	__PRINT(_L("<< CFsSyncMessageScheduler::RunL()"));
+	}
+
+
+/**
+DoCancel - Not Implemented
+*/
+void CFsSyncMessageScheduler::DoCancel()
+	{
+	__PRINT(_L("CFsSyncMessageScheduler::DoCancel"));
+	}
+
+
+/**
+Queue synchronous requests to be processed in main thread context
+	- called when synchronous messages are passed from plugin threads
+*/
+void CFsSyncMessageScheduler::Dispatch(CFsRequest* aRequest)
+	{
+	__PRINT1(_L("CFsSyncMessageScheduler::Dispatch(%08x)"), aRequest);
+
+	// Accquire lock and add request to queue.
+	iLock.Wait();
+	iList.AddFirst(*aRequest);
+
+	if(!iSignalled)
+		{
+		// set iSignalled in case another thread (plug-in) is calling dispatch, then release lock.
+		iSignalled = ETrue;
+		iLock.Signal();
+
+		// signal main thread.
+		TRequestStatus* s = &iStatus;
+		iThread.RequestComplete(s, KErrNone);
+		}
+	else
+		{
+		// main thread already signalled, just release lock.
+		iLock.Signal();
+		}
+	}
+
+
+/**
+Complete outstanding requests for the specified session
+*/
+void CFsSyncMessageScheduler::CompleteSessionRequests(CSessionFs* aSession, TInt aValue)
+	{
+	__PRINT2(_L("FsPluginManager::CompleteSessionRequests(%08x, %d)"), aSession, aValue);
+	
+	iLock.Wait();
+	TDblQueIter<CFsRequest> q(iList);
+	CFsRequest* pR;
+	while((pR=q++)!=NULL)
+		{
+		if(pR->Session()==aSession)
+			{
+			pR->iLink.Deque();
+			pR->Complete(aValue);
+			}
+		}
+	iLock.Signal();
+	}
+