--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/persistentstorage/sql/SRC/Client/SqlDbSession.cpp Fri Jan 22 11:06:30 2010 +0200
@@ -0,0 +1,857 @@
+// 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 "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:
+//
+
+#include <e32math.h>
+#include <f32file.h>
+#include <f32file64.h>
+#include <s32strm.h>
+#include <s32mem.h>
+#include "IPCBuf.h" //HIpcBuf
+#include "SqlDbSession.h" //RSqlDbSession
+#include "SqlSrvStartup.h" //StartSqlServer()
+#include "SqlResourceTest.h" //TSqlResourceTestData
+#include "SqlSecurityImpl.h" //CSqlSecurityPolicy
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////// TSqlFhCmdFunctor ///////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+TSqlFhCmdFunctor derived classes are used by RSqlDbSession for sending to SQL server localy created/opened
+file handles.
+
+@see RSqlDbSession
+@see RSqlDbSession::CreateAndSendFileHandle()
+
+@internalComponent
+*/
+NONSHARABLE_CLASS(TSqlFhCmdFunctor)
+ {
+public:
+ TSqlFhCmdFunctor(RSqlDbSession& aDbSession, const TDesC& aDbFileName, const TDesC8* aConfig=NULL) :
+ iDbSession(aDbSession),
+ iDbFileName(aDbFileName),
+ iConfig(aConfig) // ownership not transferred
+ {
+ }
+ virtual TInt operator()(RFile64& aFile, TBool aCreated, TBool aReadOnly) = 0;
+
+public:
+ RSqlDbSession& iDbSession;
+ const TDesC& iDbFileName;
+ const TDesC8* iConfig;
+ };
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////// TSqlFhOpenCmdFunctor //////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+Implements the sending of ESqlSrvDbOpenFromHandle command to the SQL server.
+
+@internalComponent
+*/
+NONSHARABLE_CLASS(TSqlFhOpenCmdFunctor) : public TSqlFhCmdFunctor
+ {
+public:
+ TSqlFhOpenCmdFunctor(RSqlDbSession& aDbSession, const TDesC& aDbFileName,const TDesC8* aConfig=NULL) :
+ TSqlFhCmdFunctor(aDbSession, aDbFileName, aConfig)
+ {
+ }
+ virtual TInt operator()(RFile64& aDbFile, TBool aCreated, TBool aReadOnly);
+
+ };
+
+/**
+Sends a command to the SQL server to use the supplied file session and file handles for adopting,
+when opening/creating a database.
+
+Usage of the IPC call arguments:
+ - Arg 0: [out] The 32 bits of the argument are used as follow:
+ @code
+ MSB LSB
+ 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+ Ro Cr C C C C C C C C C C C C C C F F F F F F F F F F F F F F F F
+ @endcode
+ Where:
+ @code
+ - "Ro" - read-only flag, true if the file is read-only;
+ - "Cr" - create/open flag, true if the file was created, false if the file was opened;
+ - "C" - config string length;
+ - "F" - database file name length;
+ @endcode
+ - Arg 1: [out] database file name + configuration string (if there is a configuration string)
+ - Arg 2: [out] file session handle
+ - Arg 3: [out] database file handle
+*/
+TInt TSqlFhOpenCmdFunctor::operator()(RFile64& aDbFile, TBool aCreated, TBool aReadOnly)
+ {
+ const TInt KConfigStrLen = iConfig ? iConfig->Length() : 0;
+ if(KConfigStrLen > KSqlSrvMaxConfigStrLen)
+ {
+ return KErrArgument;
+ }
+ TIpcArgs ipcArgs;
+ HBufC* arg1Buf = HBufC::New(iDbFileName.Length() + KConfigStrLen);
+ if(!arg1Buf)
+ {
+ return KErrNoMemory;
+ }
+ TPtr arg1 = arg1Buf->Des();
+ arg1.Copy(iDbFileName);
+ if(iConfig)
+ {
+ TBuf<KSqlSrvMaxConfigStrLen> cfgBuf;
+ cfgBuf.Copy(*iConfig);
+ arg1.Append(cfgBuf);
+ }
+ TUint32 arg0 = iDbFileName.Length() | (KConfigStrLen << 16);
+ if(aReadOnly)
+ {
+ arg0 |= 0x80000000;
+ }
+ if(aCreated)
+ {
+ arg0 |= 0x40000000;
+ }
+ ipcArgs.Set(0, arg0);
+ ipcArgs.Set(1, &arg1);
+ TInt err = aDbFile.TransferToServer(ipcArgs, 2, 3);
+ if(err == KErrNone)
+ {
+ err = iDbSession.SendReceive(ESqlSrvDbOpenFromHandle, ipcArgs);
+ }
+ delete arg1Buf;
+ return err;
+ }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////// TSqlFhAttachCmdFunctor ////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+Implements the sending of ESqlSrvDbAttachFromHandle command to the SQL server.
+
+@internalComponent
+*/
+NONSHARABLE_CLASS(TSqlFhAttachCmdFunctor) : public TSqlFhCmdFunctor
+ {
+public:
+ TSqlFhAttachCmdFunctor(RSqlDbSession& aDbSession, const TDesC& aDbFileName,
+ const TDesC& aDbName) :
+ TSqlFhCmdFunctor(aDbSession, aDbFileName),
+ iDbName(aDbName)
+ {
+ }
+ virtual TInt operator()(RFile64& aDbFile, TBool aCreated, TBool aReadOnly);
+
+private:
+ void SerializeToStreamL(RWriteStream& aStream);
+
+private:
+ const TDesC& iDbName;
+
+ };
+
+/**
+Sends a command to the SQL server to use the supplied file session and file handles for adopting,
+when attaching a database.
+
+Usage of the IPC call arguments:
+ - Arg 0: [out] The 32 bits of the argument are used as follow:
+ @code
+ MSB LSB
+ 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+ Ro F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F F
+ @endcode
+ Where:
+ @code
+ - "Ro" - read-only flag, true if the file is read-only;
+ - "F" - database file name length;
+ @endcode
+ - Arg 1: [out] db names buffer
+ - Arg 2: [out] file session handle
+ - Arg 3: [out] database file handle
+*/
+TInt TSqlFhAttachCmdFunctor::operator()(RFile64& aDbFile, TBool /*aCreated*/, TBool aReadOnly)
+ {
+ const TInt KMaxBufLen = iDbFileName.Length() * sizeof(TText) + iDbName.Length() * sizeof(TText) + 32;//"32" should be enough for all additional data like name length, alignment, etc.
+ HBufC8* buf = HBufC8::New(KMaxBufLen);
+ if(!buf)
+ {
+ return KErrNoMemory;
+ }
+ TPtr8 bufPtr = buf->Des();
+ RDesWriteStream out(bufPtr);
+ TRAPD(err, SerializeToStreamL(out));
+ if(err == KErrNone)
+ {
+ TUint32 arg0 = (TUint32)bufPtr.Length() | (aReadOnly ? 0x80000000 : 0);
+ TIpcArgs ipcArgs(arg0, &bufPtr);
+ err = aDbFile.TransferToServer(ipcArgs, 2, 3);
+ if(err == KErrNone)
+ {
+ err = iDbSession.SendReceive(ESqlSrvDbAttachFromHandle, ipcArgs);
+ }
+ }
+ delete buf;
+ return err;
+ }
+
+/**
+Serializes TSqlFhAttachCmdFunctor object content to a stream (aStream parameter).
+*/
+void TSqlFhAttachCmdFunctor::SerializeToStreamL(RWriteStream& aStream)
+ {
+ aStream << iDbFileName;
+ aStream << iDbName;
+ aStream.CommitL();
+ }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+//Database type.
+enum TDbType
+ {
+ EDbTypeUnknown, //The database resides outside the client's private data cage
+ EDbTypeClientPrivate //The database resides in the client's private data cage
+ };
+
+/**
+Returns the database type.
+
+@param aDbFileName Database file name, including the path. If it is a request for creating/opening
+ secure database, then the name format is <drive>:<[SID]database_file_name>.
+
+@return EDbTypeClientPrivate If the database resides in the client's private data cage;
+ EDbTypeUnknown This may be a database in the SQL server private data cage or somewhere else;
+ If the error code is less than 0, then the function cannot determine the database type
+ (the function was unable to connect the local file session instance).
+
+@internalComponent
+*/
+static TInt GetDbType(const TDesC& aDbFileName)
+ {
+ RFs fs;
+ TInt err = fs.Connect();
+ if(err == KErrNone)
+ {
+ TFileName clientPrivatePath;
+ err = fs.PrivatePath(clientPrivatePath);
+ fs.Close();
+ if(err == KErrNone)
+ {
+ TInt pos = aDbFileName.FindF(clientPrivatePath);
+ return (TUint)pos <= (TUint)KMaxDriveName ? EDbTypeClientPrivate : EDbTypeUnknown;
+ }
+ }
+ return err;
+ }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////
+////////////////////// RSqlDbSession /////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+/**
+Establishes a connection with the SQL server.
+
+The method initializes RSqlDbSession object establishing a connection with the SQL server.
+If the connection has established successfully, the method sends a message to the server to
+create or open (depending on aFunction parameter) a database file with aDbFileName name.
+
+@param aFunction Specifies which operation has to be performed:
+ ESqlSrvDbCreate - Create a shared non-secure or private secure database;
+ ESqlSrvDbCreateSecure - Create a shared secure database;
+ ESqlSrvDbOpen - Open a shared non-secure, shared secure or private secure database;
+
+@param aDbFileName Database file name, including the path. If it is a request for creating/opening
+ secure database, then the name format is <drive>:<[SID]database_file_name>.
+@param aSecurityPolicyData Security policy data. Non-empty descriptor only for secure databases
+@param aConfig the configuration string "PARAM=VALUE;...."
+
+@return KErrNone, the operation completed successfully;
+ KErrNoMemory, an out of memory condition has occured;
+ KErrBadName, bad database file name: zero length, directory name;
+ KErrAlreadyExists, database file already exists;
+ KErrNotReady, the drive does not exist or is not ready;
+ KErrInUse, the file has been opened already;
+ KErrNotFound, file not found;
+ KErrGeneral, no or invalid security policies (if the database to be opened is a secure database with bad security data);
+ KErrNotSupported, incompatible sql security version (if the database to be opened is a secure database with bad security data);
+ KErrPermissionDenied, the caller does not satisfy the relevant database security policies.
+ Note that database specific errors categorised as ESqlDbError, and
+ other system-wide error codes may also be returned.
+ KErrArgument invalid config string or config string length exceeds KSqlSrvMaxConfigStrLen.
+
+Usage of the IPC call arguments:
+ - Arg 0: [out] database file name length
+ - Arg 1: [out] database file name
+ - Arg 2: [out] security policies buffer length if aFunction is ESqlSrvDbCreateSecure
+ - Arg 3: [out] security policies buffer if aFunction is ESqlSrvDbCreateSecure
+*/
+TInt RSqlDbSession::Connect(TSqlSrvFunction aFunction, const TDesC& aDbFileName, const TDesC8& aSecurityPolicyData, const TDesC8* aConfig)
+ {
+ const TInt KDefaultMsgBufLen = 128;
+ iLastErrorMessage = HBufC::New(KDefaultMsgBufLen);
+ if(!iLastErrorMessage)
+ {
+ return KErrNoMemory;
+ }
+ TInt err = DoCreateSession();
+ if(err == KErrNone)
+ {
+ err = InitResourceTestData();
+ if(err == KErrNone)
+ {
+ err = ::GetDbType(aDbFileName);
+ if(err >= 0)
+ {
+ if(err == EDbTypeClientPrivate)
+ {//The database is in the client's private data cage. Set err = KErrPermissionDenied.
+ err = KErrPermissionDenied;
+ }
+ else
+ {//Unknown database type. Try to connect.
+ err = DoConnect(aFunction, aDbFileName, aSecurityPolicyData, aConfig);
+ }
+ if(err == KErrPermissionDenied && aFunction != ESqlSrvDbCreateSecure)
+ {
+ //What do we have here now? - the operation is create/open non-secure, the error is KErrPermissionDenied.
+ //So, the dll will try now to create/open the database in the application's private data cage.
+ //What if the used database file name was "C:MyDb.db"!? - the dll will try to create/open
+ //"C:MyDb.db" in the application's data cage and there
+ //is a possibility that this operation my return a result, like KErrNotFound, which will hide the original
+ //error code (KErrPermissionDenied).
+ //The dll shall not try to create/open file in the application's data cage, if the format of the
+ //database file name is secure.
+ TParsePtrC parse(aDbFileName);//this call may panic if aDbFileName cannot be parsed, but it will panic the client
+ if(parse.PathPresent())
+ {
+ //The caller or the SQL server has no enough rights to create or open the database with aDbFileName name.
+ //One of the reasons may be that the database file is in the caller's private data cage where the SQL
+ //server cannot create/open the database file.
+ //The SQL dll will try to create/open the database file on the client side and pass the file handle to the
+ //SQL server.
+ TSqlFhOpenCmdFunctor fhOpenCmdSender(*this, aDbFileName, aConfig);
+ err = CreateAndSendFileHandle(fhOpenCmdSender,
+ aFunction == ESqlSrvDbCreate ? RSqlDbSession::EDbfCreate : RSqlDbSession::EDbfOpen);
+ }
+ }
+ }
+ }
+ }
+ if(err != KErrNone)
+ {
+ Close();
+ }
+ return err;
+ }
+
+/**
+Sends a command to the server to attach an existing database to the current connection.
+
+The database can be:
+@code
+ - shared secure database;
+ - shared non-secure database;
+ - private secure database;
+@endcode
+
+@param aDbFileName Database file name.
+@param aDbName Logical database name.
+
+@return KErrNone, the operation completed successfully;
+ KErrNoMemory, an out of memory condition has occured;
+ KErrBadName, bad database file name: zero length, directory name;
+ KErrNotReady, the drive does not exist or is not ready;
+ KErrInUse, the file has been opened already;
+ KErrNotFound, file not found;
+ KErrGeneral, no or invalid security policies (if the database to be opened is a secure database with bad security data);
+ KErrNotSupported, incompatible sql security version (if the database to be opened is a secure database with bad security data);
+ KErrPermissionDenied, the caller does not satisfy the relevant database security policies.
+ Note that database specific errors categorised as ESqlDbError, and
+ other system-wide error codes may also be returned.
+
+Usage of the IPC call arguments:
+Arg 0: [out] Database file name length.
+Arg 1: [out] Database file name.
+Arg 2: [out] Logical database name length.
+Arg 3: [out] Logical database name.
+*/
+TInt RSqlDbSession::Attach(const TDesC& aDbFileName, const TDesC& aDbName)
+ {
+ TInt err = ::GetDbType(aDbFileName);
+ if(err >= 0)
+ {
+ if(err == EDbTypeClientPrivate)
+ {//The database is in the client's private data cage. Set err = KErrPermissionDenied.
+ err = KErrPermissionDenied;
+ }
+ else
+ {//Unknown database type. Try to attach.
+ err = SendReceive(ESqlSrvDbAttach, TIpcArgs(aDbFileName.Length(), &aDbFileName, aDbName.Length(), &aDbName));
+ }
+ if(err == KErrPermissionDenied)
+ {
+ //What do we have here now? - the operation is "attach", the error is KErrPermissionDenied.
+ //Which means, it is (possibly) a secure database in the server private data cage and the caller
+ //failed to pass the security checks.
+ //So, the dll will try now to open the database assuming it is in the application's private data cage
+ //and pass the file and session handles to the server which will try to attach the database using the handles.
+ //What if the used database file name was "C:MyDb.db"!? (which means that the client's intention was
+ //to attach a public shared secure database)
+ //The dll will try to open "C:MyDb.db" in the application's data cage and there
+ //is a possibility that this operation may return a result, like KErrNotFound, which will
+ //hide the original error code (KErrPermissionDenied).
+ //The dll shall not try to attach a database from the application's data cage, if the format of the
+ //database file name is secure.
+ TParsePtrC parse(aDbFileName);//this call may panic if aDbFileName cannot be parsed, but it will panic the client
+ if(parse.PathPresent())
+ {
+ TSqlFhAttachCmdFunctor fhAttachCmdSender(*this, aDbFileName, aDbName);
+ err = CreateAndSendFileHandle(fhAttachCmdSender, RSqlDbSession::EDbfOpen);
+ }
+ }
+ }
+ return err;
+ }
+
+/**
+Copies a database.
+
+The method establishes a temporary connection with the SQL server and issues a request for copying
+aSrcDbFileName database file to aDestDbFileName file. After the server completes the request
+CopyDatabase() closes the connection.
+
+@param aSrcDbFileName Source database file name.
+@param aDestDbFileName Destination database file name.
+
+@return KErrNone, the operation completed successfully;
+ KErrNoMemory, an out of memory condition has occured;
+ KErrBadName, bad database file name: zero length, directory name;
+ KErrAlreadyExists, target database file already exists;
+ KErrNotReady, the drive does not exist or is not ready;
+ KErrInUse, the file has been opened already;
+ KErrNotFound, file not found;
+ KErrPermissionDenied, the SID of the calling application does not match the SID of source or destination database.
+ Note that other system-wide error codes may also be returned.
+
+Usage of the IPC call arguments:
+Arg 0: [out] source database file name length
+Arg 1: [out] source database file name
+Arg 2: [out] destination database file name length
+Arg 3: [out] destination database file name
+*/
+TInt RSqlDbSession::CopyDatabase(const TDesC& aSrcDbFileName, const TDesC& aDestDbFileName)
+ {
+ RSqlDbSession sess;
+ TInt err = sess.DoCreateSession();
+ if(err == KErrNone)
+ {
+ err = sess.InitResourceTestData();
+ if(err == KErrNone)
+ {
+ err = sess.SendReceive(ESqlSrvDbCopy, TIpcArgs(aSrcDbFileName.Length(), &aSrcDbFileName, aDestDbFileName.Length(), &aDestDbFileName));
+ }
+ sess.Close();
+ }
+ return err;
+ }
+
+/**
+Deletes a database.
+
+The database can be:
+@code
+ - shared secure database;
+ - shared non-secure database;
+ - private secure database;
+@endcode
+
+The method establishes a temporary connection with the SQL server and issues a "delete" request regarding
+aDbFileName database file. After the server completes the request DeleteDatabase() closes the connection.
+
+@param aDbFileName Database file name. It must include the path if it is not a secure database.
+
+@return KErrNone, the operation completed successfully;
+ KErrNoMemory, an out of memory condition has occured;
+ KErrBadName, bad database file name: zero length, directory name;
+ KErrNotReady, the drive does not exist or is not ready;
+ KErrInUse, the file has been opened already;
+ KErrNotFound, file not found;
+ KErrAccessDenied, access to the database file is denied (it might be a read-only file);
+ KErrPermissionDenied, the SID of the calling application does not match the SID of the database.
+ Note that other system-wide error codes may also be returned.
+
+Usage of the IPC call arguments:
+Arg 0: [out] database file name length
+Arg 1: [out] database file name
+*/
+TInt RSqlDbSession::DeleteDatabase(const TDesC& aDbFileName)
+ {
+ RSqlDbSession sess;
+ TInt err = sess.DoCreateSession();
+ if(err == KErrNone)
+ {
+ err = sess.InitResourceTestData();
+ if(err == KErrNone)
+ {
+ err = ::GetDbType(aDbFileName);
+ if(err >= 0)
+ {
+ if(err == EDbTypeClientPrivate)
+ {//The database is in the client's private data cage. Set err = KErrPermissionDenied.
+ err = KErrPermissionDenied;
+ }
+ else
+ {//Unknown database type. Try to delete.
+ err = sess.SendReceive(ESqlSrvDbDelete, TIpcArgs(aDbFileName.Length(), &aDbFileName));
+ }
+ if(err == KErrPermissionDenied)
+ {
+ //What do we have here now? - the operation is "delete db", the error is KErrPermissionDenied.
+ //So, the dll will try now to delete a database with the same name from the application's private data cage.
+ //What if the used database file name was "C:MyDb.db" and there is a such file in server's private
+ //data cage? - the dll will try to delete a file with name "C:MyDb.db" from the application's data cage
+ //and there is a possibility that this operation my pass or may return strange result, like KErrNotFound.
+ //Bith cases are not what the user would expect.
+ //The dll shall not try to delete a file from the application's data cage, if the format of the
+ //database file name is secure.
+ TParsePtrC parse(aDbFileName);//this call may panic if aDbFileName cannot be parsed, but it will panic the client
+ if(parse.PathPresent())
+ {
+ //The caller or the SQL server has no enough rights to delete the database with aDbFileName name.
+ //One of the reasons may be that the database file is in the caller's private data cage where the SQL
+ //server cannot delete the database file.
+ //The SQL dll will try to delete the database file on the client side.
+ RFs fs;
+ err = fs.Connect();
+ if(err == KErrNone)
+ {
+ err = fs.Delete(aDbFileName);
+ fs.Close();
+ }
+ }
+ }
+ }
+ }
+ sess.Close();
+ }
+ return err;
+ }
+
+/**
+Retrieves a reference to the textual description of the error returned by the
+most recent call to any of the functions:
+- RSqlDatabase::Exec()
+- RSqlStatement::Exec()
+- RSqlStatement::Next()
+- RSqlStatement::Reset()
+
+Note that the function can only return a reference to text for
+database-specific type errors, i.e. those errors that are categorised as of
+type ESqlDbError.
+
+If an error occurs during the retrieval of the last error message, the function silently ignores the error
+and returns a NULL descriptor.
+
+@return A non-modifiable pointer descriptor representing the most recent error
+ message. Note that message may be NULL, i.e. the descriptor may have
+ zero length.
+
+Usage of the IPC call arguments:
+Arg 0: [out] max length of the message buffer
+Arg 1: [in/out] buffer for the last error message
+*/
+TPtrC RSqlDbSession::LastErrorMessage()
+ {
+ TPtr msg(iLastErrorMessage->Des());
+ msg.Zero();
+ TInt size = 0;
+ while((size = SendReceive(ESqlSrvLastErrorMsg, TIpcArgs(msg.MaxLength(), &msg))) > KSqlClientBufOverflowCode)
+ {
+ HBufC* newMsgBuf = iLastErrorMessage->ReAlloc(size - KSqlClientBufOverflowCode);
+ if(!newMsgBuf)
+ {
+ break;
+ }
+ iLastErrorMessage = newMsgBuf;
+ msg.Set(iLastErrorMessage->Des());
+ }
+ return msg;
+ }
+
+/**
+Returns the ROWID of the most recent successful INSERT into the database
+from this database connection.
+
+@return >0, the ROWID of the most recent successful INSERT into the database
+ from this database connection;
+ 0, if no successful INSERTs have ever occurred from this database connection
+ <0, if one of the system-wide error codes is returned
+
+Usage of the IPC call arguments:
+Arg 0: [in/out] the receiving buffer for the last inserted ROWID
+*/
+TInt64 RSqlDbSession::LastInsertedRowId()
+ {
+ TInt64 res;
+ TPtr8 ptr(reinterpret_cast <TUint8*> (&res), sizeof(res));
+ TInt err = SendReceive(ESqlSrvDbLastInsertedRowId, TIpcArgs(&ptr));
+ return err == KErrNone ? res : err;
+ }
+
+/**
+Retrieves the database security policies.
+
+@return A pointer to the created and internalized CSqlSecurityPolicy instance.
+
+@return KErrNone, the operation has completed successfully;
+ KErrNoMemory, an out of memory condition has occurred;
+ Note that the function may leave with some database specific errors categorised as
+ ESqlDbError or other system-wide error codes.
+
+Usage of the IPC call arguments:
+Arg 0: [out] security policy buffer size
+Arg 1: [in/out] buffer for the database security policies
+*/
+TInt RSqlDbSession::GetSecurityPolicy(RSqlBufFlat& aSecurityPolicyBuf)
+ {
+ TPtr8 ptr(aSecurityPolicyBuf.BufPtr());
+ TInt rc = KErrNone;
+ while((rc = SendReceive(ESqlSrvDbGetSecurityPolicy, TIpcArgs(ptr.MaxLength(), &ptr))) > KSqlClientBufOverflowCode)
+ {
+ rc = aSecurityPolicyBuf.ReAlloc(rc - KSqlClientBufOverflowCode);
+ if(rc != KErrNone)
+ {
+ break;
+ }
+ ptr.Set(aSecurityPolicyBuf.BufPtr());
+ }
+ return rc;
+ }
+
+/**
+Closes the database and releases the connection with the database server.
+*/
+void RSqlDbSession::Close()
+ {
+ if(Handle())
+ {
+ (void)SendReceive(ESqlSrvDbClose);
+ }
+ TSqlResourceTestData::Release();
+ delete iLastErrorMessage;
+ iLastErrorMessage = NULL;
+ RSessionBase::Close();
+ }
+
+/**
+The method establishes a connection with the SQL server.
+
+@return KErrNone, The connection was established successfully;
+ KErrAlreadyExists, the connection already exists.
+ The function may also return some other system-wide error codes.
+*/
+TInt RSqlDbSession::DoCreateSession()
+ {
+ const TInt KTimesToRetryConnection = 2;
+ TInt retry = KTimesToRetryConnection;
+ for(;;)
+ {
+ TInt err = CreateSession(KSqlSrvName, ::SqlSrvVersion());
+ if(err != KErrNotFound && err != KErrServerTerminated)
+ {
+ return err;
+ }
+ if(--retry == 0)
+ {
+ return err;
+ }
+ err = ::StartSqlServer();
+ if(err != KErrNone && err != KErrAlreadyExists)
+ {
+ return err;
+ }
+ }
+ }
+
+/**
+The method gets called immediatelly after the establishing client-server connection but before the
+create/open database message.
+If a request was made by the client (using TSqlResourceTester methods) to test the server under out of memory
+conditions, InitResourceTestData() will send this request to the server, putting the just created connection
+in a "out of memory" test mode.
+*/
+TInt RSqlDbSession::InitResourceTestData()
+ {
+ TInt err = KErrNone;
+#ifdef _DEBUG
+ TSqlResourceTestData* data = TSqlResourceTestData::Instance();
+ if(data)
+ {
+ data->Init(*this);
+ }
+ else
+ {
+ err = KErrNoMemory;
+ }
+#endif
+ return err;
+ }
+
+/**
+Creates/opens database file locally and sends the file handle to the SQL server.
+
+This function is used only when the SQL server fails to create/open/attach the requested database file with
+KErrPermissionDenied error. One of the reasons for that error may be that the database file is in the
+calling application's private data cage. In this case an attempt is made to create/open the database file locally
+and if the operation completes successfully, then the file handle is passed to the SQL server.
+
+@param aFhCmdSender A reference to a functor instance which is used for sending the file handles to the SQL server.
+@param aDbfAction It is set by the caller to RSqlDbSession::EDbfCreate if the database file has to be created or
+ to RSqlDbSession::EDbfOpen if the database file already exists and has to be opened
+
+@return KErrNone, the operation completed successfully;
+ KErrNoMemory, an out of memory condition has occured;
+ KErrBadName, bad database file name: zero length, directory name, missing drive in the file name;
+ KErrAlreadyExists, database file already exists;
+ KErrNotReady, the drive does not exist or is not ready;
+ KErrInUse, the file has been opened already;
+ KErrNotFound, file not found;
+ KErrPermissionDenied, the caller does not satisfy the relevant database security policies.
+ Note that database specific errors categorised as ESqlDbError, and
+ other system-wide error codes may also be returned.
+*/
+TInt RSqlDbSession::CreateAndSendFileHandle(TSqlFhCmdFunctor& aFhCmdSender, RSqlDbSession::TDbfAction aDbfAction)
+ {
+ //Create a file session
+ RFs fs;
+ TInt err = fs.Connect();
+ if(err != KErrNone)
+ {
+ return err;
+ }
+ //Share the file session
+ err = fs.ShareProtected();
+ if(err != KErrNone)
+ {
+ fs.Close();
+ return err;
+ }
+ //Create/open the database file locally
+ TBool readOnly = EFalse;
+ TBool fileCreated = EFalse;
+ RFile64 dbFile;
+ if(aDbfAction == RSqlDbSession::EDbfCreate)
+ {//Create the database file in R/W mode
+ err = dbFile.Create(fs, aFhCmdSender.iDbFileName, EFileShareAny | EFileWrite);
+ if(err == KErrNone)
+ {
+ fileCreated = ETrue;
+ }
+ }
+ else //aDbfAction == RSqlDbSession::EDbfOpen
+ {//Open the database file in shared R/W mode
+ err = dbFile.Open(fs, aFhCmdSender.iDbFileName, EFileShareAny | EFileWrite);
+ if(err != KErrNone)
+ {//If the the database open operation in R/W mode has failed - try to open the database in shared read-only mode.
+ readOnly = ETrue;
+ err = dbFile.Open(fs, aFhCmdSender.iDbFileName, EFileShareReadersOnly);
+ }
+ }
+ if(err == KErrNone)
+ {//Successful create/open database file operation. Send the database file and session handles to the server.
+ err = aFhCmdSender(dbFile, fileCreated, readOnly);
+ dbFile.Close();
+ }
+ if(err != KErrNone && fileCreated)
+ {
+ dbFile.Close();
+ TInt err2 = fs.Delete(aFhCmdSender.iDbFileName);
+ if(err2 != KErrNone)
+ {//Sometimes it is not possible the file to be deleted, the reported error is - KErrInUse.
+ fs.Close();
+ User::After(100000);
+ err2 = fs.Connect();
+ if(err2 == KErrNone)
+ {
+ (void)fs.Delete(aFhCmdSender.iDbFileName);
+ }
+ }
+ }
+ fs.Close();
+ return err;
+ }
+
+/**
+Prepares the "security & configuration" string and sends the command to the SQL server.
+
+@param aFunction Specifies which operation has to be performed:
+ ESqlSrvDbCreate - Create a shared non-secure or private secure database;
+ ESqlSrvDbCreateSecure - Create a shared secure database;
+ ESqlSrvDbOpen - Open a shared non-secure, shared secure or private secure database;
+
+@param aDbFileName Database file name, including the path. If it is a request for creating/opening
+ secure database, then the name format is <drive>:<[SID]database_file_name>.
+@param aSecurityPolicyData Security policy data. Non-empty descriptor only for secure databases
+@param aConfig the configuration string "PARAM=VALUE;...."
+
+@return KErrNone, the operation completed successfully;
+ KErrNoMemory, an out of memory condition has occured;
+ KErrArgument invalid config string or config string length exceeds KSqlSrvMaxConfigStrLen.
+ Other system-wide error codes may also be returned.
+
+*/
+TInt RSqlDbSession::DoConnect(TSqlSrvFunction aFunction, const TDesC& aDbFileName, const TDesC8& aSecurityPolicyData, const TDesC8* aConfig)
+ {
+ const TInt KConfigStrLen = aConfig ? aConfig->Length() : 0;
+ if(KConfigStrLen > KSqlSrvMaxConfigStrLen)
+ {
+ return KErrArgument;
+ }
+ const TInt KSecPolicyLen = aFunction == ESqlSrvDbCreateSecure ? aSecurityPolicyData.Length() : 0;
+ TIpcArgs ipcArgs(aDbFileName.Length(), &aDbFileName);
+ // pack the length xxxxyyyy where xxxx is the policy length
+ // and yyyy is the config length.
+ ipcArgs.Set(2, (TUint)(KSecPolicyLen << 16) | (TUint)KConfigStrLen);
+ const TInt KTotalLen = KConfigStrLen + KSecPolicyLen;
+ ipcArgs.Set(3, 0);
+ HBufC8* arg3Buf = NULL;
+ if(KTotalLen > 0)
+ {
+ arg3Buf = HBufC8::New(KTotalLen);
+ if(!arg3Buf)
+ {
+ return KErrNoMemory;
+ }
+ TPtr8 arg3Ptr = arg3Buf->Des();
+ if(KSecPolicyLen > 0)
+ {
+ arg3Ptr.Copy(aSecurityPolicyData);
+ }
+ if(KConfigStrLen > 0)
+ {
+ //coverity[DEADCODE]
+ //The ASSERT might be useful in catching future defect in this function
+ __SQLASSERT(aConfig != NULL, ESqlPanicInternalError);
+ arg3Ptr.Append(*aConfig);
+ }
+ ipcArgs.Set(3, &arg3Ptr);
+ }
+ TInt err = SendReceive(aFunction, ipcArgs);
+ delete arg3Buf;
+ return err;
+ }