This section describes the sharing of file handles between two processes, and the APIs provided for this purpose. This feature is only supported on Symbian platform versions with EKA2 architecture.
It is possible to share file handles between two processes, allowing an open file to be passed from one process to another. This is a necessary feature in secure versions of Symbian platform.
Some further implementation considerations are provided to support this mechanism. A description of passing file handles is given, followed by examples, which show you how to implement a binary using this feature.
See also
File Server Client Side Overview
This section contains the following topics:
Allowing an open file to be passed from one process to another is a necessary feature in secure versions of Symbian platform. The platform security model provides a data caging mechanism, allowing processes and the OS itself to hide private data from other processes.
However, while it is desirable to keep a file safe from other processes, it is useful to give a specific process access to the file:
without the need to give that process any special capabilities
without the need to give the recipient of the file the full path name of that file.
New RFile
member functions enable an open file
handle to be passed from client to a server, from server to a client or from
one process to another process.
Note that file handles are not really handles in the usual sense of referring to a kernel object, but are simply numbers that refer to an open file within a file server session.
The following RFile
member
functions support the sharing of file handles between processes:
The owner of an open Rfile object uses the Transfer***()
member
functions to transfer ownership of that object to another process. Note that
the file-server session must be marked as shareable by calling RFs::ShareProtected()
before
any file handles are transferred, otherwise the Transfer***()
functions
return KErrBadHandle.
A receiving process uses
the Adopt***()
member functions to adopt an RFile
object
passed from another process.
A receiving process uses the RFile::Name()
member
function to retrieve the file name and extension (but not the path) from the
adopted RFile
object. This is needed mainly by recognisers,
which sometimes examine a file’s extension to determine whether it is valid
or not.
The RFile::Duplicate()
member function
allows a process to clone a received RFile
object so that,
for example, two separate functions or threads in the receiving process can
read the file independently.
As another example, consider a client
that only grants access to its data caged private area files, to a
specific system server. The process that opens the file and shares it (using
one of the Transfer***()
functions) controls read and write
access to the file, while the corresponding client or server simply calls
one of the Adopt***()
functions. In effect, the Adopt***()
functions
behave like a file opening API such as RFile::temp()
, RFile::Create()
and RFile::Open()
.
The adopted file retains the access attributes of the file set by the process
doing the sharing. The access attributes of the file can be changed by the
adopting process if the security model permits it.
The following sections present example code and describe how to share a file between two processes. This example code uses a simple server that offers the kind of APIs required to implement the sharing. Details of this server are not given.
Note that for the sake of clarity, there is no error checking in the example code.
In this example, a client passes an open file’s handle to a server. The server in turn passes the handle over to a second server.
The example assumes that the paths and files
used exist and are correct. A file server session (it is recommended that
this session is used specifically for this purpose) is set as shared by
calling RFs::ShareProtected()
. This enables the session
to be used by another process. The file is then opened (it exists in the client’s
private directory). The subsession handle and the fileserver session are then
passed to the server using RFile::TransferToServer()
. The
file handle can now be adopted by the server’s process.
Client code:
RFileHandleSharer handsvr; // handle to server1 User::LeaveIfError(handsvr.Connect()); // connect to server1 CleanupClosePushL(handsvr); RFs fs; User::LeaveIfError(fs.Connect()); CleanupClosePushL(fs); User::LeaveIfError(fs.ShareProtected()); RFile file; User::LeaveIfError(file.Open(fs, _L("test.txt"), EFileRead)); CleanupClosePushL(file); // store the RFs handle in message slot 0 and the RFile handle in slot 1 TIpcArgs ipcArgs; User::LeaveIfError(file.TransferToServer(ipcArgs, 0, 1)); // send to server User::LeaveIfError(handsvr.SendReceive(EMsgXXX, ipcArgs)); // ...continue to use file // CleanupStack::PopAndDestroy(3); // close file, fs, and handsvr
Server1 code:
void CFHSession::PassFileHandleL(const RMessage2& aMsg) { RFileHandleSharer2 handsvr2; // connect to server2 User::LeaveIfError(handsvr2.Connect()); CleanupClosePushL(handsvr2); RFile file; // Adopt the file using the RFs handle from message slot 0 and the RFile handle from slot 1 User::LeaveIfError(file.AdoptFromClient(aMsg, 0, 1)); CleanupClosePushL(file); // Use the file // … // pass the file handle on to server2 TIpcArgs ipcArgs; User::LeaveIfError(file.TransferToServer(ipcArgs, 0, 1)); User::LeaveIfError(handsvr2.SendReceive(EMsgXXX, ipcArgs)); // continue to use file // … CleanupStack::PopAndDestroy(2); // close file and handsvr2 aMsg.Complete(KErrNone); }
Server2 code:
void CFHSession2::PassFileHandleL(const RMessage2& aMsg) { RFile file; // Adopt the file using the RFs handle from message slot 0 and the RFile handle from slot 1 User::LeaveIfError(file.AdoptFromClient(aMsg, 0, 1)); CleanupClosePushL(file); // ..use the file CleanupStack::PopAndDestroy(); // close file aMsg.Complete(KErrNone); }
This example is similar to Example 1 but there are some differences.
A client connects to a server that provides a shared fileserver session and a file handle to an open file when requested.
Client code:
RFileHandleSharer handsvr; // handle to server User::LeaveIfError(handsvr.Connect()); // connect to server CleanupClosePushL(handsvr); // Retrieve the RFs and RFile handles from the server TInt fsh; // session (RFs) handle TPckgBuf<TInt> fh; // sub-session (RFile) handle fsh = handsvr.SendReceive(EMsgXXX, TIpcArgs(&fh)); // pointer to fh in slot 0 User::LeaveIfError(fsh); // Adopt the file using the returned handles RFile file; User::LeaveIfError(file.AdoptFromServer(fsh, fh())); CleanupClosePushL(file); // ..use the file // CleanupStack::PopAndDestroy(2); // close file and handsvr
Server code:
void CFHSession::GetFileHandleL(const RMessage2& aMsg) { RFs fs; User::LeaveIfError(fs.Connect()); CleanupClosePushL(fs); User::LeaveIfError(fs.ShareProtected()); RFile file; User::LeaveIfError(file.Open(fs, _L("test.txt"), EFileRead)); // transfer to client: store the RFile handle into the package buffer in slot 0 // and complete the message with the RFs handle // NB this assumes that if TransferToClient() return an error, then // the standard CServer2::RunError() will complete the message User::LeaveIfError(file.TransferToClient(aMsg, 0)); ASSERT(aMsg.IsNull()); // message should have been completed file.Close(); CleanupStack::PopAndDestroy(1); // fs }
Creator code:
RFs fs; User::LeaveIfError(fs.Connect()); CleanupClosePushL(fs); User::LeaveIfError(fs.ShareProtected()); RFile file; User::LeaveIfError(file.Open(fs, _L("test.txt"), EFileRead)); CleanupClosePushL(file); // create test process RProcess p; User::LeaveIfError(p.Create(_L("FHServer.exe"), KNullDesC)); CleanupClosePushL(p); // transfer to process storing the RFs handle into environment slot 1 and the RFile handle into slot 2 // NB slot 0 is reserved for the command line User::LeaveIfError(file.TransferToProcess(p, 1, 2)); // Wait for handle to be transferred; wrap in an active object if blocking this thread is undesirable TRequestStatus transStatus; p.Rendezvous(transStatus); if(transStatus != KRequestPending) { // Process creation failed p.RendezvousCancel(transStatus); p.Kill(0); User:Leave(transStatus.Int()); } // Start the process p.Resume(); User::WaitForRequest(transStatus); User::LeaveIfError(transfStatus.Int()); // Now we can safely close the fs CleanupStack::PopAndDestroy(3); // close p, file, and fs
Created process code:
RFile file; // Adopt the file using the RFs handle into environment slot 1 and the RFile handle into slot 2 User::LeaveIfError(file.AdoptFromCreator(1)); CleanupClosePushL(file); RProcess::Rendezvous(KErrNone); // Signal transfer completed successfully // ..use the file // CleanupStack::PopAndDestroy(); // close file
An RFile
object contains two handles:
When a file handle is adopted by the receiver, the new RFile
object
that is created by the Adopt***()
member functions contains
a duplicate of the sender’s file server handle. This new RFile
object,
however, has a flag set in the subsession handle to allow it to close its
associated file server handle automatically whenever RFile::Close()
is
called. This removes the need to maintain a separate RFs
object
when adopting a file.
Security issues
It is strongly recommended that the process from which a shared file originates, opens a file server session specifically for this purpose, and closes it after it has finished using the shared file. This is because the session handle is shared along with the file handle, and therefore, any other files opened in that session may be accessible to the other process. The receiving process can increment file handle numbers (remember file handles are only numbers) and gain access to files.
It is also recommended that the adopting process does not open other files with the session it receives, as the process that shared the file may do the same.
RFs and RFile lifetime issues
The RFile
subsession
handle passed to Transfer***()
and Adopt***()
member
functions is duplicated by the Transfer***()
functions so
that the original RFile
handle can either be closed immediately
or used for a period and then closed. This is because it is not the same handle
as the one being adopted.
If there is no need to continue using the RFile
handle,
then the RFs
handle can be safely closed as follows:
For TransferToServer()
or AdoptFromClient()
,
the RFs
handle can be safely closed once the server has
called AdoptFromClient()
. This would normally be done on
return from a synchronous call.
For TransferToClient()
or AdoptFromServer()
,
the RFs
handle can be safely closed once the server has
called TransferToClient()
.
For TransferToProcess()
or AdoptFromCreator()
,
the RFs
handle can be safely closed once the client has
called TransferToProcess()
.
Extensive
testing of implementations of this mechanism are not required as, in effect,
you are simply using a file server subsession (RFile
) API.
It is recommended that you test the client-server system, but that will be
specific to how you use it. If it works you have probably implemented it correctly,
provided you adhere to the security considerations of the shared file.
Passing files between processes is a useful tool, but it is important that you adhere to the security advice.
To share a file handle, take the following steps:
Ensure the file exists. A file to be shared between two processes is a resource that lives in a system or private location for security.
Set the fileserver session as shareable so that it can be used by more that one process. A fileserver session (as with all server sessions) is a Kernel object.
Transfer the session
handle and the subsession handle from the open file using one of the RFile
Transfer***()
member
functions and the message passing system between client and server.
Use one of the RFile
Adopt***()
member
functions to begin using the handles in the receiving process.
Once you have finished,
close the received RFile
object.