Writing a plug-in A
+file server plug-in is made up of at least two classes:
+a factory class derived
+from the CFspluginFactory base
+class,
+a plug-in class derived
+from the CFsplugin base
+class.
+
Each plug-in must register
+to intercept messages. To make
+internal requests call the functions RFilePlugin, RDirPlugin or RFsPlugin.
Each
+plug-in DLL must export a plug-in entry point function with the prototype:
CFsPluginFactory* CreateFileSystem(); The
+loader calls this function when a client wishes to install a file server plug-in
+and creates an instance of the factory class. For example:
extern "C" {
+
+EXPORT_C CFsPluginFactory* CreateFileSystem()
+ {
+ return(new CMyPluginFactory());
+ }
+} CFsPluginFactory
The CFsPluginFactory base class follows
+the standard factory pattern as used in other file server DLLs. CFsPluginFactory creates CFsPlugin derived objects.
plug-in
+authors must provide implementations of the pure virtual functions:
+ Install()
This
+function is called by the file server in response to a call to RFs::AddPlugin() and
+installs the plug-in factory.
This function must set the name of the
+plug-in factory with a call to CObject::SetName().
+The plug-in name is used when mounting, dismounting and unloading plug-ins.
+Optionally, RFs::AddPlugin() can
+perform other plugin specific initialization, for example setting iSupportedDrives to
+indicate which drives the plugin will operate on, as required by the plugin
+factory. See drive
+selection.
+ NewPluginL()
This
+is called by the file server in response to a call to RFs::MountPlugin(),
+this function creates a new CFsPlugin derived
+object and returns a pointer to it.
+ UniquePosition()
This
+function returns the unique ID of the plugin. This is used to identify the
+plugin and to specify the location of the plugin in the chain. See unique position.
+ Remove() (overriding
+this virtual function is optional as it is not a pure virtual function)
This
+is called just before the plugin factory object is destroyed and allows any
+clean up to be carried out.
The default implementation just returns KErrNone.
+Implementations should return an error code on error detection.
+
The CFsPluginfactory base
+class also has the following member variables:
+iSupportedDrives
This
+member holds a bit mask that indicates which drives the plugin created by
+this factory supports being mounted on. Bits 0 to 25 correspond to 26 drive
+letters lettered from A to Z.
Plugin authors should use the the SetSupportedDrives() API in order
+to correctly set up which drives their plugin should be mounted on. Set the
+drive number to KPluginSupportAllDrives to
+indicate that the plugin needs to be mounted on all drives.
If you
+are using Symbian platform prior to version 9.5, iSupportedDrives is
+assigned to by plugin writers directly. However, this is now discouraged in
+favour of using SetSupportedDrives.
+iUniquePos
This
+member stores the unique position identifier. If this member is used then
+plugin authors should implement the UniquePosition() function
+to return the variable.
+
An example skeleton implementation of a CFsPluginFactory derived
+class is as follows:
class CMyPluginFactory : public CFsPluginFactory
+ {
+public:
+ CMyPluginFactory();
+ virtual TInt Install();
+ virtual CFsPlugin* NewPluginL();
+ virtual TInt UniquePosition();
+ virtual TInt Remove();
+ };
+
+CMyPluginFactory::CMyPluginFactory()
+ {
+ // Constructor for the plugin factory
+ }
+
+TInt CMyPluginFactory::Install()
+ {
+ // Install function for the plugin factory
+ iUniquePos = 0x2000001;
+ // Mount on all drives
+ SetSupportedDrives(KPluginSupportAllDrives);
+ return SetName(_L("MyPluginName"));
+ }
+
+CFsPlugin* CMyPluginFactory::NewPluginL()
+ {
+ // plugin factory function, creates the plugin
+ return CMyPlugin::NewL();
+ }
+
+TInt CMyPluginFactory::UniquePosition()
+ {
+ // Return’s the unique position identifier for plugins created by this factory class.
+ return iUniquePos;
+ }
+
+TInt CMyPluginFactory::Remove()
+ {
+ // Clean up function for the plugin factory
+ return KErrNone;
+ } CFsPlugin
This
+is the base class for the file server plugin and is defined in f32plugin.h.
Plugins
+must register to intercept particular types of file server requests. See Registering
+a plugin to intercept messages.
Plugin authors need to provide
+an implementation of the pure virtual method DoRequestL() and
+can optionally override the virtual functions:
+ SessionDisconnect() - virtual,
+ InitialiseL() - virtual,
+ Deliver() - virtual,
+ NewPluginConnL() - virtual,
+ DoRequestL() - pure virtual,
+
SessionDisconnect()
SessionDisconnect() is called
+by the file server when a file server session is disconnected.
The
+default implementation just returns KErrNone. Overriding
+this function allows plugins to free up any resources prior to the outstanding
+operations being cancelled.
InitialiseL()
The
+default implementation of InitialiseL() does
+nothing. Override this implementation to perform a plugin specific initialisation,
+for example registering
+intercepts. If the plugin cannot be initialised this function must
+leave with a suitable error code.
Deliver()
Deliver() is called in the context
+of the previous thread that originated or processed the request (either the
+file server main thread or another plugin thread) before the request is dispatched
+to the plugin’s thread.
The default implementation delivers the request
+to the end of the plugin thread's request queue. Requests that require priority
+handling (usually those sent using an RPlugin session)
+are delivered to the front of the queue. See Communicating
+with plugins.
Overriding this function allows plugins to perform
+operations within the context of the original thread (like validation of request
+parameters or filtering requests) before the request is sent to the plugin’s
+main thread or passed to the next plugin or drive thread.
Deliver() is
+called from outside the context of the plugin’s main thread so care must be
+taken as the plugin main thread may still be processing the previous request.
+If KErrNone is
+returned then the base class implementation of this function must be called
+to ensure that the request is dispatched to the plugin’s thread for further
+processing in DoRequestL().
+ KPluginMessageForward indicates
+that the request has been processed synchronously and should be passed down
+to the next plugin in the stack (bypassing the current plugin’s DoRequestL() function).
+This is used for pre-intercepts.
+ KPluginMessageComplete indicates
+that the request has been processed synchronously and should be passed up
+to the previous plugin in the stack (bypassing the current plugin’s DoRequestL() function).
+This is used for post-intercepts.
+
NewPluginConnL()
The
+function NewPluginConnL() is
+the default implementation and it returns KErrNotSupported.
+Override this implementation to create a CFsPluginConn derived
+object to enable direct communication between a client application and a plugin
+using RPlugin. See Communicating
+with plugins.
DoRequestL()
DoRequestL() is the main entry
+point for messages intercepted by a plugin. Individual plugins are usually
+single-threaded and must process requests in the order that they arrive. However,
+the plugin framework is not single-threaded. While a plugin is handling a
+request all other plugins are also able to handle requests. The drive thread
+is also able to handle requests forwarded to it by plugins. DoRequestL() is
+called in the context of the plugin’s main thread and must be implemented
+to intercept file server requests in this context.
The current request
+must have finished all processing before DoRequestL() has
+completed. If a plugin has multiple threads and the request is handled in
+a thread other than the main thread then the DoRequestL() function
+must be blocked until the request has been processed. This is because when DoRequestL() exits,
+the plugin framework forwards the message to the next plugin in the chain.
+Unpredictable behaviour may occur if the plugin has not finished processing
+a request before it is passed to the next plugin/file system.
Errors
+returned by this function are propagated back to the client. If this function
+leaves then the client thread panics. Note: Do not return from this
+function until the request has been fully processed. This is because after
+returning from this function the request is passed onto the next plugin in
+the chain. This means that you cannot pass the request to a separate thread
+and return immediately in order to implement a multi-threaded plugin.
KErrCompletion indicates
+that all processing for this request has been completed by this plugin. Post-intercept
+is then enabled and the flow of execution is up the plugin stack towards the
+client, starting at the previous plugin.
When a plugin intercepts
+a file read or file write request and does an early completion (i.e. returns KErrCompletion in pre-intercept)
+then the plugin author should call TFsPluginRequest::SetSharePos() to
+allow share position to be updated after early read/write completion.
TFsPluginRequest
The TFsPluginRequest class encapsulates
+the intercepted file server request and is passed as a reference to the plugin's DoRequestL() function. TFsPluginRequest provides
+APIs that extract request specific information such as:
+ DriveNumber() -
+returns the target drive number. The drive number is in the range of zero
+to 25, which corresponds to drive letters A to Z,
+ Function() -
+the type of request as defined in TFsMessage,
+ IsPostOperation() -
+returns ETrue or EFalse to indicate if the
+request being intercepted is in post-intercept mode (ETrue)
+or pre-intercept.
+
TFsPluginRequest acts
+as a utility class for plugins. This class has two main functions TFsPluginRequest::Read() and TFsPluginRequest::Write(), which
+allow a plugin to read from and write to the message arguments of a request.
TFsPluginRequest::Read() has
+various overloads for TInt, TUint, Tint64 and
+descriptor data types, all of which take as their first argument a TF32ArgType object.
The TF32ArgType is an enumeration
+which defines the type of data that is requested. For example, EPosition, ELength, EData for
+getting the position, length and data arguments from the request. TF32ArgType is
+defined as follows:
enum TF32ArgType
+ {
+ EPosition,
+ ELength,
+ EData,
+ ESize,
+ EName,
+ ENewName,
+ EEntry,
+ ETime,
+ ESetAtt,
+ EClearAtt,
+ EMode,
+ EAtt,
+ EAttMask,
+ EUid,
+ EEntryArray,
+ ENewPosition,
+ }; TFsPluginRequest::Write() only
+has two overloads, both of these take descriptor types and the argument TF32ArgType. Many functions do
+not use descriptor types, it will be necessary to package data for these inside
+a descriptor.
The following provides an example of how to do this
+for Entry:
TPckgC<TEntry> entryPckg(entry);
+ err = aRequest.Write(EEntry, entryPckg); Sending internal requests
+to the file server
The classes RFilePlugin, RDirPlugin and RFsPlugin,
+defined in F32Plugin.h, are the classes that plugin authors
+should use to send internal requests to the file server. The APIs for these
+classes are mostly identical from their client side (RFile, RDir and RFs)
+counter-parts.
Before a plugin can perform any requests on a file
+it must first either perform an Open() or AdoptFromClient(). When an AdoptFromClient() is
+performed the RFilePlugin instance opens the file associated
+with the client request. Open() can be used to open the same
+file (potentially with different parameters than those supplied by the client)
+and it can be used to open an entirely different file.
RFilePlugin has
+the following public functions:
class RFilePlugin : private RFile
+ {
+public:
+ IMPORT_C RFilePlugin(TFsPluginRequest& aRequest, TBool aDirectToDrive = EFalse);
+
+ // open a NEW file using same session as passed request
+ IMPORT_C TInt Open(const TDesC& aName,TUint aMode);
+ IMPORT_C TInt Create(const TDesC& aName,TUint aFileMode);
+ IMPORT_C TInt Replace(const TDesC& aName,TUint aFileMode);
+ IMPORT_C TInt Temp(const TDesC& aPath,TFileName& aName,TUint aFileMode);
+
+ // re-open SAME file as client's request
+ IMPORT_C TInt AdoptFromClient();
+ // Transfer the plugin's open file to the client
+ IMPORT_C TInt TransferToClient();
+
+ IMPORT_C void Close();
+
+ // RFile overloads
+ IMPORT_C TInt Read(TInt64 aPos,TDes8& aDes) const;
+ IMPORT_C TInt Read(TInt64 aPos,TDes8& aDes,TInt aLength) const;
+ IMPORT_C TInt Write(TInt64 aPos,const TDesC8& aDes);
+ IMPORT_C TInt Write(TInt64 aPos,const TDesC8& aDes,TInt aLength);
+ IMPORT_C TInt Lock(TInt64 aPos,TInt64 aLength) const;
+ IMPORT_C TInt UnLock(TInt64 aPos,TInt64 aLength) const;
+ IMPORT_C TInt Seek(TSeek aMode,TInt64& aPos) const;
+ IMPORT_C TInt Flush();
+ IMPORT_C TInt Size(TInt64& aSize) const;
+ IMPORT_C TInt SetSize(TInt64 aSize);
+ IMPORT_C TInt Att(TUint& aAttValue) const;
+ IMPORT_C TInt SetAtt(TUint aSetAttMask,TUint aClearAttMask);
+ IMPORT_C TInt Modified(TTime& aTime) const;
+ IMPORT_C TInt SetModified(const TTime& aTime);
+ IMPORT_C TInt Set(const TTime& aTime,TUint aSetAttMask,TUint aClearAttMask);
+ IMPORT_C TInt ChangeMode(TFileMode aNewMode);
+ IMPORT_C TInt Rename(const TDesC& aNewName);
+ ... Registering a plugin to
+intercept messages
The base class CFsPlugin,
+has functions that allow a plugin to register to intercept specific types
+of file server request. This is explained in more detail under Interception of file server requests.
RegisterIntercept()
The function RegisterIntercept() registers
+a plugin to intercept a specified TFsMessage.
+The second parameter of RegisterIntercept() allows plugin
+authors to specify whether the intercept is processed before, after or both
+before and after the request is processed by the drive thread. See TInterceptAtts.
UnregisterIntercept()
Un-register an intercept with UnregisterIntercept(). UnregisterIntercept() takes TFsMessage, which is the ID of
+the message and TInterceptAtts,
+which is the type of intercept that you no longer wish to intercept.
+Loading and
+mounting a plugin A plugin must be loaded and mounted before it
+can intercept requests.
Loading and unloading a
+plugin
Before a plugin can be used by the file server the library
+(.PXT) that contains the plugin needs to be loaded into
+the file server process by the loader. Do this using the RFs::AddPlugin() function
+defined in f32file.h.
AddPlugin() can
+be called by any user side application with the relevant platform security capabilities.
Unload a plugin using the RFs::RemovePlugin() function
+also defined in f32file.h.
Note: Refer
+to the device manufacturer if you want to load plugins automatically at system
+startup time.
Mounting and unmounting
+a plugin
File Server plugins are mounted using the RFs::MountPlugin() function
+defined in f32file.h. The plug in must have already been
+loaded using RFs::AddPlugin().
There
+are various overloads of the MountPlugin() function. Those
+functions which do not take a drive parameter are mounted using KPluginAutoAttach.
Call RFs::DismountPlugin() to dismount
+the file server plugin. Dismount the plugin when you no longer need it to
+intercept file server requests.
Before a plugin can be dismounted
+all resources owned by the plugin must be closed. A plugin can intercept the
+request to dismount it and use this to free resources and close down cleanly.
+If the plugin owns resources on behalf of a client (for example, a client
+has a file open that is using the decompression plugin) then the plugin can
+reject the request to dismount by returning one of the system wide error codes.
+Communicating
+with plugins The file server provides a standard logical channel
+based mechanism that enables a trusted application to communicate with a file
+server plugin. For example, a virus scanning plugin can present a user interface
+for the device user. Note: DiskAdmin capabilities
+are required in order to load a plugin. See platform
+security capabilities.
Data transfer
The class CFsPluginConnRequest is
+an information container for asynchronous requests that the client application
+wants performed. CFsPluginConnRequest also provides functions
+that enable data to be transferred between the plugin and the application.
+The following examples show how to use CFsPluginConnRequest.
User-side application
A
+user-side application uses a class derived from RPlugin to
+access the plugin. See plugin
+side. The RPlugin class (defined in f32file.h)
+provides various functions that must be overridden. The functions DoControl(), DoRequest() and DoCancel() are protected. Plugin authors must provide a public interface of their own
+design in the derived class that communicates with the plugin using these
+protected functions.
An RPlugin session is opened
+by passing in the unique
+position identifier of the plugin it wishes to communicate with. When
+the file server receives the request it validates the capabilities of the
+application before calling the plugin's NewPluginConnL() function.
+The plugin must certify that it is expecting the client application before
+creating the server side connection object.
Below is a basic example
+of an RPlugin derived class:
class RMyPlugin : public RPlugin
+{
+public:
+ inline TInt Enable();
+ inline TInt Disable();
+ };
+
+inline TInt RMyPlugin::Enable()
+ {
+ return DoControl(EEnable);
+ }
+
+inline TInt RMyPlugin::Disable()
+ {
+ return DoControl(EDisable);
+ }
+
+void MyRPlugin::DoRequest(TInt aReqNo,TRequestStatus& aStatus) const
+ {
+ RPlugin::DoRequest(aReqNo,aStatus);
+ }
+
+void MyRPlugin::DoRequest(TInt aReqNo,TRequestStatus& aStatus,TDes8& a1) const
+ {
+ RPlugin::DoRequest(aReqNo,aStatus,a1);
+ }
+
+void MyRPlugin::DoRequest(TInt aReqNo,TRequestStatus& aStatus,TDes8& a1,TDes8& a2) const
+ {
+ RPlugin::DoRequest(aReqNo,aStatus,a1,a2);
+ }
+
+TInt MyRPlugin::DoControl(TInt aFunction) const
+ {
+ return RPlugin::DoControl(aFunction);
+ }
+
+This is used in the following way:
Tint r = KErrNone
+r = TheFs.AddPlugin(_L("MyPlugin"));
+r = TheFs.MountPlugin(_L("MyPlugin"),KPluginSupportAllDrives);
+User::LeaveIfError(r);
+
+RMyPlugin thePlugin;
+r = thePlugin.Open(TheFs, KMyPluginUniquePos);
+User::LeaveIfError(r);
+r = thePlugin.Enable();
+User::LeaveIfError(r);
+...
+//Perform communication with plugin here
+...
+r = thePlugin.Disable();
+User::LeaveIfError(r);
+thePlugin.Close(); class RMyPlugin : public RPlugin
+ {
+public:
+ inline TInt Enable();
+ inline TInt Disable();
+ };
+
+inline TInt RMyPlugin::Enable()
+ {
+ return DoControl(EEnable);
+ }
+
+inline TInt RMyPlugin::Disable()
+ {
+ return DoControl(EDisable);
+ }
+
+void MyRPlugin::DoRequest(TInt aReqNo,TRequestStatus& aStatus) const
+ {
+ RPlugin::DoRequest(aReqNo,aStatus);
+ }
+
+void MyRPlugin::DoRequest(TInt aReqNo,TRequestStatus& aStatus,TDes8& a1) const
+ {
+ RPlugin::DoRequest(aReqNo,aStatus,a1);
+ }
+
+void MyRPlugin::DoRequest(TInt aReqNo,TRequestStatus& aStatus,TDes8& a1,TDes8& a2) const
+ {
+ RPlugin::DoRequest(aReqNo,aStatus,a1,a2);
+ }
+
+TInt MyRPlugin::DoControl(TInt aFunction) const
+ {
+ return RPlugin::DoControl(aFunction);
+ }
+
+etc..
+}
+This is used in the following way:
Tint r = KErrNone
+r = TheFs.AddPlugin(_L("MyPlugin"));
+r = TheFs.MountPlugin(_L("MyPlugin"),KPluginSupportAllDrives);
+User::LeaveIfError(r);
+
+RMyPlugin thePlugin;
+r = thePlugin.Open(TheFs, KMyPluginUniquePos);
+User::LeaveIfError(r);
+r = thePlugin.Enable();
+User::LeaveIfError(r);
+...
+//Perform communication with plugin here
+...
+r = thePlugin.Disable();
+User::LeaveIfError(r);
+thePlugin.Close(); Plugin side
This
+is the plugin side of the communication. This must be implemented in order
+to communicate with the application side class RPlugin above.
+The plugin side is a class derived from the CFsPluginConn class.
+The plugin’s NewPluginConnL() function
+is called from the file server when RPlugin::Open() is
+called by the client.
Synchronous (DoControl())
+and asynchronous (DoRequest())
+requests sent from the RPlugin class
+are routed directly to the associated plugin's CFsPluginConn derived
+class. The following code shows a basic implementation:
class CMyPluginConn : public CFsPluginConn
+ {
+ virtual TInt DoControl(CFsPluginConnRequest& aRequest);
+ virtual void DoRequest(CFsPluginConnRequest& aRequest);
+ virtual void DoCancel(TInt aReqMask);
+ };
+
+// From the CFsPlugin derived class
+CFsPluginConn* CMyPlugin::NewPluginConnL()
+ {
+ CMyPluginConn* thePluginConn = new(ELeave) CMyPluginConn();
+ return thePluginConn;
+ }
+
+TInt CMyPluginConn::DoControl(CFsPluginConnRequest& aRequest)
+ {
+ TInt r = KErrNotSupported;
+ CMyPlugin& myPlugin = *(CMyPlugin*)Plugin();
+ switch(aRequest.Function())
+ {
+ case RMyPlugin::EEnable:
+ r = myPlugin.Enable();
+ break;
+ case RLoggerConn::EDisable:
+ r = myPlugin.Disable();
+ break;
+ default:
+ break;
+ }
+ return r;
+ }
+
+void CMyPluginConn::DoRequest(CFsPluginConnRequest& aRequest)
+ {
+ return KErrNotSupported;
+ }
+
+void DoCancel(TInt aReqMask)
+ {
+ // Not required as no asynchronous request support
+ }
+