|
||
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 OS.
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 OS versions with EKA2 architecture.
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
About Security on the Symbian OS.
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 OS. 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:
RFile::AdoptFromClient()
RFile::AdoptFromServer()
RFile::AdoptFromCreator()
RFile::TransferToServer()
RFile::TransferToClient()
RFile::TransferToProcess()
RFile::Name()
RFile::Duplicate()
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.
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
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);
}
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.
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
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
}
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
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:
a file (or subsession) handle
a file server handle
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.
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.
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.