// 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 <e32std.h>
#include "sf_std.h"
#include "sf_plugin_priv.h"
CFsObjectCon* FsPluginManager::iPluginFactories = NULL;
CFsObjectCon* FsPluginManager::iPluginConns = NULL;
RFastLock FsPluginManager::iChainLock;
RPointerArray<CFsPlugin> 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<<i) && IsPagableDrive(i))
{
driveMask ^= 1<<i; //remove this drive
}
}
return driveMask;
}
TInt FsPluginManager::UpdateMountedDrive(CFsPlugin* aPlugin, CFsPluginFactory* aFactory,TInt aDrive)
{
if(aDrive==KPluginAutoAttach) //KPluginAutoAttach
{
if(!(aFactory->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<<aDrive;
return KErrNone;
}
return KErrNotSupported;
}
/**
FsPluginManager::Initialise
*/
void FsPluginManager::InitialiseL()
{
iPluginFactories = TheContainer->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<CFsRequest> 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<CFsRequest> 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;i<count;i++)
{
if(!aCheckCurrentOperation || iPluginChain[i]->IsRegistered(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;i<count;i++)
{
if(uPos == iPluginChain[i]->iUniquePos)
{
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;i<count;i++)
{
CFsPlugin* pP = iPluginChain[i];
if(aPluginFactory->SupportedDrives()&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<<EDriveZ)) || pP->iMountedOn & (1<<aDrive)))
return i;
}
else //version1
{
if(aUPos == pP->iUniquePos && (aDrive==KPluginAutoAttach || pP->iMountedOn & (1<<aDrive)))
return i;
}
}
return KErrNotFound;
}
if(aPos+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;i<count;i++)
{
if(aUniquePosition == iPluginChain[i]->iUniquePos)
{
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; i<count; i++)
{
CFsPlugin* plugin = NULL;
User::LeaveIfError(FsPluginManager::Plugin(plugin, i));
__ASSERT_DEBUG(plugin, User::Leave(KErrNotFound));
aRequest->iCurrentPlugin = 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);
}