/*** 2008 February 09**** The author disclaims copyright to this source code. In place of** a legal notice, here is a blessing:**** May you do good and not evil.** May you find forgiveness for yourself and forgive others.** May you share freely, never taking more than you give.************************************************************************************ This file contains code that is specific to windows.*/#include "sqliteInt.h"#if OS_SYMBIAN /* This file is used for symbian only */#define MAX_PATH 260/*** A Note About Memory Allocation:**** This driver uses malloc()/free() directly rather than going through** the SQLite-wrappers sqlite3_malloc()/sqlite3_free(). Those wrappers** are designed for use on embedded systems where memory is scarce and** malloc failures happen frequently. Win32 does not typically run on** embedded systems, and when it does the developers normally have bigger** problems to worry about than running out of memory. So there is not** a compelling need to use the wrappers.**** But there is a good reason to not use the wrappers. If we use the** wrappers then we will get simulated malloc() failures within this** driver. And that causes all kinds of problems for our tests. We** could enhance SQLite to deal with simulated malloc failures within** the OS driver, but the code to deal with those failure would not** be exercised on Linux (which does not need to malloc() in the driver)** and so we would have difficulty writing coverage tests for that** code. Better to leave the code out, we think.**** The point of this discussion is as follows: When creating a new** OS layer for an embedded system, if you use this file as an example,** avoid the use of malloc()/free(). Those routines work ok on windows** desktops but not so well in embedded systems.*/#include <stdlib.h>#include <string.h>#include <time.h>#include <e32std.h>#include <f32file.h>#include <charconv.h>#include <bautils.h>#include <unistd.h>/*** Macros used to determine whether or not to use threads.*/#if defined(THREADSAFE) && THREADSAFE# define SQLITE_W32_THREADS 1#endif/*** Include code that is common to all os_*.c files*/#include "os_common.h"/*** The symbianFile structure is a subclass of sqlite3_file* specific to the win32** portability layer.*/typedef struct symbianFile symbianFile;struct symbianFile { int isOpen; unsigned char locktype; /* Type of lock currently held on this file */ short sharedLockByte; /* Randomly chosen byte used as a shared lock */ char fileName[512]; RFs session; RFile file;};/******************************************************************************* The next group of routines implement the I/O methods specified** by the sqlite3_io_methods object.******************************************************************************//*** Close a file.**** It is reported that an attempt to close a handle might sometimes** fail. This is a very unreasonable result, but windows is notorious** for being unreasonable so I do not doubt that it might happen. If** the close fails, we pause for 100 milliseconds and try again. As** many as MX_CLOSE_ATTEMPT attempts to close the handle are made before** giving up and returning an error.*/#define MX_CLOSE_ATTEMPT 3int winClose(sqlite3_file *id){ int rc, cnt = 0; symbianFile *pFile = (symbianFile*)id; pFile->file.Close(); pFile->session.Close(); return SQLITE_OK;}/*** Some microsoft compilers lack this definition.*/#ifndef INVALID_SET_FILE_POINTER# define INVALID_SET_FILE_POINTER ((DWORD)-1)#endif/*** Read data from a file into a buffer. Return SQLITE_OK if all** bytes were read successfully and SQLITE_IOERR if anything goes** wrong.*/int winRead( sqlite3_file *id, /* File to read from */ void *pBuf, /* Write content into this buffer */ int amt, /* Number of bytes to read */ sqlite3_int64 offset /* Begin reading at this offset */){ int rc; size_t got; symbianFile *pFile = (symbianFile*)id; assert( id!=0 ); SimulateIOError(return SQLITE_IOERR_READ); TInt tOffset = (TInt)offset; rc = pFile->file.Seek(ESeekStart, tOffset); if( rc!= KErrNone){ return SQLITE_FULL; } HBufC8* buf = HBufC8::NewL(amt) ; TPtr8 ptr = buf->Des(); if (pFile->file.Read(ptr, amt) != KErrNone) { delete buf; return SQLITE_IOERR_READ; } got = buf->Length(); if( got == 0 ){ delete buf; TInt size = 0; if (pFile->file.Size(size) != KErrNone) { return SQLITE_IOERR_READ; } if (size == 0) { return SQLITE_IOERR_SHORT_READ; } return SQLITE_IOERR_READ; } memcpy(pBuf, ptr.Ptr(), got); delete buf; if( got == amt ){ return SQLITE_OK; }else{ memset(&((char*)pBuf)[got], 0, amt-got); return SQLITE_IOERR_SHORT_READ; }}/*** Write data from a buffer into a file. Return SQLITE_OK on success** or some other error code on failure.*/int winWrite( sqlite3_file *id, /* File to write into */ const void *pBuf, /* The bytes to be written */ int amt, /* Number of bytes to write */ sqlite3_int64 offset /* Offset into the file to begin writing at */){ int rc; symbianFile *pFile = (symbianFile*)id; assert( id!=0 ); SimulateIOError(return SQLITE_IOERR_WRITE); SimulateDiskfullError(return SQLITE_FULL); TInt tOffset = (TInt)offset; rc = pFile->file.Seek(ESeekStart, tOffset); if( rc!= KErrNone){ return SQLITE_FULL; } assert( amt>0 ); rc = SQLITE_OK; TPtrC8 ptr((TUint8 *)pBuf,amt); if (pFile->file.Write(ptr, amt) != KErrNone) rc = SQLITE_FULL; return rc;}/*** Truncate an open file to a specified size*/int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){ symbianFile *pFile = (symbianFile*)id; if (pFile->file.SetSize(nByte) != KErrNone) { return SQLITE_IOERR; } return SQLITE_OK;}#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.*/int sqlite3_sync_count = 0;int sqlite3_fullsync_count = 0;#endif/*** Make sure all writes to a particular file are committed to disk.*/int winSync(sqlite3_file *id, int flags){ symbianFile *pFile = (symbianFile*)id; OSTRACE3("SYNC %d lock=%d\n", pFile->h, pFile->locktype);#ifdef SQLITE_TEST if( flags & SQLITE_SYNC_FULL ){ sqlite3_fullsync_count++; } sqlite3_sync_count++;#endif TInt error = pFile->file.Flush(); if (error != KErrNone) { return SQLITE_IOERR; }else{ return SQLITE_OK; }}/*** Determine the current size of a file in bytes*/int winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){ symbianFile *pFile = (symbianFile*)id; TInt size = 0; if (pFile->file.Size(size) != KErrNone) { return SQLITE_IOERR; } *pSize = size; return SQLITE_OK;}/*** Lock the file with the lock specified by parameter locktype - one** of the following:**** (1) SHARED_LOCK** (2) RESERVED_LOCK** (3) PENDING_LOCK** (4) EXCLUSIVE_LOCK**** Sometimes when requesting one lock state, additional lock states** are inserted in between. The locking might fail on one of the later** transitions leaving the lock state different from what it started but** still short of its goal. The following chart shows the allowed** transitions and the inserted intermediate states:**** UNLOCKED -> SHARED** SHARED -> RESERVED** SHARED -> (PENDING) -> EXCLUSIVE** RESERVED -> (PENDING) -> EXCLUSIVE** PENDING -> EXCLUSIVE**** This routine will only increase a lock. The winUnlock() routine** erases all locks at once and returns us immediately to locking level 0.** It is not possible to lower the locking level one step at a time. You** must go straight to locking level 0.*/int winLock(sqlite3_file *id, int locktype){ int rc = SQLITE_OK; /* Return code from subroutines */ int res = 1; /* Result of a windows lock call */ int newLocktype; /* Set pFile->locktype to this value before exiting */ int gotPendingLock = 0;/* True if we acquired a PENDING lock this time */ symbianFile *pFile = (symbianFile*)id; assert( pFile!=0 ); OSTRACE5("LOCK %d %d was %d(%d)\n", pFile->h, locktype, pFile->locktype, pFile->sharedLockByte); // one smartphone only one application can control the database TInt size = 0; if (pFile->file.Size(size) == KErrNone) return SQLITE_OK; return SQLITE_BUSY;}/*** This routine checks if there is a RESERVED lock held on the specified** file by this or any other process. If such a lock is held, return** non-zero, otherwise zero.*/int winCheckReservedLock(sqlite3_file *id){ int rc; symbianFile *pFile = (symbianFile*)id; assert( pFile!=0 ); if( pFile->locktype>=RESERVED_LOCK ){ rc = 1; OSTRACE3("TEST WR-LOCK %d %d (local)\n", pFile->h, rc); }else{ TInt size = 0; if (pFile->file.Size(size) == KErrNone) rc = 1; OSTRACE3("TEST WR-LOCK %d %d (remote)\n", pFile->h, rc); } return rc;}/*** Lower the locking level on file descriptor id to locktype. locktype** must be either NO_LOCK or SHARED_LOCK.**** 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 NO_LOCK. If the second argument is SHARED_LOCK then this routine** might return SQLITE_IOERR;*/int winUnlock(sqlite3_file *id, int locktype){ int type; symbianFile *pFile = (symbianFile*)id; int rc = SQLITE_OK; assert( pFile!=0 ); return rc;}/*** Control and query of the open file handle.*/int winFileControl(sqlite3_file *id, int op, void *pArg){ switch( op ){ case SQLITE_FCNTL_LOCKSTATE: { *(int*)pArg = ((symbianFile*)id)->locktype; return SQLITE_OK; } } return SQLITE_ERROR;}/*** Return the sector size in bytes of the underlying block device for** the specified file. This is almost always 512 bytes, but may be** larger for some devices.**** SQLite code assumes this function cannot fail. It also assumes that** if two files are created in the same file-system directory (i.e.** a database and its journal file) that the sector size will be the** same for both.*/int winSectorSize(sqlite3_file *id){ return SQLITE_DEFAULT_SECTOR_SIZE;}/*** Return a vector of device characteristics.*/int winDeviceCharacteristics(sqlite3_file *id){ return 0;}/***************************************************************************** Here ends the I/O methods that form the sqlite3_io_methods object.**** The next block of code implements the VFS methods.****************************************************************************/void ConvertToUnicode(RFs session, TDes16& aUnicode, const char *str){ CCnvCharacterSetConverter *converter = CCnvCharacterSetConverter::NewL(); converter->PrepareToConvertToOrFromL(KCharacterSetIdentifierUtf8, session); TPtrC8 ptr((const unsigned char*)str); int state = CCnvCharacterSetConverter::KStateDefault; converter->ConvertToUnicode(aUnicode, ptr, state); delete converter;}/*** Open a file.*/int winOpen( sqlite3_vfs *pVfs, /* Not used */ const char *zName, /* Name of the file (UTF-8) */ sqlite3_file *id, /* Write the SQLite file handle here */ int flags, /* Open mode flags */ int *pOutFlags /* Status return flags */){ symbianFile *pFile = (symbianFile*)id; TBuf16<MAX_PATH> filename; pFile->isOpen = 0; memset(pFile, 0, sizeof(*pFile)); strcpy(pFile->fileName, zName); pFile->session.Connect(); ConvertToUnicode(pFile->session, filename, zName); int ret = 0; if( flags & SQLITE_OPEN_CREATE ){ if (BaflUtils::FileExists(pFile->session, filename) == 1) { ret = pFile->file.Open(pFile->session, filename, EFileStream | EFileWrite); } else { ret = pFile->file.Create(pFile->session, filename, EFileStream | EFileWrite); } } else if( flags & SQLITE_OPEN_READWRITE ){ ret = pFile->file.Open(pFile->session, filename, EFileStream | EFileWrite); }else{ ret = pFile->file.Open(pFile->session, filename, EFileStream | EFileRead); } OpenCounter(+1); if (ret != KErrNone) { return SQLITE_IOERR; } pFile->isOpen = 1; return SQLITE_OK;}/*** Delete the named file.**** Note that windows does not allow a file to be deleted if some other** process has it open. Sometimes a virus scanner or indexing program** will open a journal file shortly after it is created in order to do** whatever does. While this other process is holding the** file open, we will be unable to delete it. To work around this** problem, we delay 100 milliseconds and try to delete again. Up** to MX_DELETION_ATTEMPTs deletion attempts are run before giving** up and returning an error.*/#define MX_DELETION_ATTEMPTS 5int winDelete( sqlite3_vfs *pVfs, /* Not used on win32 */ const char *zFilename, /* Name of file to delete */ int syncDir /* Not used on win32 */){ SimulateIOError(return SQLITE_IOERR_DELETE); TBuf16<MAX_PATH> filename; RFs session; session.Connect(); ConvertToUnicode(session, filename, zFilename); BaflUtils::DeleteFile(session, filename); OSTRACE2("DELETE \"%s\"\n", zFilename); session.Close(); return SQLITE_OK;}/*** Check the existance and status of a file.*/int winAccess( sqlite3_vfs *pVfs, /* Not used on win32 */ const char *zFilename, /* Name of file to check */ int flags /* Type of test to make on this file */){ TBuf16<MAX_PATH> filename; RFs session; session.Connect(); ConvertToUnicode(session, filename, zFilename); int ret = BaflUtils::FileExists(session, filename); session.Close(); return ret;}/*** Create a temporary file name in zBuf. zBuf must be big enough to** hold at pVfs->mxPathname characters.*/int winGetTempname(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ static char zChars[] = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789"; int i, j; char zTempPath[MAX_PATH+1]; if( sqlite3_temp_directory ){ sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", sqlite3_temp_directory); } else { } for(i=strlen(zTempPath); i>0 && zTempPath[i-1]=='\\'; i--){} zTempPath[i] = 0; sqlite3_snprintf(nBuf-30, zBuf, "%s\\"SQLITE_TEMP_FILE_PREFIX, zTempPath); j = strlen(zBuf); sqlite3Randomness(20, &zBuf[j]); for(i=0; i<20; i++, j++){ zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; } zBuf[j] = 0; OSTRACE2("TEMP FILENAME: %s\n", zBuf); return SQLITE_OK; }/*** Turn a relative pathname into a full pathname. Write the full** pathname into zOut[]. zOut[] will be at least pVfs->mxPathname** bytes in size.*/int winFullPathname( sqlite3_vfs *pVfs, /* Pointer to vfs object */ const char *zRelative, /* Possibly relative input path */ int nFull, /* Size of output buffer in bytes */ char *zFull /* Output buffer */){ /* WinCE has no concept of a relative pathname, or so I am told. */ sqlite3_snprintf(pVfs->mxPathname, zFull, "%s", zRelative); return SQLITE_OK;} #define winDlOpen 0 #define winDlError 0 #define winDlSym 0 #define winDlClose 0/*** Write up to nBuf bytes of randomness into zBuf.*/int winRandomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ int i; for (i=0; i<nBuf; ++i) { zBuf[i] = rand() % 255; } return nBuf;}/*** Sleep for a little while. Return the amount of time slept.*/int winSleep(sqlite3_vfs *pVfs, int microsec){ return sleep(microsec / 1000);}/*** The following variable, if set to a non-zero value, becomes the result** returned from sqlite3OsCurrentTime(). This is used for testing.*/#ifdef SQLITE_TESTint sqlite3_current_time = 0;#endif/*** Find the current time (in Universal Coordinated Time). Write the** current time and date as a Julian Day number into *prNow and** return 0. Return 1 if the time and date cannot be found.*/int winCurrentTime(sqlite3_vfs *pVfs, double *prNow){ double now; now = time(NULL); *prNow = now; return 0;}/*** Return a pointer to the sqlite3DefaultVfs structure. We use** a function rather than give the structure global scope because** some compilers (MSVC) do not allow forward declarations of** initialized structures.*/sqlite3_vfs *sqlite3OsDefaultVfs(void){ static sqlite3_vfs winVfs = { 1, /* iVersion */ -1, /* szOsFile */ MAX_PATH, /* mxPathname */ 0, /* pNext */ "symbian", /* zName */ 0, /* pAppData */ }; winVfs.szOsFile = sizeof(symbianFile); return &winVfs;}#endif /* OS_SYMBIAN */