diff -r 000000000000 -r 08ec8eefde2f persistentstorage/sql/OsLayer/os_symbian.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/persistentstorage/sql/OsLayer/os_symbian.cpp Fri Jan 22 11:06:30 2010 +0200 @@ -0,0 +1,2611 @@ +// 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: +// The Symbian OS porting layer - single-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. +// This file is also used for the COsLayerData implementation. A single COslayerData +// object is used by the OS porting layer for managing some global data. +// +// + +/** + @file + @see TVfs + @see TFileIo +*/ + +#ifdef SQLITE_OS_SYMBIAN + +//#define _SQLPROFILER // Enable profiling //The same macro has to be enabled in SqlAssert.h file + +extern "C" +{ +#include "sqliteInt.h" +#include "os.h" +} +#include "os_common.h" +#include "SqliteSymbian.h" +#include "FileBuf64.h" +#include +#include "UTraceSql.h" +#ifdef _SQLPROFILER +#include +#include "../INC/SqlResourceProfiler.h" +#endif + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** +Panic category - used by asserts in this file (the OS porting layer). + +@see TPanicCodes + +@internalComponent +*/ +_LIT(KPanicCategory, "Sqlite"); + +/** +Panic codes - used by asserts in this file (the OS porting layer). + +@see KPanicCategory + +@internalComponent +*/ +enum TPanicCodes + { + EPanicNullOsLayerDataPtr = 1, + EPanicInvalidWAmount = 2, + EPanicOffset64bit = 3, + EPanicInvalidOpType =11, + EPanicInvalidFhStr =12, + EPanicInvalidFhData =13, + EPanicInvalidArg =14, + EPanicInvalidRAmount =15, + EPanicOsLayerDataExists =16, + EPanicInvalidDrive =17, + EPanicInvalidSectorSize =18, + EPanicInternalError =19, + EPanicNullDbFilePtr =20, + EPanicFastCounterFreq =21 + }; + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#ifdef _SQLPROFILER + + //Profiling counters, defined in SqlSrvResourceprofiler.cpp + extern TInt TheSqlSrvProfilerFileRead; + extern TInt TheSqlSrvProfilerFileWrite; + extern TInt TheSqlSrvProfilerFileSync; + extern TInt TheSqlSrvProfilerFileSetSize; + +# define __COUNTER_INCR(counter) ++counter + + ////////////////////////////////////////////////////////////////////////////// + ////////////////////////// File I/O ////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + + /** + This enum is used only in _SQLPROFILER mode for tracing the file system calls. + @internalComponent + @see FsCallBrkPt() + */ + enum TFsOpType + { + EFsOpFileCreate, + EFsOpFileOpen, + EFsOpFileClose, + EFsOpFileDelete, + EFsOpFileRead, + EFsOpFileWrite, + EFsOpFileSeek, + EFsOpFileSize, + EFsOpFileSetSize, + EFsOpFileSync, + EFsOpFileDrive, + EFsOpFileAdopt, + EFsOpFsClose, + EFsOpFsConnect, + EFsOpFsGetSystemDrive, + EFsOpFsCreatePrivatePath, + EFsOpFsPrivatePath, + EFsOpFsVolumeIoParam, + EFsOpFsEntry, + EFsOpFsAtt, + EFsOpFileCreateTemp, + EFsOpFileAttach, + // + EFsOpLast + }; + + TBool TheFileIoProfileEnabled = EFalse; + TUint32 TheFileOpCounter[EFsOpLast] = {0}; + TInt64 TheFileWriteAmount = 0; + TInt64 TheFileReadAmount = 0; + + /** + This function is used only in _SQLPROFILER mode as an appropriate place for: + - setting breakpoints for tracing the file system calls; + - collection information about the number of the file system calls; + + @param aFsOpType A TFsOpType enum item value, identifying the file system operation that will be executed; + @param a1 If the operation is "file read" or "file write" - the amount of data read/written; + + @internalComponent + + @see TFsOpType + */ + void FsCallBrkPt(TInt aFsOpType, TInt a1) + { + __ASSERT_DEBUG(aFsOpType >= 0 && aFsOpType < EFsOpLast, User::Invariant()); + if(!TheFileIoProfileEnabled) + { + return; + } + TFsOpType fsOpType = (TFsOpType)aFsOpType; + ++TheFileOpCounter[fsOpType]; + if(fsOpType == EFsOpFileWrite) + { + TheFileWriteAmount += a1; + } + else if(fsOpType == EFsOpFileRead) + { + TheFileReadAmount += a1; + } + } + +# define __FS_CALL(aFsOpType, a1) FsCallBrkPt(aFsOpType, a1) + + ////////////////////////////////////////////////////////////////////////////// + ////////////////////////// Heap Stats //////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + + /** + Memory operation type: alloc, realloc, free. Used during the profiling. + + @internalComponent + */ + enum TMemOpType + { + EMemOpAlloc, + EMemOpRealloc, + EMemOpFree, + // + EMemOpLast + }; + + TBool TheMemProfileEnabled = EFalse;//Mem operation call counters and time counters enabled/disabled + TBool TheMaxAllocProfileEnabled = EFalse;//Max mem allocation enabled/disabled + TUint32 TheMemOpCounter[EMemOpLast] = {0}; + TInt64 TheMemOpTicks[EMemOpLast] = {0}; + TInt64 TheAllocated = 0; + TInt64 TheFreed = 0; + TInt TheAllocMax = 0; + + /** + This class is used only in _SQLPROFILER mode as an appropriate place for: + - setting breakpoints for tracing the memory allocation/deallocation calls; + - collection information about the number of the memory allocation/deallocation calls and the time spent in the calls; + + The constructor's parameters are: + - aOpType A TMemOpType enum item value, identifying the operation that will be executed; + - aAmt1 The allocated/deallocated size; + - aAmt2 Used only if a block of memory is reallocated in which case a2 is the old size of the block; + + @internalComponent + + @see TMemOpType + */ + class TMemCallCounter + { + public: + TMemCallCounter(TMemOpType aOpType, TInt aAmt1, TInt aAmt2) : + iOpType(aOpType), + iStartTicks(0) + { + if(TheMaxAllocProfileEnabled && (iOpType == EMemOpAlloc || iOpType == EMemOpRealloc) && aAmt1 > TheAllocMax) + { + TheAllocMax = aAmt1; + } + if(TheMemProfileEnabled) + { + ++TheMemOpCounter[iOpType]; + switch(iOpType) + { + case EMemOpAlloc: + TheAllocated += aAmt1; + break; + case EMemOpRealloc: + TheAllocated += aAmt1; + TheFreed += aAmt2; + break; + case EMemOpFree: + TheFreed += aAmt1; + break; + default: + __ASSERT_DEBUG(0, User::Invariant()); + break; + } + iStartTicks = User::FastCounter(); + } + } + ~TMemCallCounter() + { + if(TheMemProfileEnabled) + { + TInt64 diffTicks = (TInt64)User::FastCounter() - (TInt64)iStartTicks; + if(diffTicks < 0) + { + diffTicks = KMaxTUint + diffTicks + 1; + } + TheMemOpTicks[iOpType] += diffTicks; + } + } + private: + TMemOpType iOpType; + TUint32 iStartTicks; + }; + +# define __MEM_CALL(aMemOpType, a1, a2) TMemCallCounter memCallCounter(aMemOpType, a1, a2) + + ////////////////////////////////////////////////////////////////////////////// + ////////////////////////// OS layer calls //////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + + //The OS porting layer call types + enum TOsOpType + { + EOsFileClose, + EOsFileRead, + EOsFileWrite, + EOsFileTruncate, + EOsFileSync, + EOsFileFileSize, + EOsFileLock, + EOsFileUnlock, + EOsFileCheckReservedLock, + EOsFileFileControl, + EOsFileSectorSize, + EOsFileDeviceCharacteristics, + // + EOsVfsOpen, + EOsVfsDelete, + EOsVfsAccess, + EOsVfsFullPathName, + EOsVfsRandomness, + EOsVfsSleep, + EOsVfsCurrentTime, + EOsVfsGetLastError, + // + EOsOpLast + }; + + TBool TheOsProfileEnabled = EFalse; + TUint32 TheOsOpCounter[EOsOpLast] = {0};//Each entry is a counter - how many times specific OS porting layer function has been called + +# define __OS_CALL(aOsOpType, a1, a2) \ + do \ + { \ + if(TheOsProfileEnabled) \ + { \ + ++TheOsOpCounter[aOsOpType];\ + } \ + } \ + while(0) + + ////////////////////////////////////////////////////////////////////////////// + ////////////////////////// OS layer timings ////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////// + + TBool TheOsCallTimeProfileEnabled = EFalse;//If true, the OS porting layer call timings are enabled. + TInt64 TheOsCallTicks[EOsOpLast];//Each entry represents the time in ticks spent in a specific OS porting layer function, + //disregarding the file type (main or journal) + + TBool TheOsCallTimeDetailedProfileEnabled = EFalse;//If true, the OS porting layer call details are enabled and for each call an entry will be added to the log file (epocwind.out). + TInt TheOpCounter = 0;//Operations counter. Each OS porting layer function call increments the counter. + + //Used for storing the OS call details: + // - iType - on which file the call has been made: main database file - 'M', or journal file - 'J'; + // - iIdentifier - two letters identifying the monitored OS porting layer function; + // - iCallCounter - how many times the monitored OS porting layer function has been called; + // - iTicksTotal - the total amount of time in ticks spent in the monitored OS porting layer function; + // - iBytesTotal - the total amount of bytes passed to the monitored OS porting layer function (if it is read or write); + struct TOsCallProfile + { + TOsCallProfile(char aType, char aIdentifier[]) : + iType(aType), + iCallCounter(0), + iTicksTotal(0), + iBytesTotal(0) + { + iIdentifier[0] = aIdentifier[0]; + iIdentifier[1] = aIdentifier[1]; + } + void Zero() + { + iCallCounter = 0; + iTicksTotal = 0; + iBytesTotal = 0; + } + char iType; + char iIdentifier[2]; + TInt iCallCounter; + TInt64 iTicksTotal; + TInt64 iBytesTotal; + }; + + //An array of TOsCallProfile entries, each entry keeps the profile of a specifc OS porting layer function, when + //the function was used on the main database file + TOsCallProfile TheOsCallMProfile[EOsOpLast] = + { + TOsCallProfile('M', "CL"), TOsCallProfile('M', "RD"), TOsCallProfile('M', "WR"), TOsCallProfile('M', "TR"), + TOsCallProfile('M', "SY"), TOsCallProfile('M', "FS"), TOsCallProfile('M', "LK"), TOsCallProfile('M', "UL"), + TOsCallProfile('M', "CL"), TOsCallProfile('M', "FC"), TOsCallProfile('M', "SS"), TOsCallProfile('M', "DC"), + TOsCallProfile('M', "OP"), TOsCallProfile('M', "DE"), TOsCallProfile('M', "AC"), TOsCallProfile('M', "FN"), + TOsCallProfile('M', "RN"), TOsCallProfile('M', "SL"), TOsCallProfile('M', "CT"), TOsCallProfile('M', "LE") + }; + + //An array of TOsCallProfile entries, each entry keeps the profile of a specifc OS porting layer function, when + //the function was used on the journal file + TOsCallProfile TheOsCallJProfile[EOsOpLast] = + { + TOsCallProfile('J', "CL"), TOsCallProfile('J', "RD"), TOsCallProfile('J', "WR"), TOsCallProfile('J', "TR"), + TOsCallProfile('J', "SY"), TOsCallProfile('J', "FS"), TOsCallProfile('J', "LK"), TOsCallProfile('J', "UL"), + TOsCallProfile('J', "CL"), TOsCallProfile('J', "FC"), TOsCallProfile('J', "SS"), TOsCallProfile('J', "DC"), + TOsCallProfile('J', "OP"), TOsCallProfile('J', "DE"), TOsCallProfile('J', "AC"), TOsCallProfile('J', "FN"), + TOsCallProfile('J', "RN"), TOsCallProfile('J', "SL"), TOsCallProfile('J', "CT"), TOsCallProfile('J', "LE") + }; + + //The main class for the OS porting layer call profiles. + class TOsCallCounter + { + public: + //aOsCallTicksEntryRef - a reference to the related TheOsCallTicks[] entry; + //aProfileRef - a reference to the related TOsCallProfile object - TheOsCallMProfile[] or TheOsCallJProfile[] entry; + //aOffset - file offset in bytes; + //aBytes - amount of bytes to be read/written; + TOsCallCounter(TInt64& aOsCallTicksEntryRef, TOsCallProfile& aOsCallProfileRef, TInt64 aOffset, TInt aBytes) : + iOsCallTicksEntryRef(aOsCallTicksEntryRef), + iOsCallProfileRef(aOsCallProfileRef), + iOffset(aOffset), + iBytes(aBytes) + { + if(TheOsCallTimeProfileEnabled) + { + iStartTicks = User::FastCounter(); + } + } + ~TOsCallCounter() + { + if(TheOsCallTimeProfileEnabled) + { + TInt64 diffTicks = (TInt64)User::FastCounter() - (TInt64)iStartTicks; + if(diffTicks < 0) + { + diffTicks = KMaxTUint + diffTicks + 1; + } + iOsCallTicksEntryRef += diffTicks; + if(TheOsCallTimeDetailedProfileEnabled) + { + ++TheOpCounter; + ++iOsCallProfileRef.iCallCounter; + iOsCallProfileRef.iTicksTotal += diffTicks; + iOsCallProfileRef.iBytesTotal += iBytes; + // 1 2 3 4 5 6 7 8 9 10 + RDebug::Print(_L("'%c','%c%c',%d,%d,%ld,%d,%ld,%ld,%ld\n"), + iOsCallProfileRef.iType, //1 + iOsCallProfileRef.iIdentifier[0], //2 + iOsCallProfileRef.iIdentifier[1], //3 + TheOpCounter, //4 + iOsCallProfileRef.iCallCounter, //5 + iOffset, //6 + iBytes, //7 + diffTicks, //8 + iOsCallProfileRef.iBytesTotal, //9 + iOsCallProfileRef.iTicksTotal); //10 + } + } + } + private: + TInt64& iOsCallTicksEntryRef; + TOsCallProfile& iOsCallProfileRef; + TInt64 iOffset; + TInt iBytes; + TUint32 iStartTicks; + }; + + inline TOsCallProfile& OsCallProfile(TBool aType, TInt aIndex) + { + return aType ? TheOsCallJProfile[aIndex] : TheOsCallMProfile[aIndex]; + } + +# define __OSTIME_COUNTER(aOsCallTicksRef, aOsCallProfileRef, aOffset, aBytes) TOsCallCounter osCallCounter(aOsCallTicksRef, aOsCallProfileRef, aOffset, aBytes) + +#else //_SQLPROFILER + +# define __COUNTER_INCR(counter) void(0) + +# define __FS_CALL(aFsOpType, a1) void(0) + +# define __MEM_CALL(aMemOpType, a1, a2) void(0) + +# define __OS_CALL(aOpType, a1, a2) void(0) + +# define __OSTIME_COUNTER(aOsCallTicksRef, aOsCallProfileRef, aOffset, aBytes) void(0) + +#endif//_SQLPROFILER + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////// Profiling //////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef _SQLPROFILER + +const TInt KMicroSecIn1Sec = 1000000; + +TInt FastCounterFrequency() + { + TInt ticksPerSec = 0; + TInt err = HAL::Get(HAL::EFastCounterFrequency, ticksPerSec); + __ASSERT_ALWAYS(err == KErrNone, User::Panic(KPanicCategory, EPanicFastCounterFreq)); + return ticksPerSec; + } + +TInt sqlite3SymbianProfilerStart(TInt aCounterType) + { + const TSqlResourceProfiler::TSqlCounter KCounterType = static_cast (aCounterType); + switch(KCounterType) + { + case TSqlResourceProfiler::ESqlCounterFileIO: + TheFileIoProfileEnabled = ETrue; + break; + case TSqlResourceProfiler::ESqlCounterOsCall: + TheOsProfileEnabled = ETrue; + break; + case TSqlResourceProfiler::ESqlCounterOsCallTime: + TheOsCallTimeProfileEnabled = ETrue; + break; + case TSqlResourceProfiler::ESqlCounterOsCallDetails: + TheOsCallTimeProfileEnabled = ETrue; + TheOsCallTimeDetailedProfileEnabled = ETrue; + break; + case TSqlResourceProfiler::ESqlCounterMemory: + TheMemProfileEnabled = ETrue; + break; + case TSqlResourceProfiler::ESqlCounterMaxAlloc: + TheMaxAllocProfileEnabled = ETrue; + break; + default: + return KErrNotSupported; + } + return KErrNone; + } + +TInt sqlite3SymbianProfilerStop(TInt aCounterType) + { + const TSqlResourceProfiler::TSqlCounter KCounterType = static_cast (aCounterType); + switch(KCounterType) + { + case TSqlResourceProfiler::ESqlCounterFileIO: + TheFileIoProfileEnabled = EFalse; + break; + case TSqlResourceProfiler::ESqlCounterOsCall: + TheOsProfileEnabled = EFalse; + break; + case TSqlResourceProfiler::ESqlCounterOsCallTime: + TheOsCallTimeProfileEnabled = EFalse; + break; + case TSqlResourceProfiler::ESqlCounterOsCallDetails: + TheOsCallTimeDetailedProfileEnabled = EFalse; + TheOsCallTimeProfileEnabled = EFalse; + break; + case TSqlResourceProfiler::ESqlCounterMemory: + TheMemProfileEnabled = EFalse; + break; + case TSqlResourceProfiler::ESqlCounterMaxAlloc: + TheMaxAllocProfileEnabled = EFalse; + break; + default: + return KErrNotSupported; + } + return KErrNone; + } + +TInt sqlite3SymbianProfilerReset(TInt aCounterType) + { + const TSqlResourceProfiler::TSqlCounter KCounterType = static_cast (aCounterType); + switch(KCounterType) + { + case TSqlResourceProfiler::ESqlCounterFileIO: + Mem::FillZ(TheFileOpCounter, sizeof(TheFileOpCounter)); + TheFileWriteAmount = TheFileReadAmount = 0; + break; + case TSqlResourceProfiler::ESqlCounterOsCall: + Mem::FillZ(TheOsOpCounter, sizeof(TheOsOpCounter)); + break; + case TSqlResourceProfiler::ESqlCounterOsCallTime: + case TSqlResourceProfiler::ESqlCounterOsCallDetails: + TheOpCounter = 0; + Mem::FillZ(TheOsCallTicks, sizeof(TheOsCallTicks)); + for(TInt i=0;i (aCounterType); + switch(KCounterType) + { + case TSqlResourceProfiler::ESqlCounterFileIO: + for(TInt i=0;i:\" + process's private data path. Initialized in sqlite3SymbianFsOpen(). + //Used for storing sqlite temporary files. + TInt64 iSeed; + RAllocator* iAllocator; + + enum {KZeroBufSize = SQLITE_DEFAULT_SECTOR_SIZE}; + TBuf8 iZeroBuf; + +private: + static COsLayerData* iOsLayerData; + TInt iStoredOsErrorCode; //Contains the last OS error code. + const RMessage2* iMessage; //Fh data + TBool iReadOnly; //Fh data + }; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// TDbFile struct declaration ///////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** +TDbFile derives from the sqlite3_file structure, adding data members needed for processing the SQLite requests to the OS layer. +When SQLite needs an access to a file, SQLite allocates memory for a new TDbFile instance and passes a pointer to that +instance to TVfs::Open(). TVfs::Open() creates/opens the file and initializes the TDbFile instance. +SQLite uses the initialized TDbFile instance (actually SQLite knows and uses the sqlite3_file, the base structure) +every time when needs to read or write from/to the file, using for that an appropriate TFileIo method. + +Note: currently RFileBuf64 object is used instead of RFile64. That improves the read/write file performance. + +No virtual methods here! sqlite3_file contains data members. If a virtual method is added, that will shift the offset of the +data members from the beginning of the sqlite3_file object by 4 bytes. This is not what SQLite (C code) expects. + +@internalComponent + +@see TVfs +@see TFileIo +@see TVfs::Open() +*/ +NONSHARABLE_STRUCT(TDbFile) : public sqlite3_file + { + inline TDbFile(); + RFileBuf64 iFileBuf; + HBufC* iFullName; //Used for the "delete file" operation (RFile64::FullName() makes an IPC call!) + TInt iLockType; //File lock type + TBool iReadOnly; //True if the file is read-only + TInt iSectorSize; //Media sector-size + TInt iDeviceCharacteristics; + TSqlFreePageCallback iFreePageCallback; +#ifdef _SQLPROFILER + TBool iIsJournal; +#endif + }; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// TFileIo class declaration ////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** +TFileIo class offers static methods for performing operations on a file. +Every TFileIo method has a pointer to a sqlite3_file instance (so, a TDbFile instance) as its first argument. + +SQLite never accesses the file system directly, only through function pointers, data members of the sqlite3_io_methods structure. +The OS porting layer defines a single instance of sqlite3_io_methods structure, TheFileIoApi, and uses the TFileIo to initialize the +sqlite3_io_methods data members (function pointers). +Every time when SQLite creates/opens a file using TVfs::Open(), TVfs::Open() will pass back to SQLite a pointer to the single +initialized sqlite3_io_methods instance (TheFileIoApi) that will be used later by SQLite for accessing the file. + +@internalComponent + +@see TVfs +@see TVfs::Open() +@see TheFileIoApi +@see TDbFile +*/ +NONSHARABLE_CLASS(TFileIo) + { +public: + static int Close(sqlite3_file* aDbFile); + static int Read(sqlite3_file* aDbFile, void* aBuf, int aAmt, sqlite3_int64 aOffset); + static int Write(sqlite3_file* aDbFile, const void* aData, int aAmt, sqlite3_int64 aOffset); + static int Truncate(sqlite3_file* aDbFile, sqlite3_int64 aLength); + static int Sync(sqlite3_file* aDbFile, int aFlags); + static int FileSize(sqlite3_file* aDbFile, sqlite3_int64* aSize); + static int Lock(sqlite3_file* aDbFile, int aLockType); + static int Unlock(sqlite3_file* aDbFile, int aLockType); + static int CheckReservedLock(sqlite3_file* aDbFile, int *aResOut); + static int FileControl(sqlite3_file* aDbFile, int aOp, void* aArg); + static int SectorSize(sqlite3_file* aDbFile); + static int DeviceCharacteristics(sqlite3_file* aDbFile); + }; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// TVfs class declaration ///////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** +TVfs ("VFS" - virtual file system) class offers methods for creating/openning a file, deleting a file, +a "sleep" method, a "time" method, a "rand" method, etc. +SQLite never accesses the OS API directly, only through the API offered by TVfs and TFileIo classes. + +@internalComponent + +@see TFileIo +@see TheVfsApi +@see COsLayerData +*/ +NONSHARABLE_CLASS(TVfs) + { +public: + static int Open(sqlite3_vfs* aVfs, const char* aFileName, sqlite3_file* aDbFile, int aFlags, int* aOutFlags); + static int Delete(sqlite3_vfs* aVfs, const char* aFileName, int aSyncDir); + static int Access(sqlite3_vfs* aVfs, const char* aFileName, int aFlags, int* aResOut); + static int FullPathName(sqlite3_vfs* aVfs, const char* aRelative, int aBufLen, char* aBuf); + static int Randomness(sqlite3_vfs* aVfs, int aBufLen, char* aBuf); + static int Sleep(sqlite3_vfs* aVfs, int aMicrosec); + static int CurrentTime(sqlite3_vfs* aVfs, double* aNow); + static int GetLastError(sqlite3_vfs *sVfs, int aBufLen, char* aBuf); +private: + static TInt DoOpenFromHandle(TDbFile& aDbFile, const RMessage2& aMsg, TBool aReadOnly); + static inline TInt DoGetVolumeIoParamInfo(RFs& aFs, TInt aDriveNo, TVolumeIOParamInfo& aVolumeInfo); + static TInt DoGetDeviceCharacteristics(const TDriveInfo& aDriveInfo, const TVolumeIOParamInfo& aVolumeInfo); + static TInt DoGetSectorSize(const TDriveInfo& aDriveInfo, const TVolumeIOParamInfo& aVolumeInfo); + static TInt DoGetDeviceCharacteristicsAndSectorSize(TDbFile& aDbFile, TInt& aRecReadBufSize); + + }; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////// Global variables, constants //////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// The following macro specifies the size of the RFileBuf64 file buffer in KB: +// __SQLITE_OS_SYMBIAN_FILEBUF_KBSIZE__ +// +// If not set, a default value of 8 is used +#if !defined(__SQLITE_OS_SYMBIAN_FILEBUF_KBSIZE__) +#define __SQLITE_OS_SYMBIAN_FILEBUF_KBSIZE__ 8 +#endif +const TInt KFileBufSize = __SQLITE_OS_SYMBIAN_FILEBUF_KBSIZE__ * 1024; + +/** +Pointer to the single COsLayerData instance. + +@see COsLayerData + +@internalComponent +*/ +COsLayerData* COsLayerData::iOsLayerData = NULL; + +/** +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 +*/ +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 + }; + +/** +Single sqlite3_vfs instance, which data members (function pointers) are initialized with the addresses of +TVfs members. TheVfsApi also keeps information regarding some other OS dependend characteristics like +the TDbFile size and max file name length. +TheVfsApi is used by SQLite for accessing needed OS API. + +TheVfsApi can't be a constant definition. SQLite expects the "sqlite3_vfs" object to be a R/W one, because +SQLite may have and use a chain of sqlite3_vfs instances. + +@see TVfs +@see TTFileIo +@see TDbFile + +@internalComponent +*/ +static sqlite3_vfs TheVfsApi = + { + 1, //iVersion + sizeof(TDbFile), //szOsFile + KMaxFileName, //mxPathname + 0, //pNext + "SymbianSql", //zName + 0, //pAppData + &TVfs::Open, + &TVfs::Delete, + &TVfs::Access, + &TVfs::FullPathName, + 0, + 0, + 0, + 0, + &TVfs::Randomness, + &TVfs::Sleep, + &TVfs::CurrentTime, + &TVfs::GetLastError + }; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////// COsLayerData class definition ////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** +Creates a single COsLayerData instance. + +@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. + +@panic Sqlite 16 In _DEBUG mode - the COsLayerData instance has been created already. +*/ +/* static */ TInt COsLayerData::Create() + { + __ASSERT_DEBUG(!COsLayerData::iOsLayerData, User::Panic(KPanicCategory, EPanicOsLayerDataExists)); + if(!COsLayerData::iOsLayerData) + { + COsLayerData::iOsLayerData = new COsLayerData; + if(!COsLayerData::iOsLayerData) + { + return KErrNoMemory; + } + TInt err = COsLayerData::iOsLayerData->DoCreate(); + if(err != KErrNone) + { + delete COsLayerData::iOsLayerData; + COsLayerData::iOsLayerData = NULL; + return err; + } + } + return KErrNone; + } + +/** +Destroys the COsLayerData instance. +*/ +/* static */ inline void COsLayerData::Destroy() + { + delete COsLayerData::iOsLayerData; + COsLayerData::iOsLayerData = NULL; + } + +/** +Returns a reference to the single COsLayerData instance. + +@panic Sqlite 1 In _DEBUG mode if the COsLayerData instance is NULL. +*/ +/* static */ inline COsLayerData& COsLayerData::Instance() + { + __ASSERT_DEBUG(COsLayerData::iOsLayerData != NULL, User::Panic(KPanicCategory, EPanicNullOsLayerDataPtr)); + return *COsLayerData::iOsLayerData; + } + +/** +Sets the last OS error code data member. The stored OS error code data member will be set only if it is +KErrNone. (If it is not KErrNone it means that its value has not been accessed yet) +An exception from the rule described above is KErrDiskFull error which, if happens, will be set always, because +this error has a special meaning for the database clients - special actions may have to be taken if the +disk is full. + +@param aError The OS error code +@return The OS error code +*/ +inline TInt COsLayerData::SetOsErrorCode(TInt aError) + { + if(iStoredOsErrorCode == KErrNone || aError == KErrDiskFull) + { + iStoredOsErrorCode = aError; + } + return aError; + } + +/** +Returns the last stored OS error code, which was stored by SetOsErrorCode() call. +The function also resets the stored OS error code to KErrNone. + +@return The last stored OS error code +*/ +inline TInt COsLayerData::StoredOsErrorCode() + { + TInt err = iStoredOsErrorCode; + iStoredOsErrorCode = KErrNone; + return err; + } + +/** +Stores the RMessage2 object address, file and file session handles and the read-only flag for later use when SQLite issues a +request for open the database file (private secure database). + +The aMsg argument of the function can be NULL, because this fucntion is also used to reset the stored "file handle" data. + +How this function is used: +1) When the SQL server receives a request to establish a connection with private secure database, the SQL server + will add additional information to the private secure database file name, such as: + - the file handle (the private secure database is opened by the client side dll - sqldb.dll); + - a pointer to the RMessage2 object used in this request; +2) The passed additional information will be used for adopting the file handle by calling RFile64::AdoptFromClient(). +3) Before calling TVfs::Open() to establish a connection with the database, SQLite will call TVfs::FullPathName() + to retrieve the database file full path +4) TVfs::FullPathName() will detect that the file name contains an additional information and will extraxt the information + calling COsLayerData::StoreFhData(). +5) After TVfs::FullPathName() SQLite calls TVfs::Open() where the extracted information will be used for adopting the file handle + +@param aMsg A pointer to the current RMessage2 object +@param aReadOnly True if the private secure database is read-only +*/ +inline void COsLayerData::StoreFhData(const RMessage2* aMsg, TBool aReadOnly) + { + iMessage = aMsg; + iReadOnly = aReadOnly; + } + +/** +Retrieves the RMessage2 object, file and file session handles. The stored data will be reset. +This function is used by TVfs::Open(), when a request for opening a secure private database is processed. + +@param aMsg Output parameter. A reference to a RMessage2 pointer, which will be initialized with the stored RMessage2 pointer. +@param aReadOnly Output parameter. The store read-only flag value will be set there. + +@panic Sqlite 13 In _DEBUG mode - aMsg is NULL. +*/ +inline void COsLayerData::RetrieveAndResetFhData(const RMessage2*& aMsg, TBool& aReadOnly) + { + __ASSERT_DEBUG(iMessage != NULL, User::Panic(KPanicCategory, EPanicInvalidFhData)); + aMsg = iMessage; + aReadOnly = iReadOnly; + iMessage = NULL; + } + +/** +Initializes the COsLayerData data members with their default values. +*/ +inline COsLayerData::COsLayerData() : + iAllocator(0), + iStoredOsErrorCode(KErrNone), + iMessage(0), + iReadOnly(EFalse) + { + TTime now; + now.UniversalTime(); + iSeed = now.Int64(); + iZeroBuf.FillZ(COsLayerData::KZeroBufSize); + } + +/** +Destroys the COsLayerData instance. + +Note: No SQLite functions should be called inside the destructor, because SQLite is already shutdown-ed! +*/ +inline COsLayerData::~COsLayerData() + { + __FS_CALL(EFsOpFsClose, 0); + iFs.Close(); + } + +/** +Creates a file session instance. + +Creates the private path, where the temporary files will be stored (on the system drive). + +In a case of a failure COsLayerData::DoCreate() does not close the file session. +This will be made in the calling function - COsLayerData::Create(). + +Note: No SQLite functions should be called inside the DoCreate() implementation, because SQLite is not initialized yet! + +@return KErrNone, The operation has completed succesfully; + KErrGeneral The registration of TheVfsApi has failed; + KErrNoMemory, Out of memory condition has occured; + Note that other system-wide error codes may also be returned. + +@see TVfs +@see TheVfsApi +*/ +TInt COsLayerData::DoCreate() + { + iAllocator = &User::Allocator(); + __FS_CALL(EFsOpFsConnect, 0); + TInt err = iFs.Connect(); + if(err != KErrNone) + { + return err; + } + //Get the system drive + __FS_CALL(EFsOpFsGetSystemDrive, 0); + TInt sysDrive = static_cast(RFs::GetSystemDrive()); + __FS_CALL(EFsOpFsCreatePrivatePath, 0); + if((err = iFs.CreatePrivatePath(sysDrive)) != KErrNone && err != KErrAlreadyExists) + { + return err; + } + TFileName privateDir; + __FS_CALL(EFsOpFsPrivatePath, 0); + if((err = iFs.PrivatePath(privateDir)) != KErrNone) + { + return err; + } + TDriveUnit drive(sysDrive); + TDriveName driveName = drive.Name(); + TParse parse; + (void)parse.Set(driveName, &privateDir, 0);//this call can't fail + iSysPrivDir.Copy(parse.DriveAndPath()); + return KErrNone; + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////// Symbian OS specific functions (called by the SQL server) /////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** +Returns the last OS error which occured durring the operations with the database files. +The per-thread variable, where the last OS error is hold, will be set to KErrNone. + +This function is part of Symbian OS specific SQLITE API. + +@return The last OS error. +@internalComponent +*/ +TInt sqlite3SymbianLastOsError(void) + { + return COsLayerData::Instance().StoredOsErrorCode(); + } + +/** +This function must be called once before any other SQLITE API call. +The function: +@code + - Initializes the OS poting layer; + - Initializes the SQLite library; +@endcode + +This function is part of the Symbian OS specific SQLITE API. + +@return Symbian OS specific error code, including KErrNoMemory. + +@internalComponent +*/ +TInt sqlite3SymbianLibInit(void) + { + TInt osErr = COsLayerData::Create(); + if(osErr != KErrNone) + { + return osErr; + } + osErr = KErrNone; + TInt sqliteErr = sqlite3_initialize(); + if(sqliteErr != SQLITE_OK) + { + osErr = sqliteErr == SQLITE_NOMEM ? KErrNoMemory : KErrGeneral; + COsLayerData::Destroy(); + } + return osErr; + } + +/** +This function must be called once after finishing working with sqlite. +The function: +@code + - Shuts down the SQLite library; + - Releases the allocated by the OS porting layer resources; +@endcode + +This function is part of the Symbian OS specific SQLITE API. + +@internalComponent +*/ +void sqlite3SymbianLibFinalize(void) + { + (void)sqlite3_shutdown(); + COsLayerData::Destroy(); + } + +/** +This function is part of Symbian OS specific SQLITE API. + +@return A reference to RFs instance used for sqlite file I/O operations. +@internalComponent +*/ +RFs& sqlite3SymbianFs(void) + { + return COsLayerData::Instance().iFs; + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////// 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, aFileNameDestBuf.MaxLength()); + //If len == aFileNameDestBuf.MaxLength(), then the output buffer is too small. + if(len > 0 && len < aFileNameDestBuf.MaxLength()) + { + 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, aFileNameDestBuf.MaxLength()); + //If len == aFileNameDestBuf.MaxLength(), then the output buffer is too small. + if(len > 0 && len < aFileNameDestBuf.MaxLength()) + { + aFileNameDestBuf.SetLength(len); + return ETrue; + } + return EFalse; + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////// File name, containing handles, functions //////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +const char KFhSeparator = '|'; //The symbol, which when used in the file name means that the string does not contain a real file name but file handles +const TInt KFhSessHandleIdx = 2;//The index of the file session handle in RMessage2 object +const TInt KFhFileHandleIdx = 3;//The index of the file handle in RMessage2 object +const TInt KFhMarkPos = 0; //if the symbol in this position is KFhSeparator, then the string contains file handles +const TInt KFhRoPos = 1; //read-only flag position in the string +const TInt KFhMsgAddrPos = 2; //RMessage2 address position in the string +const TInt KFhMsgAddrLen = 8; //RMessage2 address length +//const TInt KFhDrivePos = 1; //Drive position in the string (after removing the read-only flag and RMessage2 object's address) + +/** +File name string types: +@code + - ENotFhStr - The string does not contain file handles; + - EFhStr - The string contain file handles, but is not main db file; + - EFhMainDbStr - The string contain file handles and is the main db file; +@endcode + +Every file name passed to the OS porting layer's TVfs::Open() method falls into one of the following three categories: +ENotFhStr - the file name does not contain handles, EFhStr - the file name contains handles but is not a name of a private +secure database, EFhMainDbStr - the file name contains handles and is a name of a private secure database. + +@see TVfs::Open() +@see FhStringProps() + +@internalComponent +*/ +enum TFhStrType + { + ENotFhStr, //The string does not contain file handles + EFhStr, //The string contain file handles, but is not main db file + EFhMainDbStr //The string contain file handles and is the main db file + }; + +/** +The TVfs::Open() implementation uses this function to determine the type of the file name, which can be +one of the TFhStrType enum item values. + +@param aFileName Zero-terminated, UTF8 encoded file name. +@return The file name type, one of the TFhStrType enum item values. + +@see TVfs::Open() +@see TFhStrType + +@internalComponent +*/ +static TFhStrType FhStringProps(const char* aFileName) + { + char* first = strchr(aFileName, KFhSeparator); + if(!first) + { + return ENotFhStr; + } + char* last = strchr(first + 1, KFhSeparator); + if(!last) + { + return ENotFhStr; + } + return *(last + 1) == 0 ? EFhMainDbStr : EFhStr; + } + +/** +Replaces all invalid characters in aFileName. +If the file name contains handles (so that's a private secure database related name), the additional +information (handles, flags, object addresses) has to be excluded from the name in order to make it usable +by the file system. + +@param aFileName Output parameter. The cleaned file name will be copied there. +@param aPrivateDir The SQL server private data cage. + +@see TVfs::Open() +@see TFhStrType +@see FhStringProps() + +@internalComponent +*/ +static void FhConvertToFileName(TDes& aFileName, const TDesC& aPrivateDir) + { + TInt firstPos = aFileName.Locate(TChar(KFhSeparator)); + if(firstPos >= 0) + { + aFileName.Delete(firstPos, 1); + TInt lastPos = aFileName.LocateReverse(TChar(KFhSeparator)); + if(lastPos >= 0) + { + aFileName.Delete(lastPos, 1); + TParse parse; + (void)parse.Set(aFileName, &aPrivateDir, 0);//the file name should be verified by the server + aFileName.Copy(parse.FullName()); + } + } + } + +/** +Extracts the read-only flag and RMessage address from aDbFileName and stores them in single COsLayerData instance. + +@param aDbFileName Input/output parameter. The file name. + It will be reformatted and won't contain the already extracted data. + The aDbFileName format is: +@code + || +@endcode + +@see TVfs::Open() +@see TFhStrType +@see FhStringProps() + +@internalComponent + +@panic Sqlite 12 In _DEBUG mode - invalid position of the "|" character in the file name. +@panic Sqlite 12 In _DEBUG mode - no RMessage2 pointer can be extracted from the file name. +@panic Sqlite 12 In _DEBUG mode - the extracted RMessage2 pointer is NULL. +*/ +static void FhExtractAndStore(TDes& aDbFileName) + { + TInt fhStartPos = aDbFileName.Locate(TChar(KFhSeparator)); + __ASSERT_DEBUG(fhStartPos == KFhMarkPos, User::Panic(KPanicCategory, EPanicInvalidFhStr)); + //If this file name string contains file handles + if(fhStartPos == KFhMarkPos) + { + //Extract from aDbFileName string RMessage2 object's address + TLex lex(aDbFileName.Mid(fhStartPos + KFhMsgAddrPos, KFhMsgAddrLen)); + TUint32 addr; + TInt err = lex.Val(addr, EHex); + __ASSERT_DEBUG(err == KErrNone, User::Panic(KPanicCategory, EPanicInvalidFhStr)); + if(err == KErrNone) + { + //Cast the address to RMessage2 pointer. + const RMessage2* msg = reinterpret_cast (addr); + __ASSERT_DEBUG(msg != NULL, User::Panic(KPanicCategory, EPanicInvalidFhStr)); + if(msg) + { + //Store the data from aDbFileName in the single COsLayerData instance. + TBool readOnly = aDbFileName[fhStartPos + KFhRoPos] > '0'; + COsLayerData::Instance().StoreFhData(msg, readOnly); + //Remove: read-only flag and RMessage2 object's address + aDbFileName.Delete(fhStartPos + KFhRoPos, 1 + KFhMsgAddrLen); + } + } + } + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////// TDbFile class definition /////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** +Initializes TDbFile data members with their default values. +*/ +inline TDbFile::TDbFile() : + iFileBuf(KFileBufSize), + iFullName(0), + iLockType(SQLITE_LOCK_NONE), + iReadOnly(EFalse), + iSectorSize(0), + iDeviceCharacteristics(-1) + { +#ifdef _SQLPROFILER + iIsJournal = EFalse; +#endif + 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); + __OS_CALL(EOsFileClose, 0, 0); + __OSTIME_COUNTER(TheOsCallTicks[EOsFileClose], ::OsCallProfile(dbFile.iIsJournal, EOsFileClose), 0, 0); + __FS_CALL(EFsOpFileClose, 0); + dbFile.iFileBuf.Close(); + if(dbFile.iFullName) + { + __FS_CALL(EFsOpFileDelete, 0); + (void)COsLayerData::Instance().iFs.Delete(*dbFile.iFullName); + delete dbFile.iFullName; + } + 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_IOERR_READ, The file read or seek operation has failed; + SQLITE_IOERR_SHORT_READ, The amount of the data read is less than aAmt; + SQLITE_IOERR_NOMEM, An out of memory condition has occured; + SQLITE_OK, The operation has completed successfully. + +If the function succeeds, COsLayerData::SetOsErrorCode() is called with KErrNone, otherwise COsLayerData::SetOsErrorCode() is called +with the reported by the OS API error. The stored error code will be used later by the SQLite API caller. + +@see COsLayerData::SetOsErrorCode() +@see TDbFile +*/ +/* static */ int TFileIo::Read(sqlite3_file* aDbFile, void* aBuf, int aAmt, sqlite3_int64 aOffset) + { + SQLUTRACE_PROFILER(aDbFile); + SYMBIAN_TRACE_SQL_EVENTS_ONLY(UTF::Printf(UTF::TTraceContext(UTF::EInternals), KFileRead, aAmt, aOffset)); + TDbFile& dbFile = ::DbFile(aDbFile); + __OS_CALL(EOsFileRead, 0, 0); + __COUNTER_INCR(TheSqlSrvProfilerFileRead); + __OSTIME_COUNTER(TheOsCallTicks[EOsFileRead], ::OsCallProfile(dbFile.iIsJournal, EOsFileRead), aOffset, aAmt); + TPtr8 ptr((TUint8*)aBuf, 0, aAmt); + TInt err = dbFile.iFileBuf.Read(aOffset, ptr); + TInt cnt = ptr.Length(); + TInt sqliteErr = SQLITE_IOERR_READ; + switch(err) + { + case KErrNone: + sqliteErr = SQLITE_OK; + if(cnt != aAmt) + { + Mem::FillZ(static_cast (aBuf) + cnt, aAmt - cnt); + sqliteErr = SQLITE_IOERR_SHORT_READ; + err = KErrEof; + } + break; + case KErrEof: + Mem::FillZ(static_cast (aBuf) + cnt, aAmt - cnt); + sqliteErr = SQLITE_IOERR_SHORT_READ; + break; + case KErrNoMemory: + sqliteErr = SQLITE_IOERR_NOMEM; + break; + default: + break; + } + COsLayerData::Instance().SetOsErrorCode(err); + 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. + +If the write operation is in the 1st db file page and there is a registered "free pages" callback +(TDbFile::iFreePageCallback) and the free pages count is above the defined value, +then the callback will be called. + +@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_FULL, The file write or seek operation has failed. + The disk is full; + SQLITE_IOERR_NOMEM, An out of memory condition has occured; + SQLITE_OK, The operation has completed successfully. + +If the function succeeds, COsLayerData::SetOsErrorCode() is called with KErrNone, otherwise COsLayerData::SetOsErrorCode() is called +with the reported by the OS API error. The stored error code will be used later by the SQLite API caller. + +@see COsLayerData::SetOsErrorCode() +@see TDbFile +*/ +/* static */ int TFileIo::Write(sqlite3_file* aDbFile, const void* aData, int aAmt, sqlite3_int64 aOffset) + { + SQLUTRACE_PROFILER(aDbFile); + SYMBIAN_TRACE_SQL_EVENTS_ONLY(UTF::Printf(UTF::TTraceContext(UTF::EInternals), KFileWrite, aAmt, aOffset)); + TDbFile& dbFile = ::DbFile(aDbFile); + __OS_CALL(EOsFileWrite, 0, 0); + __COUNTER_INCR(TheSqlSrvProfilerFileWrite); + __OSTIME_COUNTER(TheOsCallTicks[EOsFileWrite], ::OsCallProfile(dbFile.iIsJournal, EOsFileWrite), aOffset, aAmt); + TInt err = KErrAccessDenied; + if(!dbFile.iReadOnly) + { + TPtrC8 ptr((const TUint8*)aData, aAmt); + err = dbFile.iFileBuf.Write(aOffset, ptr); + } + COsLayerData::Instance().SetOsErrorCode(err); + + const TInt KFreePageCountOffset = 36;//hard-coded constant. SQLite does not offer anything - a constant or #define. + //The checks in the "if" bellow do: + // - "err == KErrNone" - check the free page count only after a successful "write"; + // - "aOffset == 0" - check the free page count only if the write operation affects the system page (at aOffset = 0); + // - "aAmt >= (KFreePageCountOffset + sizeof(int))" - check the free page count only if the amount of bytes to be written + // is more than the offset of the free page counter (othewrise the free page counter is not affected + // by this write operation); + // - "dbFile.iFreePageCallback.IsValid()" - check the free page count only if there is a valid callback; + if(err == KErrNone && aOffset == 0 && aAmt >= (KFreePageCountOffset + sizeof(int)) && dbFile.iFreePageCallback.IsValid()) + { + const TUint8* ptr = static_cast (aData) + KFreePageCountOffset; + TInt freePageCount = (ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3]; + dbFile.iFreePageCallback.CheckAndCallback(freePageCount); + } + + return err == KErrNone ? SQLITE_OK : (err == KErrNoMemory ? SQLITE_IOERR_NOMEM : SQLITE_FULL); + } + +/** +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_FULL, The disk is full; + SQLITE_IOERR, This is a read-only file. + The file truncate operation has failed; + SQLITE_IOERR_NOMEM, An out of memory condition has occured; + SQLITE_OK, The operation has completed successfully. + +If the function succeeds, COsLayerData::SetOsErrorCode() is called with KErrNone, otherwise COsLayerData::SetOsErrorCode() is called +with the reported by the OS API error. The stored error code will be used later by the SQLite API caller. + +@see COsLayerData::SetOsErrorCode() +@see TDbFile +*/ +/* static */ int TFileIo::Truncate(sqlite3_file* aDbFile, sqlite3_int64 aLength) + { + SQLUTRACE_PROFILER(aDbFile); + SYMBIAN_TRACE_SQL_EVENTS_ONLY(UTF::Printf(UTF::TTraceContext(UTF::EInternals), KFileTruncate, aLength)); + TDbFile& dbFile = ::DbFile(aDbFile); + __OS_CALL(EOsFileTruncate, 0, 0); + __COUNTER_INCR(TheSqlSrvProfilerFileSetSize); + __OSTIME_COUNTER(TheOsCallTicks[EOsFileTruncate], ::OsCallProfile(dbFile.iIsJournal, EOsFileTruncate), aLength, 0); + if(dbFile.iReadOnly) + { + COsLayerData::Instance().SetOsErrorCode(KErrAccessDenied); + return SQLITE_IOERR; + } + __FS_CALL(EFsOpFileSetSize, 0); + TInt err = dbFile.iFileBuf.SetSize(aLength); + COsLayerData::Instance().SetOsErrorCode(err); + return err == KErrNone ? SQLITE_OK : (err == KErrNoMemory ? SQLITE_IOERR_NOMEM : SQLITE_IOERR); + } + +/** +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. + +@return SQLITE_IOERR, This is a read-only file. + The file flush operation has failed; + SQLITE_IOERR_NOMEM, An out of memory condition has occured; + SQLITE_OK, The operation has completed successfully. + +If the function succeeds, COsLayerData::SetOsErrorCode() is called with KErrNone, otherwise COsLayerData::SetOsErrorCode() is called +with the reported by the OS API error. The stored error code will be used later by the SQLite API caller. + +@see COsLayerData::SetOsErrorCode() +@see TDbFile +*/ +/* static */int TFileIo::Sync(sqlite3_file* aDbFile, int /* aFlags */) + { + SQLUTRACE_PROFILER(aDbFile); + TDbFile& dbFile = ::DbFile(aDbFile); + __OS_CALL(EOsFileSync, 0, 0); + __COUNTER_INCR(TheSqlSrvProfilerFileSync); + __OSTIME_COUNTER(TheOsCallTicks[EOsFileSync], ::OsCallProfile(dbFile.iIsJournal, EOsFileSync), 0, 0); + if(dbFile.iReadOnly) + { + COsLayerData::Instance().SetOsErrorCode(KErrAccessDenied); + return SQLITE_IOERR; + } + __FS_CALL(EFsOpFileSync, 0); + TInt err = dbFile.iFileBuf.Flush(); + COsLayerData::Instance().SetOsErrorCode(err); + return err == KErrNone ? SQLITE_OK : (err == KErrNoMemory ? SQLITE_IOERR_NOMEM : SQLITE_IOERR); + } + +/** +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, The file size operation has failed; + SQLITE_IOERR_NOMEM, An out of memory condition has occured; + SQLITE_OK, The operation has completed successfully. + +If the function succeeds, COsLayerData::SetOsErrorCode() is called with KErrNone, otherwise COsLayerData::SetOsErrorCode() is called +with the reported by the OS API error. The stored error code will be used later by the SQLite API caller. + +@see COsLayerData::SetOsErrorCode() +@see TDbFile +*/ +/* static */ int TFileIo::FileSize(sqlite3_file* aDbFile, sqlite3_int64* aSize) + { + SQLUTRACE_PROFILER(aDbFile); + TDbFile& dbFile = ::DbFile(aDbFile); + __OS_CALL(EOsFileFileSize, 0, 0); + __OSTIME_COUNTER(TheOsCallTicks[EOsFileFileSize], ::OsCallProfile(dbFile.iIsJournal, EOsFileFileSize), 0, 0); + __FS_CALL(EFsOpFileSize, 0); + TInt err = dbFile.iFileBuf.Size(*aSize); + COsLayerData::Instance().SetOsErrorCode(err); + if(err == KErrNone) + { + return SQLITE_OK; + } + return err == KErrNoMemory ? SQLITE_IOERR_NOMEM : SQLITE_IOERR; + } + +/** +SQLite OS porting layer API. + +Locks the file, referred by the aDbFile parameter, with the specified lock type. +Since this is a single-threaded OS porting layer implementation, the file is not actually locked - small +performance optimisation. The file lock type is stored for later use by the CheckReservedLock() call. + +@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. + +@see TFileIo::CheckReservedLock() +@see TFileIo::Unlock() + +@see TDbFile +*/ +/* static */ int TFileIo::Lock(sqlite3_file* aDbFile, int aLockType) + { + SQLUTRACE_PROFILER(aDbFile); + TDbFile& dbFile = ::DbFile(aDbFile); + __OS_CALL(EOsFileLock, 0, 0); + __OSTIME_COUNTER(TheOsCallTicks[EOsFileLock], ::OsCallProfile(dbFile.iIsJournal, EOsFileLock), aLockType, 0); + //If there is already a lock of this type or more restrictive on the database file, do nothing. + if(dbFile.iLockType >= aLockType) + { + return SQLITE_OK; + } + dbFile.iLockType = aLockType; + return SQLITE_OK; + } + +/** +SQLite OS porting layer API. + +Unlocks the file, referred by the aDbFile parameter. +Since this is a single-threaded OS porting layer implementation, the file never gets locked - small +performance optimisation. The Unlock() call only sets the stored file lock type with the aLockType value. + +@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. + +@see TFileIo::CheckReservedLock() +@see TFileIo::Lock() + +@see TDbFile +*/ +/* static */ int TFileIo::Unlock(sqlite3_file* aDbFile, int aLockType) + { + SQLUTRACE_PROFILER(aDbFile); + TDbFile& dbFile = ::DbFile(aDbFile); + __OS_CALL(EOsFileUnlock, 0, 0); + __OSTIME_COUNTER(TheOsCallTicks[EOsFileUnlock], ::OsCallProfile(dbFile.iIsJournal, EOsFileUnlock), aLockType, 0); + dbFile.iLockType = aLockType; + return SQLITE_OK; + } + +/** +SQLite OS porting layer API. + +Checks if the file lock type is SQLITE_LOCK_RESERVED or bigger. +Since this is a single-threaded OS porting layer implementation, the file never gets locked - small +performance optimisation. The CheckReservedLock() call only checks if the stored file lock type +is bigger or equal than SQLITE_LOCK_RESERVED. + +@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_OK. + +@see TFileIo::Lock() +@see TFileIo::Unlock() + +@see TDbFile +*/ +/* static */ int TFileIo::CheckReservedLock(sqlite3_file* aDbFile, int *aResOut) + { + SQLUTRACE_PROFILER(aDbFile); + TDbFile& dbFile = ::DbFile(aDbFile); + __OS_CALL(EOsFileCheckReservedLock, 0, 0); + __OSTIME_COUNTER(TheOsCallTicks[EOsFileCheckReservedLock], ::OsCallProfile(dbFile.iIsJournal, EOsFileCheckReservedLock), 0, 0); + *aResOut = dbFile.iLockType >= SQLITE_LOCK_RESERVED ? 1 : 0; + 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. + +Note: The range of supported operations includes KSqlFcntlRegisterFreePageCallback now. + When the function is called with aOp = KSqlFcntlRegisterFreePageCallback, then a callback will be registered + and called when the number of the free pages goes above certain threshold. + +@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. + If the operation type is KSqlFcntlRegisterFreePageCallback, then aArg points to a TSqlFreePageCallback object, + that contains the free page threshold and the callback. + +@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); + SYMBIAN_TRACE_SQL_EVENTS_ONLY(UTF::Printf(UTF::TTraceContext(UTF::EInternals), KFileFileCtr, aOp)); + TDbFile& dbFile = ::DbFile(aDbFile); + __OS_CALL(EOsFileFileControl, 0, 0); + __OSTIME_COUNTER(TheOsCallTicks[EOsFileFileControl], ::OsCallProfile(dbFile.iIsJournal, EOsFileFileControl), aOp, 0); + TInt err = KErrNone; + switch(aOp) + { + case SQLITE_FCNTL_LOCKSTATE: + *(int*)aArg = dbFile.iLockType; + break; + case KSqlFcntlRegisterFreePageCallback: + { + err = KErrArgument; + if(aArg) + { + TSqlFreePageCallback* rq = static_cast (aArg); + if(rq->IsValid()) + { + dbFile.iFreePageCallback = *rq; + err = KErrNone; + } + } + } + break; + default: + err = KErrArgument; + break; + } + COsLayerData::Instance().SetOsErrorCode(err); + 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); + __OS_CALL(EOsFileSectorSize, 0, 0); + __OSTIME_COUNTER(TheOsCallTicks[EOsFileSectorSize], ::OsCallProfile(dbFile.iIsJournal, EOsFileSectorSize), 0, 0); + __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); + __OS_CALL(EOsFileDeviceCharacteristics, 0, 0); + __OSTIME_COUNTER(TheOsCallTicks[EOsFileDeviceCharacteristics], ::OsCallProfile(dbFile.iIsJournal, EOsFileDeviceCharacteristics), 0, 0); + __ASSERT_DEBUG(dbFile.iDeviceCharacteristics >= 0, User::Panic(KPanicCategory, EPanicInternalError)); + if(dbFile.iDeviceCharacteristics >= 0) + { + return dbFile.iDeviceCharacteristics; + } + return 0; + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// TVfs class definition /////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +/** +Opens a private secure database. +Actually the database file has been created or opened by the client's process. +This function only adopts the passed in aMsg parameter file handle. + +@param aDbFile Output parameter, where the initialized file handle will be stored. +@param aMsg A reference to the current RMessage2 instance. Contains the file handle of the database created + or opened by the client's process. +@param aReadOnly True if the file is read-only. + +@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 TDbFile +@see TVfs::Open() +*/ +/* static */ TInt TVfs::DoOpenFromHandle(TDbFile& aDbFile, const RMessage2& aMsg, TBool aReadOnly) + { + __FS_CALL(EFsOpFileAdopt, 0); + TInt err = aDbFile.iFileBuf.AdoptFromClient(aMsg, KFhSessHandleIdx, KFhFileHandleIdx); + if(err == KErrNone) + { + aDbFile.iReadOnly = aReadOnly; + } + return err; + }; + +/** +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) + { + __FS_CALL(EFsOpFsVolumeIoParam, 0); + 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::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; + __FS_CALL(EFsOpFileDrive, 0); + TInt err = aDbFile.iFileBuf.Drive(driveNo, driveInfo); + if(err != KErrNone) + { + return err; + } + TVolumeIOParamInfo volumeInfo; + err = TVfs::DoGetVolumeIoParamInfo(COsLayerData::Instance().iFs, driveNo, volumeInfo); + if(err != KErrNone) + { + return err; + } + aDbFile.iDeviceCharacteristics = TVfs::DoGetDeviceCharacteristics(driveInfo, volumeInfo); + aDbFile.iSectorSize = TVfs::DoGetSectorSize(driveInfo, volumeInfo); + aRecReadBufSize = volumeInfo.iRecReadBufSize; + return KErrNone; + } + +/** +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. + +If the function succeeds, COsLayerData::SetOsErrorCode() is called with KErrNone, otherwise COsLayerData::SetOsErrorCode() is called +with the reported by the OS API error. The stored error code will be used later by the SQLite API caller. + +@see COsLayerData::SetOsErrorCode() +@see TDbFile +*/ +/* static */ int TVfs::Open(sqlite3_vfs* aVfs, const char* aFileName, sqlite3_file* aDbFile, int aFlags, int* aOutFlags) + { + SQLUTRACE_PROFILER(aVfs); + __OS_CALL(EOsVfsOpen, 0, 0); + __OSTIME_COUNTER(TheOsCallTicks[EOsVfsOpen], ::OsCallProfile(EFalse, EOsVfsOpen), 0, 0); + COsLayerData& osLayerData = COsLayerData::Instance(); + TFileName fname; + if(aFileName && !::ConvertToUnicode(aFileName, fname)) + { + osLayerData.SetOsErrorCode(KErrBadName); + return SQLITE_CANTOPEN; + } + SYMBIAN_TRACE_SQL_EVENTS_ONLY(UTF::Printf(UTF::TTraceContext(UTF::EInternals), KFileOpen, aDbFile, &fname)); + new (aDbFile) TDbFile; + TDbFile& dbFile = ::DbFile(aDbFile); + TFhStrType fhStrType = aFileName ? ::FhStringProps(aFileName) : ENotFhStr; + if(aFileName && (aFlags & SQLITE_OPEN_DELETEONCLOSE)) + { + dbFile.iFullName = fname.Alloc(); + if(!dbFile.iFullName) + { + osLayerData.SetOsErrorCode(KErrNoMemory); + return SQLITE_IOERR_NOMEM; + } + } + TInt recReadBufSize = -1; + TInt err = KErrNone; + if(fhStrType == EFhMainDbStr) + {//Main db file, open from handle + const RMessage2* msg; + TBool readOnly; + osLayerData.RetrieveAndResetFhData(msg, readOnly); + err = msg != NULL ? TVfs::DoOpenFromHandle(dbFile, *msg, readOnly) : KErrGeneral; + } + else + { + if(fhStrType == EFhStr) + {//Not the main db file. Replace invalid characters in the file name + ::FhConvertToFileName(fname, osLayerData.iSysPrivDir);//If fname does not have a path, iSysPrivDir will be used + } + TInt fmode = EFileRead; + if(aFlags & SQLITE_OPEN_READWRITE) + { + fmode |= EFileWrite; + } + if(aFlags & SQLITE_OPEN_EXCLUSIVE) + { + fmode |= EFileShareExclusive; + } + if(!aFileName) + { + __FS_CALL(EFsOpFileCreateTemp, 0); + err = dbFile.iFileBuf.Temp(osLayerData.iFs, osLayerData.iSysPrivDir, fname, fmode); + if(err == KErrNone) + { + dbFile.iFullName = fname.Alloc(); + if(!dbFile.iFullName) + { + err = KErrNoMemory; + } + } + } + else + { + err = KErrAccessDenied; + TInt prevErr = KErrNone; + if(aFlags & SQLITE_OPEN_CREATE) + { + __FS_CALL(EFsOpFileCreate, 0); + prevErr = err = dbFile.iFileBuf.Create(osLayerData.iFs, fname, fmode); + } + if(err != KErrNone && err != KErrNoMemory && err != KErrDiskFull) + { + __FS_CALL(EFsOpFileOpen, 0); + err = dbFile.iFileBuf.Open(osLayerData.iFs, fname, fmode); + } + if((err != KErrNone && err != KErrNoMemory && err != KErrDiskFull) && (aFlags & SQLITE_OPEN_READWRITE)) + { + aFlags &= ~SQLITE_OPEN_READWRITE; + aFlags |= SQLITE_OPEN_READONLY; + fmode &= ~EFileWrite; + __FS_CALL(EFsOpFileOpen, 0); + err = dbFile.iFileBuf.Open(osLayerData.iFs, fname, fmode); + } + if(err != KErrNone && prevErr == KErrAccessDenied) + { + err = KErrAccessDenied; + } + } + } + if(err == KErrNone) + { + err = TVfs::DoGetDeviceCharacteristicsAndSectorSize(dbFile, recReadBufSize); + } + osLayerData.SetOsErrorCode(err); + if(err != KErrNone) + { + __FS_CALL(EFsOpFileClose, 0); + dbFile.iFileBuf.Close(); + delete dbFile.iFullName; + dbFile.iFullName = NULL; + } + else + { + dbFile.pMethods = &TheFileIoApi; + if(fhStrType != EFhMainDbStr) + { + dbFile.iReadOnly = !(aFlags & SQLITE_OPEN_READWRITE); + } + if(aOutFlags) + { + *aOutFlags = dbFile.iReadOnly ? SQLITE_OPEN_READONLY : SQLITE_OPEN_READWRITE; + } + (void)dbFile.iFileBuf.SetReadAheadSize(dbFile.iSectorSize, recReadBufSize); + } +#ifdef _SQLPROFILER + dbFile.iIsJournal = (aFlags & SQLITE_OPEN_MAIN_JOURNAL) || (aFlags & SQLITE_OPEN_TEMP_JOURNAL) || + (aFlags & SQLITE_OPEN_SUBJOURNAL) || (aFlags & SQLITE_OPEN_MASTER_JOURNAL); +#endif + return err == KErrNone ? SQLITE_OK : (err == KErrNoMemory ? SQLITE_IOERR_NOMEM : 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. + +If the function succeeds, COsLayerData::SetOsErrorCode() is called with KErrNone, otherwise COsLayerData::SetOsErrorCode() is called +with the reported by the OS API error. The stored error code will be used later by the SQLite API caller. + +@see COsLayerData::SetOsErrorCode() +*/ +/* static */ int TVfs::Delete(sqlite3_vfs* aVfs, const char* aFileName, int /*aSyncDir*/) + { + SQLUTRACE_PROFILER(aVfs); + __OS_CALL(EOsVfsDelete, 0, 0); + __OSTIME_COUNTER(TheOsCallTicks[EOsVfsDelete], ::OsCallProfile(EFalse, EOsVfsDelete), 0, 0); + COsLayerData& osLayerData = COsLayerData::Instance(); + TBuf fname; + if(!::ConvertToUnicode(aFileName, fname)) + { + osLayerData.SetOsErrorCode(KErrBadName); + return SQLITE_ERROR; + } + SYMBIAN_TRACE_SQL_EVENTS_ONLY(UTF::Printf(UTF::TTraceContext(UTF::EInternals), KFileName, &fname)); + TFhStrType fhStrType = FhStringProps(aFileName); + if(fhStrType == EFhMainDbStr) + {//Deleting files not in your own private data cage - not allowed! + osLayerData.SetOsErrorCode(KErrPermissionDenied); + return SQLITE_ERROR; + } + if(fhStrType == EFhStr) + { + ::FhConvertToFileName(fname, osLayerData.iSysPrivDir);//If fname does not have a path, iSysPrivDir will be used + } + __FS_CALL(EFsOpFileDelete, 0); + TInt err = osLayerData.iFs.Delete(fname); + osLayerData.SetOsErrorCode(err); + return err == KErrNone ? SQLITE_OK : (err == KErrNoMemory ? SQLITE_IOERR_NOMEM : 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; + +If the function succeeds, COsLayerData::SetOsErrorCode() is called with KErrNone, otherwise COsLayerData::SetOsErrorCode() is called +with the reported by the OS API error. The stored error code will be used later by the SQLite API caller. + +@see COsLayerData::SetOsErrorCode() +*/ +/* static */ int TVfs::Access(sqlite3_vfs* aVfs, const char* aFileName, int aFlags, int* aResOut) + { + SQLUTRACE_PROFILER(aVfs); + __OS_CALL(EOsVfsAccess, 0, 0); + __OSTIME_COUNTER(TheOsCallTicks[EOsVfsAccess], ::OsCallProfile(EFalse, EOsVfsAccess), aFlags, 0); + COsLayerData& osLayerData = COsLayerData::Instance(); + TBuf fname; + if(!::ConvertToUnicode(aFileName, fname)) + { + osLayerData.SetOsErrorCode(KErrGeneral); + return SQLITE_IOERR_ACCESS; + } + SYMBIAN_TRACE_SQL_EVENTS_ONLY(UTF::Printf(UTF::TTraceContext(UTF::EInternals), KFileName, &fname)); + TFhStrType fhStrType = ::FhStringProps(aFileName); + if(fhStrType == EFhStr) + { + ::FhConvertToFileName(fname, osLayerData.iSysPrivDir);//If fname does not have a path, iSysPrivDir will be used + } + TEntry entry; + __FS_CALL(EFsOpFsEntry, 0); + TInt err = osLayerData.iFs.Entry(fname, entry); + if(aFlags == SQLITE_ACCESS_EXISTS && err == KErrNotFound) + { + osLayerData.SetOsErrorCode(KErrNone); + *aResOut = 0; + return SQLITE_OK; + } + if(err != KErrNone) + { + osLayerData.SetOsErrorCode(err); + 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; + } + osLayerData.SetOsErrorCode(KErrNone); + 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); + __OS_CALL(EOsVfsFullPathName, 0, 0); + __OSTIME_COUNTER(TheOsCallTicks[EOsVfsFullPathName], ::OsCallProfile(EFalse, EOsVfsFullPathName), aBufLen, 0); + COsLayerData& osLayerData = COsLayerData::Instance(); + osLayerData.StoreFhData(NULL, EFalse); + //Convert the received file name to UTF16 + TBuf fname; + if(!::ConvertToUnicode(aRelative, fname)) + { + return SQLITE_ERROR; + } + SYMBIAN_TRACE_SQL_EVENTS_ONLY(UTF::Printf(UTF::TTraceContext(UTF::EInternals), KFileName, &fname)); + //Zero-terminate the converted file name + fname.Append(TChar(0)); + TParse parse; + TFhStrType strType = ::FhStringProps(aRelative);//Detect string type - it may not be a real file name + if(strType == EFhMainDbStr) + {//The additonal information has to be extracted and fname reformatted, because SQLITE will + //use the returned full file name when making a decission to share the cache. + ::FhExtractAndStore(fname); + (void)parse.Set(fname, 0, 0);//the file name has to be verified by the file server + } + else + { + (void)parse.Set(fname, &osLayerData.iSysPrivDir, 0);//If fname does not have a path, iSysPrivDir will be used + } + TPtr8 dest8(reinterpret_cast (aBuf), aBufLen); + if(!::ConvertFromUnicode(parse.FullName(), dest8)) + {//Zero the stored fh data, because it has been initialized by the FhExtractAndStore(fname) call (couple of lines above) + osLayerData.StoreFhData(NULL, EFalse); + 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); + __OS_CALL(EOsVfsRandomness, 0, 0); + __OSTIME_COUNTER(TheOsCallTicks[EOsVfsRandomness], ::OsCallProfile(EFalse, EOsVfsRandomness), aBufLen, 0); + COsLayerData& osLayerData = COsLayerData::Instance(); + const TInt KRandIterations = aBufLen / sizeof(int); + for(TInt i=0;iAlloc(aSize); + } + +/** +SQLite OS porting layer API. + +Memory reallocation routine. + +@internalComponent +*/ +extern "C" void* sqlite3SymbianRealloc(void* aPtr, size_t aSize) + { +#ifdef _SQLPROFILER + TInt size = COsLayerData::Instance().iAllocator->AllocLen(aPtr); + __MEM_CALL(EMemOpRealloc, aSize, size); +#endif + return COsLayerData::Instance().iAllocator->ReAlloc(aPtr, aSize); + } + +/** +SQLite OS porting layer API. + +Memory free routine. + +@internalComponent +*/ +extern "C" void sqlite3SymbianFree(void* aPtr) + { +#ifdef _SQLPROFILER + TInt size = COsLayerData::Instance().iAllocator->AllocLen(aPtr); + __MEM_CALL(EMemOpFree, size, 0); +#endif + COsLayerData::Instance().iAllocator->Free(aPtr); + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// SQLite init/release functions /////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** +Registers the single sqlite3_vfs instance ("TheVfsApi" global variable) by calling sqlite3_vfs_register() +*/ +extern "C" int sqlite3_os_init(void) + { + return sqlite3_vfs_register(&TheVfsApi, 1);//"1" means - make TheVfsApi to be the default VFS object + } + +/** +Unregisters the single sqlite3_vfs instance ("TheVfsApi" global variable) by calling sqlite3_vfs_unregister() +*/ +extern "C" int sqlite3_os_end(void) + { + return sqlite3_vfs_unregister(&TheVfsApi); + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#endif//SQLITE_OS_SYMBIAN +