diff -r 000000000000 -r 08ec8eefde2f persistentstorage/sqlite3api/OsLayer/os_symbian_mt.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/persistentstorage/sqlite3api/OsLayer/os_symbian_mt.cpp Fri Jan 22 11:06:30 2010 +0200 @@ -0,0 +1,1703 @@ +// 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: +// os_symbian.cpp +// The Symbian OS porting layer - multi-threaded implementation. +// SQLite never accesses the file system and the OS services directly. +// SQLite uses for that sqlite3_vfs and sqlite3_file objects. +// sqlite3_vfs and sqlite3_file functionality is implemented in this file - +// TVfs and TFileIo classes. +// +// + +/** + @file + @see TVfs + @see TFileIo +*/ + +#ifdef SQLITE_OS_SYMBIAN + +extern "C" + { + #include "sqliteInt.h" + #include "os.h" + #include "os_common.h" + } +#include +#include "os_symbian.h" +#include "UTraceSqlite.h" + +#ifdef SQLITE_TEST + +//Count the number of fullsyncs and normal syncs. This is used to test +//that syncs and fullsyncs are occuring at the right times. +extern "C" int sqlite3_sync_count = 0; +extern "C" int sqlite3_fullsync_count = 0; + +//The following variable, if set to a non-zero value, becomes the result +//returned from sqlite3OsCurrentTime(). This is used for testing. +extern "C" int sqlite3_current_time = 0; + +#endif//SQLITE_TEST + +_LIT(KCwd, ".\\"); + +//Used for the random numbers generation +static inline TInt64& Seed() + { + static TInt64 seed = 0; + if(seed == 0) + { + TTime now; + now.UniversalTime(); + seed = now.Int64(); + } + return seed; + } + +/** +Os2SqliteErr() is called at the end of many of the interface functions of the OS porting layer (wherever it is appropriate - +TFileIo and TVfs interfaces). The purpose of this function is to identify the "out of memory" and "disk is full" errors +reported by the used Symbian OS APIs (aOsErr parameter) and report them to SQLite as SQLITE_FULL and SQLITE_NOMEM errors. +The KErrEof error (TFileIo::Read() can return KErrEof) is reported to SQLite as SQLITE_IOERR_SHORT_READ. The rest of failures +are reported as the error specified in aDefaultErr parameter. + +@param aOsErr Symbian OS error +@param aDefaultErr The default SQLite error that should be used if the aOsErr parameter is not one of: + KErrNone, KErrEof, KErrNoMemory, KErrDiskFull +@return SQLITE_OK, The OS porting layer function call has completed successfully, + SQLITE_IOERR_SHORT_READ, The amount of the data read is less than the requested amount, + SQLITE_IOERR_NOMEM, Out of memory, + SQLITE_FULL, The disk is full, + aDefaultErr, The rest of failures will be reported as aDefaultErr. +*/ +static TInt Os2SqliteErr(TInt aOsErr, TInt aDefaultErr) + { + switch(aOsErr) + { + case KErrNone: + return SQLITE_OK; + case KErrEof: + return SQLITE_IOERR_SHORT_READ; + case KErrNoMemory: + return SQLITE_IOERR_NOMEM; + case KErrDiskFull: + return SQLITE_FULL; + default: +#ifdef _DEBUG + RDebug::Print(_L("SQLite3 C API, Os2SqliteErr(), err=%d\n"), aOsErr); +#endif + break; + } + return aDefaultErr; + } + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////// TStaticFs ///////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** +Connects the file session used by the SQLite OS porting layer. +Single RFs instance per process is used. + +@return KErrNone The operation was completed successfully, + System-wide error code if the operation has failed. +*/ +TInt TStaticFs::Connect() + { + TInt err = iFs.Connect(); + if(err == KErrNone) + { + err = iFs.ShareAuto(); + } + if(err != KErrNone) + { + iFs.Close(); + } + return err; + } + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////// sqlite3_mutex ///////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** +Initializes sqlite3_mutex data members with their default values. +*/ +sqlite3_mutex::sqlite3_mutex() : + iRefCount(0), + iOwnerThreadId(KMaxTUint64) + { + } + +/** +Closes the mutex handle. +*/ +sqlite3_mutex::~sqlite3_mutex() + { + iMutex.Close(); + } + +/** +Gives the calling thread an exclusive access to the SQLite resources (global variables, file handles, buffers, cache, etc.). +The calling thread becomes a mutex owner. +If the mutex is already locked by another thread, the calling thread will block until the other thread releases the mutex. +The method can be called by the mutex owning thread more than once, even if the mutex is already entered. +*/ +void sqlite3_mutex::Enter() + { + iMutex.Wait(); + RThread currThread; + iOwnerThreadId = currThread.Id(); + ++iRefCount; + } + +/** +Unlocks the mutex. If sqlite3_mutex::Enter() was called more than once by the owning thread, then the number of +sqlite3_mutex::Leave() calls must eventually match the number of sqlite3_mutex::Enter() calls. +If there are thread(s) blocked on sqlite3_mutex::Enter(), after the mutex gets unlocked one of the waiting threads +will be able to lock the mutex and get an exclusive access to the guarded resources. + +@panic SqliteMt 23 Negative mutex lock counter (in debug builds only) +@panic SqliteMt 24 The mutex has been entered (locked) by a different thread than the current one (in debug builds only) +*/ +void sqlite3_mutex::Leave() + { + __ASSERT_DEBUG(iRefCount > 0, User::Panic(KPanicCategory, EPanicMutexLockCounter)); +#ifdef _DEBUG + RThread currThread; + __ASSERT_DEBUG(iOwnerThreadId == currThread.Id(), User::Panic(KPanicCategory, EPanicMutexOwner)); +#endif + --iRefCount; + iMutex.Signal(); + } + +/** +Returns true if the mutex is already locked (entered). + +@return True if the mutex is locked, false otherwise +*/ +TBool sqlite3_mutex::IsHeld() const + { + RThread currThread; + return iRefCount != 0 && iOwnerThreadId == currThread.Id(); + } + +/** +Creates the mutex. + +@return KErrNone The operation was completed successfully, + System-wide error code if the operation has failed. +*/ +TInt sqlite3_mutex::Create() + { + return iMutex.CreateLocal(); + } + +/** +Creates new CRecursiveMutex object. + +@return A pointer to the created CRecursiveMutex object or NULL if the operation has failed. +*/ +CRecursiveMutex* CRecursiveMutex::New() + { + CRecursiveMutex* self = new CRecursiveMutex; + if(self) + { + if(self->Create() != KErrNone) + { + delete self; + self = NULL; + } + } + return self; + } + +CRecursiveMutex::~CRecursiveMutex() + { + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////// TMutexApi //////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** +Initializes the mutex system. +No-op function. + +@return SQLITE_OK +*/ +int TMutexApi::Init() + { + SQLUTRACE_PROFILER(0); + return SQLITE_OK; + } + +/** +Finalizes the mutex system. +No-op function. + +@return SQLITE_OK +*/ +int TMutexApi::End() + { + SQLUTRACE_PROFILER(0); + return SQLITE_OK; + } + +/** +Creates a new mutex. +If the request is for a static mutex, a pointer to already created static mutex will be returned. + +@param aType The mutex type: static, fast, recursive +@return A pointer to the created mutex or NULL if the operation has failed +*/ +sqlite3_mutex* TMutexApi::Alloc(int aType) + { + SQLUTRACE_PROFILER(0); + sqlite3_mutex* mutex = NULL; + switch(aType) + { + case SQLITE_MUTEX_FAST: + case SQLITE_MUTEX_RECURSIVE: + mutex = CRecursiveMutex::New(); + break; + default: + mutex = ::StaticMutex(aType - 2); + break; + } + return mutex; + } + +/** +Destroys a mutex, created previously by a call to TMutexApi::Alloc(). +@param aMutex Pointer to the mutex object that has to be destroyed +*/ +void TMutexApi::Free(sqlite3_mutex* aMutex) + { + SQLUTRACE_PROFILER(0); + delete aMutex; + } + +/** +Locks the mutex. +See sqlite3_mutex::Enter() for more details. + +@param aMutex Pointer to the mutex object + +@see sqlite3_mutex::Enter() +*/ +void TMutexApi::Enter(sqlite3_mutex* aMutex) + { + SQLUTRACE_PROFILER(0); + aMutex->Enter(); + } + +/** +No-op. Always returns SQLITE_BUSY. + +@return SQLITE_BUSY +*/ +int TMutexApi::Try(sqlite3_mutex*) + { + SQLUTRACE_PROFILER(0); + return SQLITE_BUSY; + } + +/** +Unlocks the mutex. +See sqlite3_mutex::Leave() for more details. + +@param aMutex Pointer to the mutex object + +@see sqlite3_mutex::Leave() +*/ +void TMutexApi::Leave(sqlite3_mutex* aMutex) + { + SQLUTRACE_PROFILER(0); + aMutex->Leave(); + } + +/** +Checks whether the mutex is locked or not. +See sqlite3_mutex::IsHeld() for more details. + +@param aMutex Pointer to the mutex object + +@return True if the mutex is locked, false otherwise + +@see sqlite3_mutex::IsHeld() +*/ +int TMutexApi::Held(sqlite3_mutex* aMutex) + { + SQLUTRACE_PROFILER(0); + return aMutex->IsHeld(); + } + +/** +Checks whether the mutex is locked or not. +See sqlite3_mutex::IsHeld() for more details. + +@param aMutex Pointer to the mutex object + +@return False if the mutex is locked, true otherwise + +@see sqlite3_mutex::IsHeld() +*/ +int TMutexApi::Notheld(sqlite3_mutex* aMutex) + { + SQLUTRACE_PROFILER(0); + return !aMutex->IsHeld(); + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// SQLite init/release functions /////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** +Initializes the OS porting layer global data. +*/ +extern "C" SQLITE_EXPORT int sqlite3_os_init(void) + { + return sqlite3_vfs_register(VfsApi(), 1); + } + +/** +Destroys the OS porting layer global data. +*/ +extern "C" SQLITE_EXPORT int sqlite3_os_end(void) + { + return sqlite3_vfs_unregister(VfsApi()); + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////// TheFileIoApi ///////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** +Single sqlite3_io_methods instance, which data members (function pointers) are initialized with the addresses of +TFileIo members. +TheFileIoApi is used by SQLite for performing OS independent file I/O. + +@see TFileIo +@see TVfs + +@internalComponent +*/ +const static sqlite3_io_methods TheFileIoApi = + { + 1, //Version + &TFileIo::Close, + &TFileIo::Read, + &TFileIo::Write, + &TFileIo::Truncate, + &TFileIo::Sync, + &TFileIo::FileSize, + &TFileIo::Lock, + &TFileIo::Unlock, + &TFileIo::CheckReservedLock, + &TFileIo::FileControl, + &TFileIo::SectorSize, + &TFileIo::DeviceCharacteristics + }; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////// TheMutexMethods ////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** +*/ +static sqlite3_mutex_methods TheMutexMethods = + { + &TMutexApi::Init, + &TMutexApi::End, + &TMutexApi::Alloc, + &TMutexApi::Free, + &TMutexApi::Enter, + &TMutexApi::Try, + &TMutexApi::Leave, + &TMutexApi::Held, + &TMutexApi::Notheld + }; + +extern "C" sqlite3_mutex_methods* sqlite3DefaultMutex(void) + { + return &TheMutexMethods; + }; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////// UTF16<-->UTF8, conversion functions //////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** +The function converts aFileName to UTF16 encoded file name, and stores the UTF16 encoded file name +to the place pointed by aFileNameDestBuf argument. +If the UTF16 conversion of the file name failed because the file name is too long or NULL, +the function returns EFalse. + +@param aFileName Expected to point to UTF8 encoded, zero terminated string. + Max allowed aFileName length is KMaxFileName (excluding terminating 0 character). +@param aFileNameDestBuf Output parameter. Will hold UTF16, non-zero-terminated string. + The max length must be at least KMaxFileName characters. + +@return True if the conversion has been completed successfully +*/ +static TBool ConvertToUnicode(const char *aFileName, TDes& aFileNameDestBuf) + { + if(aFileName) + { + wchar_t* dest = reinterpret_cast (const_cast (aFileNameDestBuf.Ptr())); + TInt len = mbstowcs(dest, aFileName, KMaxFileName); + //Check the file name length. If it is longer than KMaxFileName characters, then the file name is not valid. + if(len > 0 && len <= KMaxFileName) + { + aFileNameDestBuf.SetLength(len); + return ETrue; + } + } + return EFalse; + } + +/** +The function converts aFileName to UTF8 encoded file name, and stores the UTF8 encoded file name +to the place pointed by aFileNameDestBuf argument. +If the UTF8 conversion of the file name failed because the file name is too long or NULL, +the function returns EFalse. + +@param aFileName Expected to point to UTF16 encoded, zero terminated string. + Max allowed aFileName length is KMaxFileName (excluding terminating 0 character). +@param aFileNameDestBuf Output parameter. Will hold UTF8, non-zero-terminated string. + The max length must be at least KMaxFileName characters. + +@return True if the conversion has been completed successfully +*/ +static TBool ConvertFromUnicode(const TDesC& aFileName, TDes8& aFileNameDestBuf) + { + char* dest = reinterpret_cast (const_cast (aFileNameDestBuf.Ptr())); + const wchar_t* src = reinterpret_cast (aFileName.Ptr()); + TInt len = wcstombs(dest, src, KMaxFileName); + //Check the file name length. If it is longer than KMaxFileName characters, then the file name is not valid. + if(len > 0 && len <= KMaxFileName) + { + aFileNameDestBuf.SetLength(len); + return ETrue; + } + return EFalse; + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////// TDbFile class definition /////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +const TInt KFileBufSize = 8 * 1024; + +/** +Initializes TDbFile data members with their default values. +*/ +inline TDbFile::TDbFile() : + iFileBuf(KFileBufSize), + iFullName(NULL), + iSharedLockByte(0), + iLockType(SQLITE_LOCK_NONE), + iReadOnly(EFalse), + iSectorSize(0), + iDeviceCharacteristics(-1) + { + pMethods = 0; + } + +/** +Casts the passed sqlite3_file pointer to a reference to the derived class - TDbFile&. +All sqlite3_file pointers passed to TFileIo methods are actually pointers to TDbFile instances. +So the cast is safe. + +@param aDbFile A pointer to a sqlite3_file instance + +@return A TDbFile reference. +@see TFileIo +@see TVfs +@see TDbFile + +@panic Sqlite 20 In _DEBUG mode if aDbFile is NULL. + +@internalComponent +*/ +static inline TDbFile& DbFile(sqlite3_file* aDbFile) + { + __ASSERT_DEBUG(aDbFile != 0, User::Panic(KPanicCategory, EPanicNullDbFilePtr)); + return *(static_cast (aDbFile)); + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////// TFileIo class definition /////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** +SQLite OS porting layer API. + +Closes the file referred by aDbFile parameter. +If aDbFile, which is actually a pointer to a TDbFile instance, the iFullName data member is not NULL, +then the file will be deleted. + +@param aDbFile A pointer to a TDbFile instance, than contains the file handle to be closed. + +@return SQLITE_OK + +@see TDbFile +*/ +/* static */ int TFileIo::Close(sqlite3_file* aDbFile) + { + SQLUTRACE_PROFILER(aDbFile); + TDbFile& dbFile = ::DbFile(aDbFile); + dbFile.iFileBuf.Close(); + if(dbFile.iFullName) + { + (void)TStaticFs::Fs().Delete(*dbFile.iFullName); + delete dbFile.iFullName; + dbFile.iFullName = NULL; + } + OpenCounter(-1); + return SQLITE_OK; + } + +/** +SQLite OS porting layer API. + +Reads from the file referred by the aDbFile parameter. + +@param aDbFile A pointer to a TDbFile instance, that contains the file handle to be read from. +@param aBuf Output parameter. The data read from the file will be copied there. + The buffer size must be at least aAmt bytes. +@param aAmt The amount of data to be read form the file. +@param aOffset The offset in the file where the read operation should start. + +@return SQLITE_FULL, The disk is full, + SQLITE_IOERR_SHORT_READ, The amount of the data read is less than aAmt, + SQLITE_IOERR_READ, File read error, + SQLITE_IOERR_NOMEM, An out of memory condition has occured, + SQLITE_OK, The operation has completed successfully. + +@see TDbFile +*/ +/* static */ int TFileIo::Read(sqlite3_file* aDbFile, void* aBuf, int aAmt, sqlite3_int64 aOffset) + { + SQLUTRACE_PROFILER(aDbFile); + SYMBIAN_TRACE_SQLITE_EVENTS_ONLY(UTF::Printf(UTF::TTraceContext(UTF::EInternals), KFileRead, aAmt, aOffset)); + SimulateIOError(return SQLITE_IOERR_READ); + TDbFile& dbFile = ::DbFile(aDbFile); + TPtr8 ptr((TUint8*)aBuf, 0, aAmt); + TInt err = dbFile.iFileBuf.Read(aOffset, ptr); + TInt cnt = ptr.Length(); + TInt sqliteErr = ::Os2SqliteErr(err, SQLITE_IOERR_READ); + if(cnt != aAmt && (sqliteErr == SQLITE_OK || sqliteErr == SQLITE_IOERR_SHORT_READ)) + { + Mem::FillZ(static_cast (aBuf) + cnt, aAmt - cnt); + sqliteErr = SQLITE_IOERR_SHORT_READ; + } + return sqliteErr; + } + +/** +SQLite OS porting layer API. + +Writes to the file referred by the aDbFile parameter. +"Write beyond the end of the file" operations are allowed. + +@param aDbFile A pointer to a TDbFile instance, that contains the file handle to be written to. +@param aData The data to be written to the file. The buffer size must be at least aAmt bytes. +@param aAmt The amount of data to be written to the file. +@param aOffset The offset in the file where the write operation should start. + +@return SQLITE_IOERR_WRITE, the file write operation has failed or the file is read-only, + SQLITE_FULL, The disk is full, + SQLITE_IOERR_NOMEM, An out of memory condition has occured, + SQLITE_OK, The operation has completed successfully. + +@see TDbFile +*/ +/* static */ int TFileIo::Write(sqlite3_file* aDbFile, const void* aData, int aAmt, sqlite3_int64 aOffset) + { + SQLUTRACE_PROFILER(aDbFile); + SYMBIAN_TRACE_SQLITE_EVENTS_ONLY(UTF::Printf(UTF::TTraceContext(UTF::EInternals), KFileWrite, aAmt, aOffset)); + SimulateIOError(return SQLITE_IOERR_WRITE); + SimulateDiskfullError(return SQLITE_FULL); + TDbFile& dbFile = ::DbFile(aDbFile); + TInt err = KErrAccessDenied; + if(!dbFile.iReadOnly) + { + TPtrC8 ptr((const TUint8*)aData, aAmt); + err = dbFile.iFileBuf.Write(aOffset, ptr); + } + return ::Os2SqliteErr(err, SQLITE_IOERR_WRITE); + } + +/** +SQLite OS porting layer API. + +Truncates the file referred by the aDbFile parameter. + +@param aDbFile A pointer to a TDbFile instance, that contains the file handle. +@param aLength The new file size in bytes. + +@return SQLITE_IOERR_TRUNCATE, the file truncate operation has failed or the file is read-only, + SQLITE_FULL, The disk is full, + The file truncate operation has failed, + SQLITE_IOERR_NOMEM, An out of memory condition has occured, + SQLITE_OK, The operation has completed successfully. + +@see TDbFile +*/ +/* static */ int TFileIo::Truncate(sqlite3_file* aDbFile, sqlite3_int64 aLength) + { + SQLUTRACE_PROFILER(aDbFile); + SYMBIAN_TRACE_SQLITE_EVENTS_ONLY(UTF::Printf(UTF::TTraceContext(UTF::EInternals), KFileTruncate, aLength)); + SimulateIOError(return SQLITE_IOERR_TRUNCATE); + TDbFile& dbFile = ::DbFile(aDbFile); + TInt err = KErrAccessDenied; + if(!dbFile.iReadOnly) + { + err = dbFile.iFileBuf.SetSize(aLength); + } + return ::Os2SqliteErr(err, SQLITE_IOERR_TRUNCATE); + } + +/** +SQLite OS porting layer API. + +Flushes the file referred by the aDbFile parameter. + +@param aDbFile A pointer to a TDbFile instance, that contains the file handle. +@param aFlags This parameter is not used in the production builds. It may be one of + SQLITE_SYNC_NORMAL or SQLITE_SYNC_FULL and is used only by the TCL test suite. + +@return SQLITE_IOERR_FSYNC, This is a read-only file, or the file flush operation has failed, + SQLITE_IOERR_NOMEM, An out of memory condition has occured, + SQLITE_FULL, The disk is full, + SQLITE_OK, The operation has completed successfully. + +@see TDbFile +*/ +/* static */int TFileIo::Sync(sqlite3_file* aDbFile, int aFlags) + { + SQLUTRACE_PROFILER(aDbFile); + SimulateIOError(return SQLITE_IOERR_FSYNC); + TDbFile& dbFile = ::DbFile(aDbFile); +#ifdef SQLITE_TEST + if(aFlags & SQLITE_SYNC_FULL) + { + sqlite3_fullsync_count++; + } + sqlite3_sync_count++; +#else + aFlags = aFlags; +#endif + TInt err = KErrAccessDenied; + if(!dbFile.iReadOnly) + { + err = dbFile.iFileBuf.Flush(); + } + return ::Os2SqliteErr(err, SQLITE_IOERR_FSYNC); + } + +/** +SQLite OS porting layer API. + +Returns the size of the file referred by the aDbFile parameter. + +@param aDbFile A pointer to a TDbFile instance, that contains the file handle. +@param aSize Output parameter. If the function completes successfully, the file size will be stored there. + +@return SQLITE_IOERR_FSTAT, The file size operation has failed; + SQLITE_IOERR_NOMEM, An out of memory condition has occured; + SQLITE_OK, The operation has completed successfully. + +@see TDbFile +*/ +/* static */ int TFileIo::FileSize(sqlite3_file* aDbFile, sqlite3_int64* aSize) + { + SQLUTRACE_PROFILER(aDbFile); + SimulateIOError(return SQLITE_IOERR_FSTAT); + TDbFile& dbFile = ::DbFile(aDbFile); + TInt err = dbFile.iFileBuf.Size(*aSize); + return ::Os2SqliteErr(err, SQLITE_IOERR_FSTAT); + } + +/** +This function is called when SQLite needs to obtain a read lock. This is done by generating a +random file position within the first page beyond the first Gb of the file and locking a single byte there. +There is a possible problem with that random file position, because the database file may be shared between multiple +connections. That increases the possibility of generating the same "random" file position by different connections to the +same file. In order to minimise that, TFileIo::GetReadLock() will generate up to 3 different file positions in a case of +a "lock byte" failure. +The generated file position will be stored in TDbFile::iSharedLockByte data member and will be used later for the +unlock operation. + +@param aDbFile The Os porting layer file handle +@return KErrNone The locking operation has completed successfully, + KErrLocked The 1 byte file area that begins from the generated file position is already locked, + Some other system-wide error codes in a case of failure. + +@see TFileIo::UnlockReadLock() +*/ +/* static */TInt TFileIo::GetReadLock(TDbFile& aDbFile) + { + const TInt KLockTryCount = 3; + TInt rc = KErrLocked; + for(TInt i=0;i SQLITE_LOCK_SHARED +SQLITE_LOCK_SHARED -> SQLITE_LOCK_RESERVED +SQLITE_LOCK_SHARED -> (SQLITE_LOCK_PENDING) -> SQLITE_LOCK_EXCLUSIVE +SQLITE_LOCK_RESERVED -> (SQLITE_LOCK_PENDING) -> SQLITE_LOCK_EXCLUSIVE +SQLITE_LOCK_PENDING -> SQLITE_LOCK_EXCLUSIVE + +@param aDbFile A pointer to a TDbFile instance, that contains the file handle. +@param aLockType Lock type: SQLITE_LOCK_NONE, SQLITE_LOCK_SHARED, SQLITE_LOCK_RESERVED, SQLITE_LOCK_PENDING or + SQLITE_LOCK_EXCLUSIVE. + +@return SQLITE_IOERR_NOMEM, An out of memory condition has occured; + SQLITE_BUSY, The requested lock cannot be obtained; + SQLITE_LOCK, File locking error, + SQLITE_OK, The operation has completed successfully. + +@see TFileIo::CheckReservedLock() +@see TFileIo::Unlock() + +@see TDbFile +*/ +/* static */ int TFileIo::Lock(sqlite3_file* aDbFile, int aLockType) + { + SQLUTRACE_PROFILER(aDbFile); + TDbFile& dbFile = ::DbFile(aDbFile); + //If there is already a lock of this type or more restrictive on the aDbFile, then - do nothing. + if(dbFile.iLockType >= aLockType) + { + return SQLITE_OK; + } + + //The file flushing here must be done in order to get the file buffer object content (iFileBuf data member)) + //synchronised with the database file content (the database file content may get modified by a different connection + //at the same time). + if(aLockType == SQLITE_LOCK_SHARED && !dbFile.iReadOnly) + { + TInt err = dbFile.iFileBuf.Flush(ETrue); + if(err != KErrNone) + { + return ::Os2SqliteErr(err, SQLITE_IOERR_LOCK); + } + } + + //Make sure the locking sequence is correct + __ASSERT_DEBUG(dbFile.iLockType != SQLITE_LOCK_NONE || aLockType == SQLITE_LOCK_SHARED, User::Panic(KPanicCategory, EPanicInvalidLock)); + __ASSERT_DEBUG(aLockType != SQLITE_LOCK_PENDING, User::Panic(KPanicCategory, EPanicInvalidLock)); + __ASSERT_DEBUG(aLockType != SQLITE_LOCK_RESERVED || dbFile.iLockType == SQLITE_LOCK_SHARED, User::Panic(KPanicCategory, EPanicInvalidLock)); + + TInt rc = SQLITE_OK; //Return code from subroutines + TBool locked = ETrue; //Result of a file lock call (the default value means: "lock accuired") + TInt newLockType = -1; //Set dbFile.iLockType to this value before exiting + TBool gotPendingLock = EFalse;//True if we acquired a SQLITE_LOCK_PENDING lock this time + + //Lock the SQLITE_LOCK_PENDING byte if we need to acquire a SQLITE_LOCK_PENDING lock or + //SQLITE_LOCK_SHARED lock. If we are acquiring a SQLITE_LOCK_SHARED lock, the acquisition of + //the SQLITE_LOCK_PENDING byte is temporary. + newLockType = dbFile.iLockType; + if(dbFile.iLockType == SQLITE_LOCK_NONE || (aLockType == SQLITE_LOCK_EXCLUSIVE && dbFile.iLockType == SQLITE_LOCK_RESERVED)) + { + //Try 3 times to get the pending lock. The pending lock might be + //held by another reader process who will release it momentarily. + const TInt KLockTryCnt = 3; + locked = EFalse; + for(TInt i=0;i= SQLITE_LOCK_SHARED, User::Panic(KPanicCategory, EPanicInvalidLock)); + (void)TFileIo::UnlockReadLock(dbFile); + TInt err = dbFile.iFileBuf.Lock(SHARED_FIRST, SHARED_SIZE); + if(err != KErrNone && err != KErrLocked) + { + return ::Os2SqliteErr(err, SQLITE_IOERR_LOCK); + } + locked = (err == KErrNone); + if(locked) + { + newLockType = SQLITE_LOCK_EXCLUSIVE; + } + } + + // If we are holding a PENDING lock that ought to be released, then + // release it now. + if(gotPendingLock && aLockType == SQLITE_LOCK_SHARED) + { + (void)dbFile.iFileBuf.UnLock(PENDING_BYTE, 1); + } + + // Update the state of the lock has held in the file descriptor then + // return the appropriate result code. + rc = locked ? SQLITE_OK : SQLITE_BUSY; + dbFile.iLockType = newLockType; + return rc; + } + +/** +SQLite OS porting layer API. + +Lower the locking level on file descriptor id to locktype. locktype +must be either SQLITE_LOCK_NONE or SQLITE_LOCK_SHARED. + +If the locking level of the file descriptor is already at or below +the requested locking level, this routine is a no-op. + +It is not possible for this routine to fail if the second argument +is SQLITE_LOCK_NONE. If the second argument is SQLITE_LOCK_SHARED then this routine +might return SQLITE_IOERR; + +@param aDbFile A pointer to a TDbFile instance, that contains the file handle. +@param aLockType Lock type: SQLITE_LOCK_NONE, SQLITE_LOCK_SHARED, SQLITE_LOCK_RESERVED, SQLITE_LOCK_PENDING or + SQLITE_LOCK_EXCLUSIVE. + +@return SQLITE_OK, The operation has completed successfully, + SQLITE_IOERR_UNLOCK, The unlock operation has failed. + +@see TFileIo::CheckReservedLock() +@see TFileIo::Lock() + +@see TDbFile +*/ +/* static */ int TFileIo::Unlock(sqlite3_file* aDbFile, int aLockType) + { + __ASSERT_DEBUG(aLockType <= SQLITE_LOCK_SHARED, User::Panic(KPanicCategory, EPanicInvalidLock)); + + SQLUTRACE_PROFILER(aDbFile); + TDbFile& dbFile = ::DbFile(aDbFile); + TInt rc = SQLITE_OK; + TInt currLockType = dbFile.iLockType; + + if(currLockType >= SQLITE_LOCK_EXCLUSIVE) + { + (void)dbFile.iFileBuf.UnLock(SHARED_FIRST, SHARED_SIZE); + if(aLockType == SQLITE_LOCK_SHARED) + { + TInt err = TFileIo::GetReadLock(dbFile); + if(err != KErrNone && err != KErrLocked) + { + return ::Os2SqliteErr(err, SQLITE_IOERR_UNLOCK); + } + if(err == KErrLocked) + { + //This should never happen. We should always be able to reacquire the read lock + rc = SQLITE_IOERR_UNLOCK; + } + } + } + if(currLockType >= SQLITE_LOCK_RESERVED) + { + (void)dbFile.iFileBuf.UnLock(RESERVED_BYTE, 1); + } + if(aLockType == SQLITE_LOCK_NONE && currLockType >= SQLITE_LOCK_SHARED) + { + (void)TFileIo::UnlockReadLock(dbFile); + } + if(currLockType>= SQLITE_LOCK_PENDING) + { + (void)dbFile.iFileBuf.UnLock(PENDING_BYTE, 1); + } + + dbFile.iLockType = aLockType; + return rc; + } + +/** +SQLite OS porting layer API. + +Checks if the file lock type is SQLITE_LOCK_RESERVED or bigger. + +@param aDbFile A pointer to a TDbFile instance, that contains the file handle. +@param aResOut Output parameter. It should be set to 1 if the stored lock type is bigger or equal + than SQLITE_LOCK_RESERVED. + +@return SQLITE_IOERR_CHECKRESERVEDLOCK, The operation has failed, + SQLITE_OK The operation has completed successfully. + +@see TFileIo::Lock() +@see TFileIo::Unlock() + +@see TDbFile +*/ +/* static */ int TFileIo::CheckReservedLock(sqlite3_file* aDbFile, int *aResOut) + { + SQLUTRACE_PROFILER(aDbFile); + TDbFile& dbFile = ::DbFile(aDbFile); + TInt rc; + if(dbFile.iLockType >= SQLITE_LOCK_RESERVED) + { + rc = 1; + } + else + { + TInt err = dbFile.iFileBuf.Lock(RESERVED_BYTE, 1); + if(err != KErrNone && err != KErrLocked) + { + return ::Os2SqliteErr(err, SQLITE_IOERR_CHECKRESERVEDLOCK); + } + rc = (err == KErrNone); + if(rc) //non-zero rc means: the lock has been successful (there wasn't a reserved lock on this file) + { + (void)dbFile.iFileBuf.UnLock(RESERVED_BYTE, 1); + } + rc = !rc; + } + *aResOut = rc; + return SQLITE_OK; + } + +/** +SQLite OS porting layer API. + +Performs an aOp operation on the file referred by the aDbFile parameter. +Since the only supported operation at the moment is SQLITE_FCNTL_LOCKSTATE, and the current lock type is stored as +a data memebr of TDbFile, the function implementation has been optimised - no file I/O calls. The stored file lock type +is retured if the operation is SQLITE_FCNTL_LOCKSTATE. + +@param aDbFile A pointer to a TDbFile instance, that contains the file handle. +@param aOp File operation type. Currently only SQLITE_FCNTL_LOCKSTATE is supported. +@param aArg An additional input/output parameter which purpose depends on the type of the current file operation. + If the file operation is SQLITE_FCNTL_LOCKSTATE, then aArg is used as an output parameter, where + the file lock type is stored. + +@return SQLITE_ERROR, Non-supported operation, + SQLITE_OK, The operation has completed successfully. + +@see TDbFile +*/ +/* static */ int TFileIo::FileControl(sqlite3_file* aDbFile, int aOp, void* aArg) + { + SQLUTRACE_PROFILER(aDbFile); + TDbFile& dbFile = ::DbFile(aDbFile); + SYMBIAN_TRACE_SQLITE_EVENTS_ONLY(UTF::Printf(UTF::TTraceContext(UTF::EInternals), KFileFileCtr, aOp, dbFile.iFullName)); + TInt err = KErrNone; + switch(aOp) + { + case SQLITE_FCNTL_LOCKSTATE: + *(int*)aArg = dbFile.iLockType; + break; + default: + err = KErrArgument; + break; + } + return err == KErrNone ? SQLITE_OK : SQLITE_ERROR; + } + +/** +SQLite OS porting layer API. + +Retrieves the sector size of the media of the file referred by the aDbFile parameter. +Since the sector size never changes till the file is open, the function has been optimised - no file I/O calls. +The sector size is retrieved during the TVfs::Open() call and stored in TDbFile::iSectorSize. The SectorSize() +call returns the value of TDbFile::iSectorSize. + +@param aDbFile A pointer to a TDbFile instance, that contains the file handle. + +@return The sector size. + +@panic Sqlite 19 In _DEBUG mode - TDbFile::iSectorSize is negative or 0 . + +@see TDbFile +@see TVfs::Open() +*/ +/* static */ int TFileIo::SectorSize(sqlite3_file* aDbFile) + { + SQLUTRACE_PROFILER(aDbFile); + TDbFile& dbFile = ::DbFile(aDbFile); + __ASSERT_DEBUG(dbFile.iSectorSize > 0, User::Panic(KPanicCategory, EPanicInternalError)); + if(dbFile.iSectorSize > 0) + { + return dbFile.iSectorSize; + } + return SQLITE_DEFAULT_SECTOR_SIZE; + } + +/** +SQLite OS porting layer API. + +Retrieves the device characteristics of the device of the file referred by the aDbFile parameter. +Since the device characteristics never change till the file is open, the function has been optimised - no file I/O calls. +The device characteristics are retrieved during the TVfs::Open() call and stored in TDbFile::iDeviceCharacteristics. +The DeviceCharacteristics() call returns the value of TDbFile::iDeviceCharacteristics. + +@param aDbFile A pointer to a TDbFile instance, that contains the file handle. + +@return A bit set containing the device characteristics. + +@panic Sqlite 19 In _DEBUG mode - TDbFile::iDeviceCharacteristics is negative or 0 . + +@see TDbFile +@see TVfs::Open() +*/ +/* static */ int TFileIo::DeviceCharacteristics(sqlite3_file* aDbFile) + { + SQLUTRACE_PROFILER(aDbFile); + TDbFile& dbFile = ::DbFile(aDbFile); + __ASSERT_DEBUG(dbFile.iDeviceCharacteristics >= 0, User::Panic(KPanicCategory, EPanicInternalError)); + if(dbFile.iDeviceCharacteristics >= 0) + { + return dbFile.iDeviceCharacteristics; + } + return 0; + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// TVfs class definition /////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** +Collects information about the drive referred by the aDriveNo parameter. + +@param aFs RFs instance. +@param aDriveNo The drive about which an information will be collected. +@param aVolumeInfo Output parameter. A reference to a TVolumeIOParamInfo object where the collected information will be stored. + +@return KErrNone, The operation has completed succesfully; + KErrNoMemory, Out of memory condition has occured; + Note that other system-wide error codes may also be returned. + +@see TVfs::Open() +*/ +/* static */ inline TInt TVfs::DoGetVolumeIoParamInfo(RFs& aFs, TInt aDriveNo, TVolumeIOParamInfo& aVolumeInfo) + { + return aFs.VolumeIOParam(aDriveNo, aVolumeInfo); + } + +/** +Retrieves and returns in a bit set the device characteristics. + +@param aDriveInfo A TDriveInfo reference from which the device characteristics will be extracted. +@param aVolumeInfo A TVolumeIOParamInfo reference from which the device characteristics will be extracted. + +@return A bit set containing the device characteristics: + SQLITE_IOCAP_SAFE_APPEND, SQLITE_IOCAP_ATOMIC, the atomic block size. + +@see TVfs::DoGetDriveInfo(); +@see TVfs::DoGetVolumeIoParamInfo(); +@see TVfs::Open() +*/ +/* static */ TInt TVfs::DoGetDeviceCharacteristics(const TDriveInfo& aDriveInfo, const TVolumeIOParamInfo& aVolumeInfo) + { + TInt deviceCharacteristics = 0; + if(aDriveInfo.iDriveAtt & (KDriveAttLocal | KDriveAttInternal)) + { + deviceCharacteristics |= SQLITE_IOCAP_SAFE_APPEND;//Data written first, file size updated second + } + if(aDriveInfo.iDriveAtt & KDriveAttTransaction) + { + deviceCharacteristics |= SQLITE_IOCAP_ATOMIC; + } + if(aVolumeInfo.iBlockSize >= SQLITE_DEFAULT_SECTOR_SIZE && (aVolumeInfo.iBlockSize & (aVolumeInfo.iBlockSize - 1)) == 0) + { + switch(aVolumeInfo.iBlockSize) + { + case 512: + deviceCharacteristics |= SQLITE_IOCAP_ATOMIC512; + break; + case 1024: + deviceCharacteristics |= SQLITE_IOCAP_ATOMIC1K; + break; + case 2048: + deviceCharacteristics |= SQLITE_IOCAP_ATOMIC2K; + break; + case 4096: + deviceCharacteristics |= SQLITE_IOCAP_ATOMIC4K; + break; + case 8192: + deviceCharacteristics |= SQLITE_IOCAP_ATOMIC8K; + break; + case 16384: + deviceCharacteristics |= SQLITE_IOCAP_ATOMIC16K; + break; + case 32768: + deviceCharacteristics |= SQLITE_IOCAP_ATOMIC32K; + break; + case 65536: + deviceCharacteristics |= SQLITE_IOCAP_ATOMIC64K; + break; + default: + //Do nothing. deviceCharacteristics was initialized with 0 at the beginning of the function body. + break; + } + } + return deviceCharacteristics; + } + +/** +Retrieves and returns the sector size of the drive referred by the aDriveInfo parameter. +The sector size must be a power of two. +The sector size is extracted only if aDriveInfo refers to a removable device, otherwise the +SQLITE_DEFAULT_SECTOR_SIZE value (512 bytes) will be used as a sector size. + +@param aDriveInfo A TDriveInfo reference. +@param aVolumeInfo A TVolumeIOParamInfo reference. + +@return The sector size of the drive referred by the aDriveInfo parameter. + +@panic Sqlite 19 In _DEBUG mode - The sector size is negative, zero or is not a power of two. + +@see TVfs::Open() +*/ +/* static */ TInt TVfs::DoGetSectorSize(const TDriveInfo& aDriveInfo, const TVolumeIOParamInfo& aVolumeInfo) + { + //Initialize the sectorSize variable only if: + // - aDriveInfo refers to a removable drive + // - aVolumeInfo.iBlockSize > SQLITE_DEFAULT_SECTOR_SIZE; + // - aVolumeInfo.iBlockSize is power of 2; + TInt sectorSize = SQLITE_DEFAULT_SECTOR_SIZE; + if(aDriveInfo.iDriveAtt & KDriveAttRemovable) + { + if(aVolumeInfo.iBlockSize > SQLITE_DEFAULT_SECTOR_SIZE && (aVolumeInfo.iBlockSize & (aVolumeInfo.iBlockSize - 1)) == 0) + { + sectorSize = aVolumeInfo.iBlockSize; + } + } + __ASSERT_DEBUG(sectorSize > 0 && (sectorSize & (sectorSize - 1)) == 0, User::Panic(KPanicCategory, EPanicInternalError)); + return sectorSize; + } + +/** +Retrieves in a bit set the device characteristics of the device of the file referred by the aDbFile parameter. +Retrieves the sector size of the drive of the file referred by the aDbFile parameter. +The sector size and the device characteristics will be stored in iSectorSize and iDeviceCharacteristics TDbFile data members. +The stored values will be used later by TFileIo::DeviceCharacteristics() and TFileIo::SectorSize(). + +@param aDbFile Input/Output parameter. A TDriveInfo reference. The collected information will be stored in TDbDrive + data members. +@param aRecReadBufSize Output parameter. The recommended buffer size for optimised reading performance. + +@return KErrNone, The operation has completed succesfully; + Note that other system-wide error codes may also be returned. + +@panic Sqlite 19 In _DEBUG mode - TDbFile::iSectorSize has been already initialized. +@panic Sqlite 19 In _DEBUG mode - TDbFile::iDeviceCharacteristics has been already initialized. + +@see TVfs::DoGetDeviceCharacteristics(); +@see TVfs::DoGetSectorSize(); +@see TVfs::Open() +@see TDbFile +@see TFileIo::DeviceCharacteristics() +@see TFileIo::SectorSize() +*/ +/* static */ TInt TVfs::DoGetDeviceCharacteristicsAndSectorSize(TDbFile& aDbFile, TInt& aRecReadBufSize) + { + __ASSERT_DEBUG(aDbFile.iDeviceCharacteristics < 0, User::Panic(KPanicCategory, EPanicInternalError)); + __ASSERT_DEBUG(aDbFile.iSectorSize <= 0, User::Panic(KPanicCategory, EPanicInternalError)); + TInt driveNo; + TDriveInfo driveInfo; + TInt err = aDbFile.iFileBuf.Drive(driveNo, driveInfo); + if(err != KErrNone) + { + return err; + } + TVolumeIOParamInfo volumeInfo; + err = TVfs::DoGetVolumeIoParamInfo(TStaticFs::Fs(), driveNo, volumeInfo); + if(err != KErrNone) + { + return err; + } + aDbFile.iDeviceCharacteristics = TVfs::DoGetDeviceCharacteristics(driveInfo, volumeInfo); + aDbFile.iSectorSize = TVfs::DoGetSectorSize(driveInfo, volumeInfo); + aRecReadBufSize = volumeInfo.iRecReadBufSize; + return KErrNone; + } + +//Creates a temporary file. The file location will be the application's session path. +//If the session path does not exist, then the function will create the session path. +static TInt CreateTempFile(TDbFile& aDbFile, TFileName& aFileNameOut, TInt aFileMode) + { + TFileName sessionPath; + TInt err = TStaticFs::Fs().SessionPath(sessionPath); + if(err == KErrNone) + { + err = aDbFile.iFileBuf.Temp(TStaticFs::Fs(), sessionPath, aFileNameOut, aFileMode); + if(err == KErrPathNotFound) + { + err = TStaticFs::Fs().MkDirAll(sessionPath); + if(err == KErrNone) + { + err = aDbFile.iFileBuf.Temp(TStaticFs::Fs(), sessionPath, aFileNameOut, aFileMode); + } + } + if(err == KErrNone) + { + aDbFile.iFullName = aFileNameOut.Alloc(); + if(!aDbFile.iFullName) + { + aDbFile.iFileBuf.Close(); + (void)TStaticFs::Fs().Delete(aFileNameOut); + err = KErrNoMemory; + } + } + } + return err; + } + +/** +SQLite OS porting layer API. + +Opens or creates a file which name is in the aFileName parameter. +If the function succeeds, the file handle and other related information will be stored in the place pointed by the +aDbFile parameter, a memory block of sizeof(TDbFile) size for which is allocated by the caller. +The function will also retrieve the sector size and the device characteristics and store them in aDbFile, +which is actually a TDbFile pointer, for later use. + +@param aFileName Zero-terminated, UTF8 encoded file name. + If aFileName is NULL then a temporary file is created. +@param aDbFile Output parameter. The file handle and other related information will be stored there. +@param aFlags "Open/Create" input flags: + SQLITE_OPEN_DELETEONCLOSE, + SQLITE_OPEN_READWRITE, + SQLITE_OPEN_EXCLUSIVE, + SQLITE_OPEN_CREATE +@param aOutFlags "Open/Create" output flags: + SQLITE_OPEN_READWRITE, + SQLITE_OPEN_READONLY + +@return SQLITE_CANTOPEN, The aFileName parameter cannot be converted to UTF16. + Any other file I/O error will also be reported as SQLITE_CANTOPEN; + SQLITE_IOERR_NOMEM, An out of memory condition has occured; + SQLITE_OK, The operation has completed successfully. + +@see TDbFile +*/ +/* static */ int TVfs::Open(sqlite3_vfs* aVfs, const char* aFileName, sqlite3_file* aDbFile, int aFlags, int* aOutFlags) + { + SQLUTRACE_PROFILER(aVfs); + TFileName fname; + if(aFileName && !::ConvertToUnicode(aFileName, fname)) + { + return SQLITE_CANTOPEN; + } + SYMBIAN_TRACE_SQLITE_EVENTS_ONLY(UTF::Printf(UTF::TTraceContext(UTF::EInternals), KFileOpen, aDbFile, &fname)); + new (aDbFile) TDbFile; + TDbFile& dbFile = ::DbFile(aDbFile); + if(aFileName && (aFlags & SQLITE_OPEN_DELETEONCLOSE)) + { + dbFile.iFullName = fname.Alloc(); + if(!dbFile.iFullName) + { + return SQLITE_IOERR_NOMEM; + } + } + TInt recReadBufSize = -1; + TInt err = KErrNone; + TInt fmode = EFileRead; + if(aFlags & SQLITE_OPEN_READWRITE) + { + fmode |= EFileWrite; + } + //SQLite TCL tests expect the journal file to be open in shared mode. + if(aFlags & SQLITE_OPEN_EXCLUSIVE && !(aFlags & SQLITE_OPEN_MAIN_JOURNAL)) + { + fmode |= EFileShareExclusive; + } + else + { + fmode |= (fmode & EFileWrite) ? EFileShareAny : EFileShareReadersOnly; + } + if(!aFileName) + {//Create temporary file + err = ::CreateTempFile(dbFile, fname, fmode); + } + else + { + err = KErrGeneral;//The error has to be set here, because, there is case where none of the file create/open operations will be executed + if(aFlags & SQLITE_OPEN_CREATE) + { + err = dbFile.iFileBuf.Create(TStaticFs::Fs(), fname, fmode); + } + if(err != KErrNone && err != KErrNoMemory) + { + err = dbFile.iFileBuf.Open(TStaticFs::Fs(), fname, fmode); + } + if((err != KErrNone && err != KErrNoMemory) && (aFlags & SQLITE_OPEN_READWRITE)) + { + aFlags &= ~SQLITE_OPEN_READWRITE; + aFlags |= SQLITE_OPEN_READONLY; + fmode &= ~EFileWrite; + err = dbFile.iFileBuf.Open(TStaticFs::Fs(), fname, fmode); + } + } + if(err == KErrNone) + { + err = TVfs::DoGetDeviceCharacteristicsAndSectorSize(dbFile, recReadBufSize); + } + if(err != KErrNone) + { + dbFile.iFileBuf.Close(); + delete dbFile.iFullName; + dbFile.iFullName = NULL; + } + else + { + dbFile.pMethods = &TheFileIoApi; + dbFile.iReadOnly = !(aFlags & SQLITE_OPEN_READWRITE); + if(aOutFlags) + { + *aOutFlags = dbFile.iReadOnly ? SQLITE_OPEN_READONLY : SQLITE_OPEN_READWRITE; + } + (void)dbFile.iFileBuf.SetReadAheadSize(dbFile.iSectorSize, recReadBufSize); + OpenCounter(+1); + } + return ::Os2SqliteErr(err, SQLITE_CANTOPEN); + } + +/** +SQLite OS porting layer API. + +Deletes a file which name is in the aFileName parameter. + +@param aFileName Zero-terminated, UTF8 encoded file name. + +@return SQLITE_ERROR, The aFileName parameter cannot be converted to UTF16. + The file name refers to a private secure database; + SQLITE_IOERR_NOMEM, An out of memory condition has occured; + SQLITE_IOERR_DELETE,The delete file operation has failed; + SQLITE_OK, The operation has completed successfully. +*/ +/* static */ int TVfs::Delete(sqlite3_vfs* aVfs, const char* aFileName, int /*aSyncDir*/) + { + SQLUTRACE_PROFILER(aVfs); + SimulateIOError(return SQLITE_IOERR_DELETE); + TFileName fname; + if(!::ConvertToUnicode(aFileName, fname)) + { + return SQLITE_ERROR; + } + SYMBIAN_TRACE_SQLITE_EVENTS_ONLY(UTF::Printf(UTF::TTraceContext(UTF::EInternals), KFileName, &fname)); + TInt err = TStaticFs::Fs().Delete(fname); + return ::Os2SqliteErr(err, SQLITE_IOERR_DELETE); + } + +/** +SQLite OS porting layer API. + +Retrieves an information about a file which name is in the aFileName parameter. +The requested information type can be: does the file exist, is the file read-only or read/write. + +@param aFileName Zero-terminated, UTF8 encoded file name. +@param aFlags This parameter can be one of: SQLITE_ACCESS_READ, SQLITE_ACCESS_EXISTS or SQLITE_ACCESS_READWRITE. +@param aResOut Output parameter, set to 1 if the tested condition is true, 0 otherwise. + +@return SQLITE_OK, The call has completed successfully, + SQLITE_IOERR_NOMEM, An out of memory conditon has occured, + SQLITE_IOERR_ACCESS,File I/O error; +*/ +/* static */ int TVfs::Access(sqlite3_vfs* aVfs, const char* aFileName, int aFlags, int* aResOut) + { + SQLUTRACE_PROFILER(aVfs); + TFileName fname; + if(!::ConvertToUnicode(aFileName, fname)) + { + return SQLITE_IOERR_ACCESS; + } + SYMBIAN_TRACE_SQLITE_EVENTS_ONLY(UTF::Printf(UTF::TTraceContext(UTF::EInternals), KFileName, &fname)); + TEntry entry; + TInt err = TStaticFs::Fs().Entry(fname, entry); + if(aFlags == SQLITE_ACCESS_EXISTS && err == KErrNotFound) + { + *aResOut = 0; + return SQLITE_OK; + } + if(err != KErrNone) + { + return err == KErrNoMemory ? SQLITE_IOERR_NOMEM : SQLITE_IOERR_ACCESS; + } + *aResOut = 0; + switch(aFlags) + { + case SQLITE_ACCESS_READ: + *aResOut = entry.IsReadOnly(); + break; + case SQLITE_ACCESS_EXISTS: + *aResOut = 1; + break; + case SQLITE_ACCESS_READWRITE: + *aResOut = !entry.IsReadOnly(); + break; + default: + break; + } + return SQLITE_OK; + } + +/** +SQLite OS porting layer API. + +Accepts UTF8 encoded, zero-terminated file as an input argument in the aRelative parameter +and constructs the full file path in the aBuf output parameter. + +If the format of aRelative argument is <[SID]FileName.[EXT]>, then the database file name will be +treated as a name of a secure database file which has to be created/opened in the server's private +directory on the system drive. + +If the format of aRelative argument is , then the database file name +will be treated as a name of a secure database file which has to be created/opened in the server's +private directory on drive. + +If the format of aRelative argument is , then the database file name +will be treated as a name of a non-secure database file in directory. +If aRelative contains file handles, then it will be treated as a name of a file belonging to server's +private data cage. + +@param aRelative The input file name, zero-terminated, UTF8 encoded. +@param aBufLen The output buffer length. +@param aBuf Output buffer for the constructed full file name path. The allocated buffer length must be at least aBufLen bytes. + +@return SQLITE_ERROR, The aRelative parameter is NULL or cannot be converted to UTF16; + SQLITE_OK The operation has completed successfully. +*/ +/* static */ int TVfs::FullPathName(sqlite3_vfs* aVfs, const char* aRelative, int aBufLen, char* aBuf) + { + SQLUTRACE_PROFILER(aVfs); + if(!aRelative) //NULL argument + { + return SQLITE_ERROR; + } + //Convert the received file name to UTF16 + TBuf fname; + if(!::ConvertToUnicode(aRelative, fname)) + { + return SQLITE_ERROR; + } + SYMBIAN_TRACE_SQLITE_EVENTS_ONLY(UTF::Printf(UTF::TTraceContext(UTF::EInternals), KFileName, &fname)); + //Search if the file name begins with ".\" - current directory + if(fname.Find(KCwd) == 0) + { + fname.Delete(0, KCwd().Length()); + } + fname.Append(TChar(0));//Zero-terminate the converted file name + TFileName defaultPath; + TInt err = TStaticFs::Fs().SessionPath(defaultPath); + if(err != KErrNone) + { + return SQLITE_ERROR; + } + TParse parse; + (void)parse.Set(fname, &defaultPath, 0);//If fname does not have a path, defaultPath will be used + TPtr8 dest8(reinterpret_cast (aBuf), aBufLen); + if(!::ConvertFromUnicode(parse.FullName(), dest8)) + { + return SQLITE_ERROR; + } + return SQLITE_OK; + } + +/** +SQLite OS porting layer API. + +Generates a set of random numbers and stores them in the aBuf output parameter. + +@param aBufLen The output buffer length. +@param aBuf Output buffer for the generated random numbers. The allocated buffer length must be at least aBufLen bytes. + +@return The length of the used part of the output buffer. +*/ +/* static */ int TVfs::Randomness(sqlite3_vfs* aVfs, int aBufLen, char* aBuf) + { + SQLUTRACE_PROFILER(aVfs); + const TInt KRandIterations = aBufLen / sizeof(int); + for(TInt i=0;i