diff -r 000000000000 -r a41df078684a userlibandfileserver/fileserver/sfile/sf_plugin_man.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/userlibandfileserver/fileserver/sfile/sf_plugin_man.cpp Mon Oct 19 15:55:17 2009 +0100 @@ -0,0 +1,755 @@ +// Copyright (c) 2005-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_plugin.cpp +// +// + +#include +#include "sf_std.h" +#include "sf_plugin_priv.h" + +CFsObjectCon* FsPluginManager::iPluginFactories = NULL; +CFsObjectCon* FsPluginManager::iPluginConns = NULL; +RFastLock FsPluginManager::iChainLock; +RPointerArray FsPluginManager::iPluginChain; +CFsSyncMessageScheduler* FsPluginManager::iScheduler = NULL; + +TBool IsPagableDrive(TInt aDrive) + { + if(LocalDrives::IsValidDriveMapping(aDrive)) + { + TLocalDriveCapsBuf capsBuf; + if(!LocalDrives::IsProxyDrive(aDrive) && LocalDrives::GetLocalDrive(aDrive).Caps(capsBuf) && capsBuf().iDriveAtt & KDriveAttPageable) + { + return (TBool)ETrue; + } + } + return (TBool) EFalse; + } + +/* +@param aNonPagableDriveMask - a drive mask of the drives a plugin wishes to mount on +@return A drive mask of the drives a plugin wishes to mount on not containing any pagable drives. +*/ +TInt NonPagableDrivesMask(TInt aNonPagableDriveMask) + { + TInt driveMask = aNonPagableDriveMask; + for(TInt i = 0; i < KMaxDrives; i++) + { + //If we're interested in this drive + if((aNonPagableDriveMask & 1<SupportedDrives()&KPluginVersionTwo)) + { //Version 1 Plugin - Mount on all (except Z and pagble drives) + TInt drivesAToY = (KPluginSupportAllDrives>>1); + aPlugin->iMountedOn = NonPagableDrivesMask(drivesAToY); + } + else + { //Version 2 Plugin - Mount on what the plugin supports (except pagable drives) + aPlugin->iMountedOn = NonPagableDrivesMask((aFactory->SupportedDrives()&(~KPluginVersionTwo))); + } + + if(!(aPlugin->iMountedOn &(~KPluginVersionTwo))) + { //Are we mounted on anything? + return KErrNotSupported; + } + return KErrNone; + } + + // DriveZ special handling + if(aDrive==KPluginMountDriveZ) + aDrive = EDriveZ; + + //Mounting on a single drive + if(!IsPagableDrive(aDrive)) + { + aPlugin->iMountedOn |= 1<CreateL(); + iPluginConns = TheContainer->CreateL(); + iPluginChain.Reset(); + User::LeaveIfError(iChainLock.CreateLocal()); + + // Create and install the synchronous message scheduler + // - Messages are dispatched here from plugin threads if they are + // to be executed in the context of the main file server thread. + iScheduler = CFsSyncMessageScheduler::NewL(); + iScheduler->RunL(); + } + +/** +FsPluginManager::MountPlugin +*/ +TInt FsPluginManager::MountPlugin(CFsPluginFactory& aPluginFactory, TInt aDrive, TInt aPos) + { + TInt uniquePosition = aPluginFactory.UniquePosition(); + CFsPlugin* pP = NULL; + CFsPluginFactory* pF = &aPluginFactory; + + //Version1 plugins could either been mounted on 1 or all drives. + //This isn't particularily desirable. + // Thus - + //For version2 plugins: + //Check whether this plugin has already been mounted + pP = FindByUniquePosition(uniquePosition); + if(pP && (aPluginFactory.iSupportedDrives&KPluginVersionTwo)) + { + //if this plugin has already been mounted then + //Instead of trying to mount and failing with KErrInUse, + //lets update the iMountedOn instead. + return UpdateMountedDrive(pP,pF,aDrive); + } + + + TRAPD(err, pP = aPluginFactory.NewPluginL()); + if(err != KErrNone) + { + if(pP) + pP->Close(); + return err; + } + + TFullName name = aPluginFactory.Name(); + pP->SetName(&name); + pP->iUniquePos=aPluginFactory.UniquePosition(); + pP->SetDrive(aDrive); + + //Set which drive(s?) this plugin is mounted on. + err = UpdateMountedDrive(pP,pF,aDrive); + if(err != KErrNone) + { + pP->Close(); + return err; + } + + LockChain(); + err = InsertInPluginStack(pP,aPos); + UnlockChain(); + if(err != KErrNone) + { + pP->Close(); + return err; + } + + err = InitPlugin(*pP); + if(err != KErrNone) + { + pP->Close(); + return err; + } + aPluginFactory.IncrementMounted(); + + return KErrNone; + } + +/** +Dismounts a plugin +Must be called with the plugin chain locked. +*/ +void FsPluginManager::DismountPlugin(CFsPluginFactory& aPluginFactory,TInt aPos) + { + CFsPlugin* plugin=iPluginChain[aPos]; + + if (plugin != NULL) + { + // Check if there is any requests for this plugin + // if so, deliver them to the next supporting plugin + TransferRequests(plugin->iThreadP); + + plugin->iThreadP=NULL; + + iPluginChain.Remove(aPos); + iPluginChain.Compress(); + + //Need this to remove it from container + //plugin->Close() deletes plugin. + plugin->Close(); + plugin=NULL; + } + else + { + ASSERT(0); // force panic in debug mode + } + + aPluginFactory.DecrementMounted(); + } + +/* + * This will iterate through a plugins request queue and + * search for the first occurance it finds of a CancelPluginOp + * request. + */ +void FsPluginManager::GetNextCancelPluginOpRequest(CPluginThread* aPluginThread, CFsRequest*& aCancelPluginRequest) + { + __THRD_PRINT(_L("FsPluginManager::GetNextCancelPluginOpRequest")); + + TDblQueIter iter(aPluginThread->iList); + CFsRequest* request = NULL; + + while((request=iter++)!=NULL) + { + if(request->Operation()->iFunction == KCancelPlugin) + { + aCancelPluginRequest = request; + break; + } + } + } + + + + + +/** +Transfer any outstanding requests to next/previous plugin depending on +if it is post filter or not +*/ +void FsPluginManager::TransferRequests(CPluginThread* aPluginThread) + { + aPluginThread->iListLock.Wait(); + + __THRD_PRINT(_L("FsPluginManager::TransferRequests - moving requests")); + + /* + * We are transferring requests up and down the chain + * because this plugin is being removed. + * + * There is a potential problem when one of the outstanding requests + * is CancelPluginOp which is called when the Session is being closed. + * The CancelPluginOp will try to cancel all of the requests on a plugin's + * queue which is associated with that session. + * DismountPlugin(and thus TransferRequests) is trying to preserve the requests + * by passing them along to the Next/Previous plugins. + * + * If there is a cancel in the chain we must NOT pass any requests to Previous Plugins + * as these have already had their chains emptied. + * We should also be wary of passing requests up the chain as they will simply be cancelled + * somewhere closer to the drive [thread]. + * + * Therefore, we shall check whether there is a KCancelPlugin op in the + * chain first. + * If there is a cancelPluginOp in the chain, we will cancel of the requests + * that are associated with that session. + * + * After that is out of the way we preserve the remaining requests for different sessions by + * passing them on. + */ + if(!aPluginThread->iList.IsEmpty()) + { + CFsRequest* cancelRequest = NULL; + //For every CancelPluginOp + while(FsPluginManager::GetNextCancelPluginOpRequest(aPluginThread, cancelRequest), cancelRequest!=NULL) + { + RDebug::Print(_L("Transferring Plugin Requests - CancelPluginOp")); + TDblQueIter iter(aPluginThread->iList); + CFsRequest* request = NULL; + //For every request + while((request=iter++)!=NULL) + { + if(request->Session() == cancelRequest->Session() && request != cancelRequest) + { + request->iLink.Deque(); + request->Complete(KErrCancel); + } + } + cancelRequest->iLink.Deque(); + cancelRequest->Complete(KErrNone); + cancelRequest = NULL; + } + + /* + * Now that all requests that were to be cancelled have been cancelled, + * we can now go about moving the remaining ones on to , or back to, + * the appropriate next or previous plugins. + * + * ToDo: This next 'while' might be able to be replaced with a call to + * DispatchToPlugin/DispatchToDrive instead. + */ + while(!aPluginThread->iList.IsEmpty()) + { + CFsRequest* pR=aPluginThread->iList.First(); + CFsMessageRequest& mR = *(CFsMessageRequest*) pR; + pR->iLink.Deque(); + pR->iCurrentPlugin=NULL; + + if(pR->IsPluginSpecific()) + { + pR->Complete(KErrCancel); + continue; + } + + if(pR->IsPostOperation()) + { + //[set the plugin to] pass the request backwards in the chain + PrevPlugin(pR->iCurrentPlugin, &mR, EFalse); + } + else //IsPreOperations + { + //[set the plugin to] pass the request forwards in the chain + NextPlugin(pR->iCurrentPlugin, &mR, EFalse); + } + + if(pR->iCurrentPlugin) + { + pR->iCurrentPlugin->iThreadP->DeliverBack(pR); + } + else + { + if(!pR->IsPostOperation() && (pR->DriveNumber()>=EDriveA && pR->DriveNumber()<=EDriveZ)) + { + //Deliver to drive thread + CDriveThread* dT=NULL; + TInt r=FsThreadManager::GetDriveThread(pR->DriveNumber(),&dT); + __ASSERT_ALWAYS(r==KErrNone && dT,Fault(EFsDriveThreadError)); + CRequestThread* pT = (CRequestThread*)dT; + pT->DeliverBack(pR); + } + else + { + pR->Complete(KErrCancel); + } + } + } + } + + aPluginThread->iExit = ETrue; + aPluginThread->iListLock.Signal(); + + __THRD_PRINT(_L("FsPluginManager::TransferRequests - all requests moved/cancelled")); + } + +/** +Install a plugin factory +*/ +TInt FsPluginManager::InstallPluginFactory(CFsPluginFactory* aFactory,RLibrary aLib) + { + TInt r=aFactory->Install(); + __PRINT1TEMP(_L("InstallPluginFactory %S"),aFactory->Name()); + if (r==KErrNone) + {TRAP(r,iPluginFactories->AddL(aFactory,ETrue))} + if (r!=KErrNone) + aFactory->Remove(); + if (r==KErrNone) + aFactory->SetLibrary(aLib); + else + aFactory->Close(); + return(r); + } + +/** +Find a plugin factory by name +*/ +CFsPluginFactory* FsPluginManager::GetPluginFactory(const TDesC& aName) + { + + TInt h=0; + TInt r=iPluginFactories->FindByName(h,aName); + if (r!=KErrNone) + return(NULL); + return((CFsPluginFactory*)iPluginFactories->At(h)); + } + +/** +Find the next plugin that supports the operation +@param aCheckCurrentOperation - Optional, if false, will return the next plugin, + whether the plugin is currently registered + for the current function of not. (so long as mounted on the current drive) +*/ +TInt FsPluginManager::NextPlugin(CFsPlugin*& aPlugin, CFsMessageRequest* aMsgRequest,TBool aLock, TBool aCheckCurrentOperation) + { + if(aMsgRequest->DirectToDrive()) + { + aPlugin = NULL; + return KErrNotFound; + } + + TInt r = KErrNone; + TInt start; + TInt function = aMsgRequest->Operation()->Function(); + TInt drive = aMsgRequest->DriveNumber(); + + if(aLock) + LockChain(); + + TInt count = iPluginChain.Count(); + + if(aPlugin == NULL) + start=0; + else + start = iPluginChain.Find(aPlugin)+1; + + if(start!=KErrNotFound) + { + for(TInt i=start;iIsRegistered(function, CFsPlugin::EPreIntercept)) + { + if((function == EFsDismountPlugin) || (iPluginChain[i]->IsMounted(drive))) + { + + aPlugin = iPluginChain[i]; + if(aLock) + UnlockChain(); + return(r); + } + } + } + } + aPlugin = NULL; + if(aLock) + UnlockChain(); + return(KErrNotFound); + } + +/** +Find the next plugin that supports the operation +*/ +TInt FsPluginManager::PrevPlugin(CFsPlugin*& aPlugin, CFsMessageRequest* aMsgRequest, TBool aLock) + { + if(aMsgRequest->DirectToDrive() && (aMsgRequest->CurrentOperationPtr() != NULL)) + { + aPlugin = NULL; + return KErrNotFound; + } + + TInt r = KErrNone; + TInt start; + TInt function = aMsgRequest->Operation()->Function(); + TInt drive = aMsgRequest->DriveNumber(); + + if(aLock) + LockChain(); + + TInt count= iPluginChain.Count(); + + if(aPlugin == NULL) + start = count-1; + else + start = iPluginChain.Find(aPlugin)-1; + + if(start!=KErrNotFound) + { + for(TInt i=start;i>=0;i--) + { + CFsPlugin* owner = aMsgRequest->iOwnerPlugin; + if(owner == iPluginChain[i]) + break; + + if(iPluginChain[i]->IsRegistered(function, CFsPlugin::EPostIntercept)) + { + if((function == EFsDismountPlugin) || (iPluginChain[i]->IsMounted(drive))) + { + + aPlugin = iPluginChain[i]; + if(aLock) + UnlockChain(); + return(r); + } + } + } + } + aPlugin = NULL; + if(aLock) + UnlockChain(); + return(KErrNotFound); + } +/** +Inserts the plugin in the stack (chain) + +if aPos absolute postion, it simply inserts it +if aPos unique position, it walks through chain and checks the unique positions +*/ +TInt FsPluginManager::InsertInPluginStack(CFsPlugin*& aPlugin,TInt aPos) + { + TInt ret=KErrNone; + TInt count= iPluginChain.Count(); + if(aPos == KPluginAutoLocate) + { + TInt uPos= aPlugin->iUniquePos; + for(TInt i=0;iiUniquePos) + { + return KErrInUse; + } + + if(uPos < iPluginChain[i]->iUniquePos) + { + ret=iPluginChain.Insert(aPlugin,i); + return ret; + } + } + ret=iPluginChain.Append(aPlugin); + } + else + { + // Absolute position + + if(aPos > count) + return(KErrNotFound); + if(aPos == count) + { + ret=iPluginChain.Append(aPlugin); + return ret; + } + ret=iPluginChain.Insert(aPlugin,aPos); + } + return ret; + } + +/** +Looks for a plugin in the chain +*/ +TInt FsPluginManager::IsInChain(TInt aUPos,TInt aPos, TInt aDrive, CFsPluginFactory* aPluginFactory) + { + TInt count= iPluginChain.Count(); + + if(aPos == KPluginAutoLocate) + { + for(TInt i=0;iSupportedDrives()&KPluginVersionTwo) //Version2 + { + //If KPluginAutoAttach, then we're dismounted from all drives. + //If KPluginMountDriveZ, then check against 25 explicitly (cannot change aDrive:=Z as that==KPluginAutoAttach)) + //If any other drive mounted. + if(aUPos == pP->iUniquePos && (aDrive==KPluginAutoAttach || (aDrive==KPluginMountDriveZ && pP->iMountedOn & (1<iMountedOn & (1<iUniquePos && (aDrive==KPluginAutoAttach || pP->iMountedOn & (1<iPluginChain.Count()) + return(KErrNotFound); + + if(iPluginChain[aPos]->iUniquePos == aUPos && aDrive==iPluginChain[aPos]->Drive()) + return aPos; + return KErrNotFound; + } + +/** +Finds a plugin by unique position +*/ +CFsPlugin* FsPluginManager::FindByUniquePosition(TInt aUniquePosition) + { + LockChain(); + CFsPlugin* plugin = NULL; + TInt count= iPluginChain.Count(); + for(TInt i=0;iiUniquePos) + { + plugin = iPluginChain[i]; + break; + } + } + UnlockChain(); + return plugin; + } + +/** +Create a connection to a plugin +*/ +CFsPluginConn* FsPluginManager::CreatePluginConnL(TInt aUniquePosition, TUint aClientId) + { + CFsPlugin* pP = FindByUniquePosition(aUniquePosition); + if(pP != NULL) + { + CFsPluginConn* pC = pP->NewPluginConnL(); + pC->iPluginP = pP; + pC->iClientId = aClientId; + iPluginConns->AddL(pC, ETrue); + return pC; + } + + User::Leave(KErrNotFound); + return NULL; + } + +/** +Create a plugin thread +Should only by called from main file server thread with plugin thread unavailable +*/ +TInt FsPluginManager::InitPlugin(CFsPlugin& aPlugin) + { + TInt err = KErrNone; + + if(!aPlugin.iThreadP) + { + TRAP(err,aPlugin.iThreadP=CPluginThread::NewL(aPlugin)); + if(err!=KErrNone) + return err; + } + + aPlugin.iThreadId = aPlugin.iThreadP->StartL(); + return err; + } +/** +Cancels plugin requests +*/ +void FsPluginManager::CancelPlugin(CFsPlugin* aPlugin,CSessionFs* aSession) + { + aPlugin->iThreadP->CompleteSessionRequests(aSession,KErrCancel); + } + +/** +Gets number of plugins in the plugin stack +*/ +TInt FsPluginManager::ChainCount() + { + return(iPluginChain.Count()); + } + +/** + * Returns a CFsPlugin* from aPos in the plugin chain. + * + * To be called whilst already holding the iChainLock. + * + * @returns (via parameter) CFsPlugin*& aPlugin +*/ +TInt FsPluginManager::Plugin(CFsPlugin*& aPlugin, TInt aPos) + { + if(aPos >= iPluginChain.Count() || aPos < 0) + return KErrNotFound; + + aPlugin = iPluginChain[aPos]; + return KErrNone; + } + +/** +Locks the chain +*/ +void FsPluginManager::LockChain() + { + iChainLock.Wait(); + } + +/** +Unlocks the chain +*/ +void FsPluginManager::UnlockChain() + { + iChainLock.Signal(); + } + +/** +Gets plugin conn from handle +*/ +CFsPluginConn* FsPluginManager::GetPluginConnFromHandle(CSessionFs* aSession, TInt aHandle) + { + return((CFsPluginConn*)(SessionObjectFromHandle(aHandle,iPluginConns->UniqueID(),aSession))); + } + +/** +Checks if the current thread is plugin conn's thread +*/ +TBool FsPluginManager::IsPluginConnThread(TThreadId tid, CFsPlugin* aPlugin) + { + iPluginConns->Lock(); + TInt count = iPluginConns->Count(); + while(count--) + { + CFsPluginConn* conn = (CFsPluginConn*)(*iPluginConns)[count]; + if(conn->Plugin() == aPlugin) + { + if(conn->ClientId() == tid) + { + iPluginConns->Unlock(); + return ETrue; + } + } + } + iPluginConns->Unlock(); + return EFalse; + } + +/** +Dispatch a synchronous message +*/ +void FsPluginManager::DispatchSync(CFsRequest* aRequest) + { + __THRD_PRINT(_L("FsPluginManager::DispatchSync")); + if(!FsThreadManager::IsMainThread() && iScheduler) + { + iScheduler->Dispatch(aRequest); + } + else + { + aRequest->Process(); + } + } + +void FsPluginManager::CompleteSessionRequests(CSessionFs* aSession, TInt aValue, CFsInternalRequest* aRequest) +/** +Complete outstanding requests for the specified session +*/ + { + __PRINT2(_L("FsPluginManager::CompleteSessionRequests(%08x, %d)"), aSession, aValue); + + // Iterate through all plugins, cancelling outstanding session requests + aRequest->Set(CancelPluginOp, aSession); + + FsPluginManager::LockChain(); + TInt count = FsPluginManager::ChainCount(); + TInt i; + for(i=0; iiCurrentPlugin = plugin; + aRequest->Status() = KRequestPending; + aRequest->Dispatch(); + //Cancel is delivered to the front of the request queue + //so hopefully this wont take too long. + FsPluginManager::UnlockChain(); + User::WaitForRequest(aRequest->Status()); + FsPluginManager::LockChain(); + __ASSERT_ALWAYS(aRequest->Status().Int()==KErrNone||aRequest->Status().Int()==KErrCancel,Fault(ESessionDisconnectThread2)); + count = FsPluginManager::ChainCount(); + } + FsPluginManager::UnlockChain(); + +// RDebug::Print(_L("FsPluginManager::CompleteSessionRequests - CSRs")); + iScheduler->CompleteSessionRequests(aSession, aValue); + } + +