diff -r 000000000000 -r a41df078684a userlibandfileserver/fileserver/sfile/sf_request.cpp --- /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; jiNext = list; + } + list->iNext = iFreeHead; + iFreeHead = start; + return KErrNone; + } + else + { + __CACHE_PRINT1(_L("RequestAllocator::AllocRequest() Limit exceeded Count = %d"),aNum); + CFsClientMessageRequest* request; + for(i=0;iSetAllocated(); + 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(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; jiIsAllocated = EFalse; + } + + iAllocNumOperation += KAllocReqBlock; + TMsgOperation* last; + for(i=1;iiNext = 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(&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 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(&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 q(iList); + CFsRequest* pR; + while((pR=q++)!=NULL) + { + if(pR->Session()==aSession) + { + pR->iLink.Deque(); + pR->Complete(aValue); + } + } + iLock.Signal(); + } +