From: drh Date: Fri, 14 May 2010 16:34:34 +0000 (+0000) Subject: Pull in all the latest changes from the trunk. X-Git-Tag: version-3.7.2~358^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=refs%2Fheads%2Fwal-win32;p=thirdparty%2Fsqlite.git Pull in all the latest changes from the trunk. Update the win32 SHM methods to work with the new interface design. FossilOrigin-Name: 4b69f2cd315b6b66d10e5190d235114788853258 --- 83235214406580a32e1051d0af68c9b01034858e diff --cc manifest index 2bdebe33df,22d9593528..bdd18c520a --- a/manifest +++ b/manifest @@@ -1,5 -1,8 +1,8 @@@ - C Updates\sto\sWAL\ssupport\sin\sos_win.c:\spulled\sin\slatest\schanges\sfrom\ssrc/os_unix.c;\supdated\stracing;\smisc.\simprovements. - D 2010-05-12T17:14:59 + -----BEGIN PGP SIGNED MESSAGE----- + Hash: SHA1 + -C Simplifications\sto\sthe\sSHM\simplementation\sin\sos_unix.c,\staking\sadvantage\nof\sthe\sremoval\sof\sthe\sLinuxThreads\smess. -D 2010-05-14T14:52:25 ++C Pull\sin\sall\sthe\slatest\schanges\sfrom\sthe\strunk.\nUpdate\sthe\swin32\sSHM\smethods\sto\swork\swith\sthe\snew\sinterface\sdesign. ++D 2010-05-14T16:34:35 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0 F Makefile.in a5cad1f8f3e021356bfcc6c77dc16f6f1952bbc3 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 @@@ -145,17 -148,17 +148,17 @@@ F src/mutex.c e2358d8f9a9021ab0bba4d511 F src/mutex.h 6fde601e55fa6c3fae768783c439797ab84c87c6 F src/mutex_noop.c 10ae943d26ba86ea92319f72adbb501a89c563e0 F src/mutex_os2.c 20477db50cf3817c2f1cd3eb61e5c177e50231db - F src/mutex_unix.c 04a25238abce7e3d06b358dcf706e26624270809 - F src/mutex_w32.c 4cc201c1bfd11d1562810554ff5500e735559d7e + F src/mutex_unix.c becb8c4e07616abf84650d3687d62a1461d5d9cd + F src/mutex_w32.c fb1cf87c5a88b56c7df0d9ddb796ed9641046c3d F src/notify.c cbfa66a836da3a51567209636e6a94059c137930 - F src/os.c aec6922553585a25d5655666defc125a7e217674 - F src/os.h b389844e5469a2918e8a45fe6ae52b4c28dfb2b2 - F src/os_common.h 0d6ee583b6ee3185eb9d951f890c6dd03021a08d - F src/os_os2.c 8ad77a418630d7dee91d1bb04f79c2096301d3a0 - F src/os_unix.c 34fe71c67fce72360411d60fe069c7f0dc612dd0 - F src/os_win.c 858e38344cab4b8c82be3263dd997c8f0f07ee0c - F src/pager.c a47af9c2c9ca425bd68642d61764266331a3323f - F src/pager.h 934b598583a9d936bb13c37d62a2fe68ac48781c + F src/os.c c0a5dfce2a214dacb679425632d04f8a2021f364 + F src/os.h 8a7e2456237ecf3a2e55b02f9fe6091f1ad36902 + F src/os_common.h a8f95b81eca8a1ab8593d23e94f8a35f35d4078f + F src/os_os2.c 665876d5eec7585226b0a1cf5e18098de2b2da19 + F src/os_unix.c 025da44ba18d91c2d0205e667bae4dd47be13639 -F src/os_win.c 70c4a3327716213b59adf3a8adf2d5318b044a19 ++F src/os_win.c e0a74dd60e5114ebafdd8372b0d037cb0e960176 + F src/pager.c 1e163a82ae8405433dca559831caa06aafbba3b0 + F src/pager.h 76466c3a5af56943537f68b1f16567101a0cd1d0 F src/parse.y ace5c7a125d9f2a410e431ee3209034105045f7e F src/pcache.c ace8f6a5ecd4711cc66a1b23053be7109bd437cf F src/pcache.h c683390d50f856d4cd8e24342ae62027d1bb6050 @@@ -813,7 -816,14 +816,14 @@@ F tool/speedtest2.tcl ee2149167303ba8e9 F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f - P 111ad59f21d53d1ec63c084ca5f98f2aaf7cd070 - R dbc78c75ab3394a3992dedd59d6e1ec0 - U shaneh - Z 1406477810564323a0dea454027b6d3a -P e294b696ba91512b1ca5547774c51ea07b4cb5bc -R 21f94067ad533c60202276014029d639 ++P 76504726a1ef7f6a0445ec800776462138b22d72 d1debe5def82a6bc72f11b8787176ac60259630f ++R a9795407fa84d51fbed2fe19974b207a + U drh -Z 433a8524a713de9ce43e3d2748510f46 ++Z d79712dbdc8aee8b22b73408f9d8d19b + -----BEGIN PGP SIGNATURE----- + Version: GnuPG v1.4.6 (GNU/Linux) + -iD8DBQFL7WOsoxKgR168RlERAjJgAJ9zjWsl8oBYzeTvVFGM58MGDrdXDwCfV8pN -CnLKIoKp0Sl7DOCRB0tGSS8= -=V8Bz ++iD8DBQFL7XueoxKgR168RlERAmQDAJ9A6pB3LSLevi49j5UgkI8P8vCmtQCaAs3i ++afk+KXni4+86rCQ2tTbJWk8= ++=dADp + -----END PGP SIGNATURE----- diff --cc manifest.uuid index 0bf12bb074,f3ccfbe4cd..eff35a07a1 --- a/manifest.uuid +++ b/manifest.uuid @@@ -1,1 -1,1 +1,1 @@@ - 76504726a1ef7f6a0445ec800776462138b22d72 -d1debe5def82a6bc72f11b8787176ac60259630f ++4b69f2cd315b6b66d10e5190d235114788853258 diff --cc src/os_win.c index 7783115288,664d15ccbc..29898eb78b --- a/src/os_win.c +++ b/src/os_win.c @@@ -76,6 -76,6 +76,10 @@@ # define FormatMessageW(a,b,c,d,e,f,g) 0 #endif ++/* Forward references */ ++typedef struct winShm winShm; /* A connection to shared-memory */ ++typedef struct winShmNode winShmNode; /* A region of shared-memory */ ++ /* ** WinCE lacks native support for file locking so we have to fake it ** with some code of our own. @@@ -95,12 -95,12 +99,15 @@@ typedef struct winceLock */ typedef struct winFile winFile; struct winFile { -- const sqlite3_io_methods *pMethod;/* Must be first */ ++ const sqlite3_io_methods *pMethod; /*** Must be first ***/ ++ sqlite3_vfs *pVfs; /* The VFS used to open this file */ HANDLE h; /* Handle for accessing the file */ unsigned char locktype; /* Type of lock currently held on this file */ short sharedLockByte; /* Randomly chosen byte used as a shared lock */ DWORD lastErrno; /* The Windows errno from the last I/O error */ DWORD sectorSize; /* Sector size of the device file is on */ ++ winShm *pShm; /* Instance of shared memory on this file */ ++ const char *zPath; /* Full pathname of this file */ #if SQLITE_OS_WINCE WCHAR *zDeleteOnClose; /* Name of file to delete when closing */ HANDLE hMutex; /* Mutex used to control access to shared lock */ @@@ -629,6 -629,7 +636,8 @@@ static int winClose(sqlite3_file *id) winFile *pFile = (winFile*)id; assert( id!=0 ); ++ assert( pFile->pShm==0 ); + OSTRACE(("CLOSE %d\n", pFile->h)); do{ rc = CloseHandle(pFile->h); }while( rc==0 && ++cnt < MX_CLOSE_ATTEMPT && (Sleep(100), 1) ); @@@ -746,24 -746,24 +755,25 @@@ static int winWrite static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){ LONG upperBits = (LONG)((nByte>>32) & 0x7fffffff); LONG lowerBits = (LONG)(nByte & 0xffffffff); - DWORD rc; + DWORD dwRet; winFile *pFile = (winFile*)id; DWORD error; + int rc = SQLITE_OK; assert( id!=0 ); + OSTRACE(("TRUNCATE %d %lld\n", pFile->h, nByte)); SimulateIOError(return SQLITE_IOERR_TRUNCATE); - rc = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN); - if( rc==INVALID_SET_FILE_POINTER && (error=GetLastError())!=NO_ERROR ){ + dwRet = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN); + if( dwRet==INVALID_SET_FILE_POINTER && (error=GetLastError())!=NO_ERROR ){ pFile->lastErrno = error; - return SQLITE_IOERR_TRUNCATE; - } + rc = SQLITE_IOERR_TRUNCATE; /* SetEndOfFile will fail if nByte is negative */ - if( !SetEndOfFile(pFile->h) ){ + }else if( !SetEndOfFile(pFile->h) ){ pFile->lastErrno = GetLastError(); - return SQLITE_IOERR_TRUNCATE; + rc = SQLITE_IOERR_TRUNCATE; } - return SQLITE_OK; + OSTRACE(("TRUNCATE %d %lld %s\n", pFile->h, nByte, rc==SQLITE_OK ? "ok" : "failed")); + return rc; } #ifdef SQLITE_TEST @@@ -1139,1170 -1139,372 +1149,1325 @@@ static int winDeviceCharacteristics(sql return 0; } - /* - ** This vector defines all the methods that can operate on an - ** sqlite3_file for win32. ++/**************************************************************************** ++********************************* Shared Memory ***************************** ++** ++** The next subdivision of code manages the shared-memory primitives. +*/ - static const sqlite3_io_methods winIoMethod = { - 1, /* iVersion */ - winClose, - winRead, - winWrite, - winTruncate, - winSync, - winFileSize, - winLock, - winUnlock, - winCheckReservedLock, - winFileControl, - winSectorSize, - winDeviceCharacteristics - }; ++#ifndef SQLITE_OMIT_WAL + - /*************************************************************************** - ** Here ends the I/O methods that form the sqlite3_io_methods object. ++/* ++** Helper functions to obtain and relinquish the global mutex. The ++** global mutex is used to protect the winLockInfo objects used by ++** this file, all of which may be shared by multiple threads. +** - ** The next block of code implements the VFS methods. - ****************************************************************************/ ++** Function winShmMutexHeld() is used to assert() that the global mutex ++** is held when required. This function is only used as part of assert() ++** statements. e.g. ++** ++** winShmEnterMutex() ++** assert( winShmMutexHeld() ); ++** winEnterLeave() ++*/ ++static void winShmEnterMutex(void){ ++ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); ++} ++static void winShmLeaveMutex(void){ ++ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); ++} ++#ifdef SQLITE_DEBUG ++static int winShmMutexHeld(void) { ++ return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); ++} ++#endif + +/* - ** Convert a UTF-8 filename into whatever form the underlying - ** operating system wants filenames in. Space to hold the result - ** is obtained from malloc and must be freed by the calling - ** function. - */ - static void *convertUtf8Filename(const char *zFilename){ - void *zConverted = 0; - if( isNT() ){ - zConverted = utf8ToUnicode(zFilename); - /* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed. ++** Object used to represent a single file opened and mmapped to provide ++** shared memory. When multiple threads all reference the same ++** log-summary, each thread has its own winFile object, but they all ++** point to a single instance of this object. In other words, each ++** log-summary is opened only once per process. ++** ++** winShmMutexHeld() must be true when creating or destroying ++** this object or while reading or writing the following fields: ++** ++** nRef ++** pNext ++** ++** The following fields are read-only after the object is created: ++** ++** fid ++** zFilename ++** ++** Either winShmNode.mutex must be held or winShmNode.nRef==0 and ++** winShmMutexHeld() is true when reading or writing any other field ++** in this structure. ++** ++** To avoid deadlocks, mutex and mutexBuf are always released in the ++** reverse order that they are acquired. mutexBuf is always acquired ++** first and released last. This invariant is check by asserting ++** sqlite3_mutex_notheld() on mutex whenever mutexBuf is acquired or ++** released. +*/ - #if SQLITE_OS_WINCE==0 - }else{ - zConverted = utf8ToMbcs(zFilename); ++struct winShmNode { ++ sqlite3_mutex *mutex; /* Mutex to access this object */ ++ sqlite3_mutex *mutexBuf; /* Mutex to access zBuf[] */ ++ char *zFilename; /* Name of the file */ ++ winFile hFile; /* File handle from winOpen */ ++ HANDLE hMap; /* File handle from CreateFileMapping */ ++ DWORD lastErrno; /* The Windows errno from the last I/O error */ ++ int szMap; /* Size of the mapping of file into memory */ ++ char *pMMapBuf; /* Where currently mmapped(). NULL if unmapped */ ++ int nRef; /* Number of winShm objects pointing to this */ ++ winShm *pFirst; /* All winShm objects pointing to this */ ++ winShmNode *pNext; /* Next in list of all winShmNode objects */ ++#ifdef SQLITE_DEBUG ++ u8 exclMask; /* Mask of exclusive locks held */ ++ u8 sharedMask; /* Mask of shared locks held */ ++ u8 nextShmId; /* Next available winShm.id value */ +#endif - } - /* caller will handle out of memory */ - return zConverted; - } ++}; + +/* - ** Create a temporary file name in zBuf. zBuf must be big enough to - ** hold at pVfs->mxPathname characters. ++** A global array of all winShmNode objects. ++** ++** The winShmMutexHeld() must be true while reading or writing this list. +*/ - static int getTempname(int nBuf, char *zBuf){ - static char zChars[] = - "abcdefghijklmnopqrstuvwxyz" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "0123456789"; - size_t i, j; - char zTempPath[MAX_PATH+1]; - if( sqlite3_temp_directory ){ - sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", sqlite3_temp_directory); - }else if( isNT() ){ - char *zMulti; - WCHAR zWidePath[MAX_PATH]; - GetTempPathW(MAX_PATH-30, zWidePath); - zMulti = unicodeToUtf8(zWidePath); - if( zMulti ){ - sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", zMulti); - free(zMulti); - }else{ - return SQLITE_NOMEM; - } - /* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed. - ** Since the ASCII version of these Windows API do not exist for WINCE, - ** it's important to not reference them for WINCE builds. ++static winShmNode *winShmNodeList = 0; ++ ++/* ++** Structure used internally by this VFS to record the state of an ++** open shared memory connection. ++** ++** winShm.pFile->mutex must be held while reading or writing the ++** winShm.pNext and winShm.locks[] elements. ++** ++** The winShm.pFile element is initialized when the object is created ++** and is read-only thereafter. +*/ - #if SQLITE_OS_WINCE==0 - }else{ - char *zUtf8; - char zMbcsPath[MAX_PATH]; - GetTempPathA(MAX_PATH-30, zMbcsPath); - zUtf8 = sqlite3_win32_mbcs_to_utf8(zMbcsPath); - if( zUtf8 ){ - sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", zUtf8); - free(zUtf8); - }else{ - return SQLITE_NOMEM; - } ++struct winShm { ++ winShmNode *pShmNode; /* The underlying winShmNode object */ ++ winShm *pNext; /* Next winShm with the same winShmNode */ ++ u8 lockState; /* Current lock state */ ++ u8 hasMutex; /* True if holding the winShmNode mutex */ ++ u8 hasMutexBuf; /* True if holding pFile->mutexBuf */ ++ u8 sharedMask; /* Mask of shared locks held */ ++ u8 exclMask; /* Mask of exclusive locks held */ ++#ifdef SQLITE_DEBUG ++ u8 id; /* Id of this connection with its winShmNode */ +#endif - } - for(i=sqlite3Strlen30(zTempPath); i>0 && zTempPath[i-1]=='\\'; i--){} - zTempPath[i] = 0; - sqlite3_snprintf(nBuf-30, zBuf, - "%s\\"SQLITE_TEMP_FILE_PREFIX, zTempPath); - j = sqlite3Strlen30(zBuf); - sqlite3_randomness(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; - } ++}; + +/* - ** The return value of getLastErrorMsg - ** is zero if the error message fits in the buffer, or non-zero - ** otherwise (if the message was truncated). ++** Size increment by which shared memory grows +*/ - static int getLastErrorMsg(int nBuf, char *zBuf){ - /* FormatMessage returns 0 on failure. Otherwise it - ** returns the number of TCHARs written to the output - ** buffer, excluding the terminating null char. - */ - DWORD error = GetLastError(); - DWORD dwLen = 0; - char *zOut = 0; ++#define SQLITE_WIN_SHM_INCR 4096 + - if( isNT() ){ - WCHAR *zTempWide = NULL; - dwLen = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - error, - 0, - (LPWSTR) &zTempWide, - 0, - 0); - if( dwLen > 0 ){ - /* allocate a buffer and convert to UTF8 */ - zOut = unicodeToUtf8(zTempWide); - /* free the system buffer allocated by FormatMessage */ - LocalFree(zTempWide); - } - /* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed. - ** Since the ASCII version of these Windows API do not exist for WINCE, - ** it's important to not reference them for WINCE builds. ++/* ++** Constants used for locking +*/ - #if SQLITE_OS_WINCE==0 - }else{ - char *zTemp = NULL; - dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - error, - 0, - (LPSTR) &zTemp, - 0, - 0); - if( dwLen > 0 ){ - /* allocate a buffer and convert to UTF8 */ - zOut = sqlite3_win32_mbcs_to_utf8(zTemp); - /* free the system buffer allocated by FormatMessage */ - LocalFree(zTemp); - } - #endif - } - if( 0 == dwLen ){ - sqlite3_snprintf(nBuf, zBuf, "OsError 0x%x (%u)", error, error); - }else{ - /* copy a maximum of nBuf chars to output buffer */ - sqlite3_snprintf(nBuf, zBuf, "%s", zOut); - /* free the UTF8 buffer */ - free(zOut); - } - return 0; - } ++#define WIN_SHM_BASE 32 /* Byte offset of the first lock byte */ ++#define WIN_SHM_DMS 0x01 /* Mask for Dead-Man-Switch lock */ ++#define WIN_SHM_A 0x10 /* Mask for region locks... */ ++#define WIN_SHM_B 0x20 ++#define WIN_SHM_C 0x40 ++#define WIN_SHM_D 0x80 + ++#ifdef SQLITE_DEBUG +/* - ** Open a file. ++** Return a pointer to a nul-terminated string in static memory that ++** describes a locking mask. The string is of the form "MSABCD" with ++** each character representing a lock. "M" for MUTEX, "S" for DMS, ++** and "A" through "D" for the region locks. If a lock is held, the ++** letter is shown. If the lock is not held, the letter is converted ++** to ".". ++** ++** This routine is for debugging purposes only and does not appear ++** in a production build. +*/ - static 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 */ - ){ - HANDLE h; - DWORD dwDesiredAccess; - DWORD dwShareMode; - DWORD dwCreationDisposition; - DWORD dwFlagsAndAttributes = 0; - #if SQLITE_OS_WINCE - int isTemp = 0; - #endif - winFile *pFile = (winFile*)id; - void *zConverted; /* Filename in OS encoding */ - const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */ - char zTmpname[MAX_PATH+1]; /* Buffer used to create temp filename */ - - assert( id!=0 ); - UNUSED_PARAMETER(pVfs); - - pFile->h = INVALID_HANDLE_VALUE; - - /* If the second argument to this function is NULL, generate a - ** temporary file name to use - */ - if( !zUtf8Name ){ - int rc = getTempname(MAX_PATH+1, zTmpname); - if( rc!=SQLITE_OK ){ - return rc; - } - zUtf8Name = zTmpname; - } ++static const char *winShmLockString(u8 mask){ ++ static char zBuf[48]; ++ static int iBuf = 0; ++ char *z; + - /* Convert the filename to the system encoding. */ - zConverted = convertUtf8Filename(zUtf8Name); - if( zConverted==0 ){ - return SQLITE_NOMEM; - } ++ z = &zBuf[iBuf]; ++ iBuf += 8; ++ if( iBuf>=sizeof(zBuf) ) iBuf = 0; + - if( flags & SQLITE_OPEN_READWRITE ){ - dwDesiredAccess = GENERIC_READ | GENERIC_WRITE; - }else{ - dwDesiredAccess = GENERIC_READ; - } - /* SQLITE_OPEN_EXCLUSIVE is used to make sure that a new file is - ** created. SQLite doesn't use it to indicate "exclusive access" - ** as it is usually understood. - */ - assert(!(flags & SQLITE_OPEN_EXCLUSIVE) || (flags & SQLITE_OPEN_CREATE)); - if( flags & SQLITE_OPEN_EXCLUSIVE ){ - /* Creates a new file, only if it does not already exist. */ - /* If the file exists, it fails. */ - dwCreationDisposition = CREATE_NEW; - }else if( flags & SQLITE_OPEN_CREATE ){ - /* Open existing file, or create if it doesn't exist */ - dwCreationDisposition = OPEN_ALWAYS; - }else{ - /* Opens a file, only if it exists. */ - dwCreationDisposition = OPEN_EXISTING; - } - dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; - if( flags & SQLITE_OPEN_DELETEONCLOSE ){ - #if SQLITE_OS_WINCE - dwFlagsAndAttributes = FILE_ATTRIBUTE_HIDDEN; - isTemp = 1; - #else - dwFlagsAndAttributes = FILE_ATTRIBUTE_TEMPORARY - | FILE_ATTRIBUTE_HIDDEN - | FILE_FLAG_DELETE_ON_CLOSE; - #endif - }else{ - dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL; - } - /* Reports from the internet are that performance is always - ** better if FILE_FLAG_RANDOM_ACCESS is used. Ticket #2699. */ - #if SQLITE_OS_WINCE - dwFlagsAndAttributes |= FILE_FLAG_RANDOM_ACCESS; - #endif - if( isNT() ){ - h = CreateFileW((WCHAR*)zConverted, - dwDesiredAccess, - dwShareMode, - NULL, - dwCreationDisposition, - dwFlagsAndAttributes, - NULL - ); - /* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed. - ** Since the ASCII version of these Windows API do not exist for WINCE, - ** it's important to not reference them for WINCE builds. - */ - #if SQLITE_OS_WINCE==0 - }else{ - h = CreateFileA((char*)zConverted, - dwDesiredAccess, - dwShareMode, - NULL, - dwCreationDisposition, - dwFlagsAndAttributes, - NULL - ); - #endif - } - OSTRACE(("OPEN %d %s 0x%lx %s\n", - h, zName, dwDesiredAccess, - h==INVALID_HANDLE_VALUE ? "failed" : "ok")); - if( h==INVALID_HANDLE_VALUE ){ - free(zConverted); - if( flags & SQLITE_OPEN_READWRITE ){ - return winOpen(pVfs, zName, id, - ((flags|SQLITE_OPEN_READONLY)&~SQLITE_OPEN_READWRITE), pOutFlags); - }else{ - return SQLITE_CANTOPEN_BKPT; - } - } - if( pOutFlags ){ - if( flags & SQLITE_OPEN_READWRITE ){ - *pOutFlags = SQLITE_OPEN_READWRITE; - }else{ - *pOutFlags = SQLITE_OPEN_READONLY; - } - } - memset(pFile, 0, sizeof(*pFile)); - pFile->pMethod = &winIoMethod; - pFile->h = h; - pFile->lastErrno = NO_ERROR; - pFile->sectorSize = getSectorSize(pVfs, zUtf8Name); - #if SQLITE_OS_WINCE - if( (flags & (SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_DB)) == - (SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_DB) - && !winceCreateLock(zName, pFile) - ){ - CloseHandle(h); - free(zConverted); - return SQLITE_CANTOPEN_BKPT; - } - if( isTemp ){ - pFile->zDeleteOnClose = zConverted; - }else - #endif - { - free(zConverted); - } - OpenCounter(+1); - return SQLITE_OK; ++ z[0] = (mask & WIN_SHM_DMS) ? 'S' : '.'; ++ z[1] = (mask & WIN_SHM_A) ? 'A' : '.'; ++ z[2] = (mask & WIN_SHM_B) ? 'B' : '.'; ++ z[3] = (mask & WIN_SHM_C) ? 'C' : '.'; ++ z[4] = (mask & WIN_SHM_D) ? 'D' : '.'; ++ z[5] = 0; ++ return z; +} ++#endif /* SQLITE_DEBUG */ + +/* - ** Delete the named file. ++** Apply posix advisory locks for all bytes identified in lockMask. +** - ** 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 it 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. ++** lockMask might contain multiple bits but all bits are guaranteed ++** to be contiguous. ++** ++** Locks block if the mask is exactly WIN_SHM_C and are non-blocking ++** otherwise. +*/ - #define MX_DELETION_ATTEMPTS 5 - static int winDelete( - sqlite3_vfs *pVfs, /* Not used on win32 */ - const char *zFilename, /* Name of file to delete */ - int syncDir /* Not used on win32 */ ++#define _SHM_UNLCK 1 ++#define _SHM_RDLCK 2 ++#define _SHM_WRLCK 3 ++static int winShmSystemLock( ++ winShmNode *pFile, /* Apply locks to this open shared-memory segment */ ++ int lockType, /* _SHM_UNLCK, _SHM_RDLCK, or _SHM_WRLCK */ ++ u8 lockMask /* Which bytes to lock or unlock */ +){ - int cnt = 0; - DWORD rc; - DWORD error = 0; - void *zConverted = convertUtf8Filename(zFilename); - UNUSED_PARAMETER(pVfs); - UNUSED_PARAMETER(syncDir); - if( zConverted==0 ){ - return SQLITE_NOMEM; - } - SimulateIOError(return SQLITE_IOERR_DELETE); - if( isNT() ){ - do{ - DeleteFileW(zConverted); - }while( ( ((rc = GetFileAttributesW(zConverted)) != INVALID_FILE_ATTRIBUTES) - || ((error = GetLastError()) == ERROR_ACCESS_DENIED)) - && (++cnt < MX_DELETION_ATTEMPTS) - && (Sleep(100), 1) ); - /* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed. - ** Since the ASCII version of these Windows API do not exist for WINCE, - ** it's important to not reference them for WINCE builds. - */ - #if SQLITE_OS_WINCE==0 - }else{ - do{ - DeleteFileA(zConverted); - }while( ( ((rc = GetFileAttributesA(zConverted)) != INVALID_FILE_ATTRIBUTES) - || ((error = GetLastError()) == ERROR_ACCESS_DENIED)) - && (++cnt < MX_DELETION_ATTEMPTS) - && (Sleep(100), 1) ); - #endif - } - free(zConverted); - OSTRACE(("DELETE \"%s\" %s\n", zFilename, ( (rc == INVALID_FILE_ATTRIBUTES) - && (error == ERROR_FILE_NOT_FOUND)) ? "ok" : "failed" )); - return ( (rc == INVALID_FILE_ATTRIBUTES) - && (error == ERROR_FILE_NOT_FOUND)) ? SQLITE_OK : SQLITE_IOERR_DELETE; - } ++ OVERLAPPED ovlp; ++ DWORD dwFlags; ++ int nBytes; /* Number of bytes to lock */ ++ int i; /* Offset into the locking byte range */ ++ int rc = 0; /* Result code form Lock/UnlockFileEx() */ ++ u8 mask; /* Mask of bits in lockMask */ + - /* - ** Check the existance and status of a file. - */ - static 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 */ - int *pResOut /* OUT: Result */ - ){ - DWORD attr; - int rc = 0; - void *zConverted = convertUtf8Filename(zFilename); - UNUSED_PARAMETER(pVfs); - if( zConverted==0 ){ - return SQLITE_NOMEM; - } - if( isNT() ){ - attr = GetFileAttributesW((WCHAR*)zConverted); - /* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed. - ** Since the ASCII version of these Windows API do not exist for WINCE, - ** it's important to not reference them for WINCE builds. - */ - #if SQLITE_OS_WINCE==0 ++ /* Access to the winShmNode object is serialized by the caller */ ++ assert( sqlite3_mutex_held(pFile->mutex) || pFile->nRef==0 ); ++ ++ /* Initialize the locking parameters */ ++ if( lockMask==WIN_SHM_C && lockType!=_SHM_UNLCK ){ ++ dwFlags = 0; ++ OSTRACE(("SHM-LOCK %d requesting blocking lock %s\n", ++ pFile->hFile.h, ++ winShmLockString(lockMask))); + }else{ - attr = GetFileAttributesA((char*)zConverted); - #endif - } - free(zConverted); - switch( flags ){ - case SQLITE_ACCESS_READ: - case SQLITE_ACCESS_EXISTS: - rc = attr!=INVALID_FILE_ATTRIBUTES; - break; - case SQLITE_ACCESS_READWRITE: - rc = (attr & FILE_ATTRIBUTE_READONLY)==0; - break; - default: - assert(!"Invalid flags argument"); ++ dwFlags = LOCKFILE_FAIL_IMMEDIATELY; ++ OSTRACE(("SHM-LOCK %d requesting %s %s\n", ++ pFile->hFile.h, ++ lockType!=_SHM_UNLCK ? "lock" : "unlock", ++ winShmLockString(lockMask))); + } - *pResOut = rc; - return SQLITE_OK; - } ++ if( lockType == _SHM_WRLCK ) dwFlags |= LOCKFILE_EXCLUSIVE_LOCK; + ++ /* Find the first bit in lockMask that is set */ ++ for(i=0, mask=0x01; mask!=0 && (lockMask&mask)==0; mask <<= 1, i++){} ++ assert( mask!=0 ); ++ memset(&ovlp, 0, sizeof(OVERLAPPED)); ++ ovlp.Offset = i+WIN_SHM_BASE; ++ nBytes = 1; + - /* - ** Turn a relative pathname into a full pathname. Write the full - ** pathname into zOut[]. zOut[] will be at least pVfs->mxPathname - ** bytes in size. - */ - static 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 */ - ){ - - #if defined(__CYGWIN__) - UNUSED_PARAMETER(nFull); - cygwin_conv_to_full_win32_path(zRelative, zFull); - return SQLITE_OK; - #endif ++ /* Extend the locking range for each additional bit that is set */ ++ mask <<= 1; ++ while( mask!=0 && (lockMask & mask)!=0 ){ ++ nBytes++; ++ mask <<= 1; ++ } + - #if SQLITE_OS_WINCE - UNUSED_PARAMETER(nFull); - /* WinCE has no concept of a relative pathname, or so I am told. */ - sqlite3_snprintf(pVfs->mxPathname, zFull, "%s", zRelative); - return SQLITE_OK; - #endif ++ /* Verify that all bits set in lockMask are contiguous */ ++ assert( mask==0 || (lockMask & ~(mask | (mask-1)))==0 ); + - #if !SQLITE_OS_WINCE && !defined(__CYGWIN__) - int nByte; - void *zConverted; - char *zOut; - UNUSED_PARAMETER(nFull); - zConverted = convertUtf8Filename(zRelative); - if( isNT() ){ - WCHAR *zTemp; - nByte = GetFullPathNameW((WCHAR*)zConverted, 0, 0, 0) + 3; - zTemp = malloc( nByte*sizeof(zTemp[0]) ); - if( zTemp==0 ){ - free(zConverted); - return SQLITE_NOMEM; - } - GetFullPathNameW((WCHAR*)zConverted, nByte, zTemp, 0); - free(zConverted); - zOut = unicodeToUtf8(zTemp); - free(zTemp); - /* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed. - ** Since the ASCII version of these Windows API do not exist for WINCE, - ** it's important to not reference them for WINCE builds. - */ - #if SQLITE_OS_WINCE==0 ++ /* Release/Acquire the system-level lock */ ++ if( lockType==_SHM_UNLCK ){ ++ for(i=0; ihFile.h, 0, 1, 0, &ovlp); ++ if( !rc ) break; ++ } + }else{ - char *zTemp; - nByte = GetFullPathNameA((char*)zConverted, 0, 0, 0) + 3; - zTemp = malloc( nByte*sizeof(zTemp[0]) ); - if( zTemp==0 ){ - free(zConverted); - return SQLITE_NOMEM; ++ /* release old individual byte locks (if any) ++ ** and set new individual byte locks */ ++ for(i=0; ihFile.h, 0, 1, 0, &ovlp); ++ rc = LockFileEx(pFile->hFile.h, dwFlags, 0, 1, 0, &ovlp); ++ if( !rc ) break; + } - GetFullPathNameA((char*)zConverted, nByte, zTemp, 0); - free(zConverted); - zOut = sqlite3_win32_mbcs_to_utf8(zTemp); - free(zTemp); - #endif + } - if( zOut ){ - sqlite3_snprintf(pVfs->mxPathname, zFull, "%s", zOut); - free(zOut); - return SQLITE_OK; ++ if( !rc ){ ++ OSTRACE(("SHM-LOCK %d %s ERROR 0x%08lx\n", ++ pFile->hFile.h, ++ lockType==_SHM_UNLCK ? "UnlockFileEx" : "LockFileEx", ++ GetLastError())); ++ /* release individual byte locks (if any) */ ++ ovlp.Offset-=i; ++ for(i=0; ihFile.h, 0, 1, 0, &ovlp); ++ } ++ } ++ rc = (rc!=0) ? SQLITE_OK : SQLITE_BUSY; ++ ++ /* Update the global lock state and do debug tracing */ ++#ifdef SQLITE_DEBUG ++ OSTRACE(("SHM-LOCK %d ", pFile->hFile.h)); ++ if( rc==SQLITE_OK ){ ++ if( lockType==_SHM_UNLCK ){ ++ OSTRACE(("unlock ok")); ++ pFile->exclMask &= ~lockMask; ++ pFile->sharedMask &= ~lockMask; ++ }else if( lockType==_SHM_RDLCK ){ ++ OSTRACE(("read-lock ok")); ++ pFile->exclMask &= ~lockMask; ++ pFile->sharedMask |= lockMask; ++ }else{ ++ assert( lockType==_SHM_WRLCK ); ++ OSTRACE(("write-lock ok")); ++ pFile->exclMask |= lockMask; ++ pFile->sharedMask &= ~lockMask; ++ } + }else{ - return SQLITE_NOMEM; ++ if( lockType==_SHM_UNLCK ){ ++ OSTRACE(("unlock failed")); ++ }else if( lockType==_SHM_RDLCK ){ ++ OSTRACE(("read-lock failed")); ++ }else{ ++ assert( lockType==_SHM_WRLCK ); ++ OSTRACE(("write-lock failed")); ++ } + } ++ OSTRACE((" - change requested %s - afterwards %s:%s\n", ++ winShmLockString(lockMask), ++ winShmLockString(pFile->sharedMask), ++ winShmLockString(pFile->exclMask))); +#endif ++ ++ return rc; +} + +/* - ** Get the sector size of the device used to store - ** file. ++** For connection p, unlock all of the locks identified by the unlockMask ++** parameter. +*/ - static int getSectorSize( - sqlite3_vfs *pVfs, - const char *zRelative /* UTF-8 file name */ ++static int winShmUnlock( ++ winShmNode *pFile, /* The underlying shared-memory file */ ++ winShm *p, /* The connection to be unlocked */ ++ u8 unlockMask /* Mask of locks to be unlocked */ +){ - DWORD bytesPerSector = SQLITE_DEFAULT_SECTOR_SIZE; - /* GetDiskFreeSpace is not supported under WINCE */ - #if SQLITE_OS_WINCE - UNUSED_PARAMETER(pVfs); - UNUSED_PARAMETER(zRelative); - #else - char zFullpath[MAX_PATH+1]; - int rc; - DWORD dwRet = 0; - DWORD dwDummy; ++ int rc; /* Result code */ ++ winShm *pX; /* For looping over all sibling connections */ ++ u8 allMask; /* Union of locks held by connections other than "p" */ + - /* - ** We need to get the full path name of the file - ** to get the drive letter to look up the sector - ** size. - */ - rc = winFullPathname(pVfs, zRelative, MAX_PATH, zFullpath); - if( rc == SQLITE_OK ) - { - void *zConverted = convertUtf8Filename(zFullpath); - if( zConverted ){ - if( isNT() ){ - /* trim path to just drive reference */ - WCHAR *p = zConverted; - for(;*p;p++){ - if( *p == '\\' ){ - *p = '\0'; - break; - } - } - dwRet = GetDiskFreeSpaceW((WCHAR*)zConverted, - &dwDummy, - &bytesPerSector, - &dwDummy, - &dwDummy); - }else{ - /* trim path to just drive reference */ - char *p = (char *)zConverted; - for(;*p;p++){ - if( *p == '\\' ){ - *p = '\0'; - break; - } - } - dwRet = GetDiskFreeSpaceA((char*)zConverted, - &dwDummy, - &bytesPerSector, - &dwDummy, - &dwDummy); - } - free(zConverted); - } - if( !dwRet ){ - bytesPerSector = SQLITE_DEFAULT_SECTOR_SIZE; - } ++ /* Access to the winShmNode object is serialized by the caller */ ++ assert( sqlite3_mutex_held(pFile->mutex) ); ++ ++ /* don't attempt to unlock anything we don't have locks for */ ++ if( (unlockMask & (p->exclMask|p->sharedMask)) != unlockMask ){ ++ OSTRACE(("SHM-LOCK %d unlocking more than we have locked - requested %s - have %s\n", ++ pFile->hFile.h, ++ winShmLockString(unlockMask), ++ winShmLockString(p->exclMask|p->sharedMask))); ++ unlockMask &= (p->exclMask|p->sharedMask); + } - #endif - return (int) bytesPerSector; - } + - #ifndef SQLITE_OMIT_LOAD_EXTENSION - /* - ** Interfaces for opening a shared library, finding entry points - ** within the shared library, and closing the shared library. - */ - /* - ** Interfaces for opening a shared library, finding entry points - ** within the shared library, and closing the shared library. - */ - static void *winDlOpen(sqlite3_vfs *pVfs, const char *zFilename){ - HANDLE h; - void *zConverted = convertUtf8Filename(zFilename); - UNUSED_PARAMETER(pVfs); - if( zConverted==0 ){ - return 0; ++ /* Compute locks held by sibling connections */ ++ allMask = 0; ++ for(pX=pFile->pFirst; pX; pX=pX->pNext){ ++ if( pX==p ) continue; ++ assert( (pX->exclMask & (p->exclMask|p->sharedMask))==0 ); ++ allMask |= pX->sharedMask; + } - if( isNT() ){ - h = LoadLibraryW((WCHAR*)zConverted); - /* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed. - ** Since the ASCII version of these Windows API do not exist for WINCE, - ** it's important to not reference them for WINCE builds. - */ - #if SQLITE_OS_WINCE==0 ++ ++ /* Unlock the system-level locks */ ++ if( (unlockMask & allMask)!=unlockMask ){ ++ rc = winShmSystemLock(pFile, _SHM_UNLCK, unlockMask & ~allMask); + }else{ - h = LoadLibraryA((char*)zConverted); - #endif ++ rc = SQLITE_OK; + } - free(zConverted); - return (void*)h; - } - static void winDlError(sqlite3_vfs *pVfs, int nBuf, char *zBufOut){ - UNUSED_PARAMETER(pVfs); - getLastErrorMsg(nBuf, zBufOut); - } - void (*winDlSym(sqlite3_vfs *pVfs, void *pHandle, const char *zSymbol))(void){ - UNUSED_PARAMETER(pVfs); - #if SQLITE_OS_WINCE - /* The GetProcAddressA() routine is only available on wince. */ - return (void(*)(void))GetProcAddressA((HANDLE)pHandle, zSymbol); - #else - /* All other windows platforms expect GetProcAddress() to take - ** an Ansi string regardless of the _UNICODE setting */ - return (void(*)(void))GetProcAddress((HANDLE)pHandle, zSymbol); - #endif - } - void winDlClose(sqlite3_vfs *pVfs, void *pHandle){ - UNUSED_PARAMETER(pVfs); - FreeLibrary((HANDLE)pHandle); - } - #else /* if SQLITE_OMIT_LOAD_EXTENSION is defined: */ - #define winDlOpen 0 - #define winDlError 0 - #define winDlSym 0 - #define winDlClose 0 - #endif + ++ /* Undo the local locks */ ++ if( rc==SQLITE_OK ){ ++ p->exclMask &= ~unlockMask; ++ p->sharedMask &= ~unlockMask; ++ } ++ return rc; ++} + +/* - ** Write up to nBuf bytes of randomness into zBuf. ++** Get reader locks for connection p on all locks in the readMask parameter. +*/ - static int winRandomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ - int n = 0; - UNUSED_PARAMETER(pVfs); - #if defined(SQLITE_TEST) - n = nBuf; - memset(zBuf, 0, nBuf); - #else - if( sizeof(SYSTEMTIME)<=nBuf-n ){ - SYSTEMTIME x; - GetSystemTime(&x); - memcpy(&zBuf[n], &x, sizeof(x)); - n += sizeof(x); - } - if( sizeof(DWORD)<=nBuf-n ){ - DWORD pid = GetCurrentProcessId(); - memcpy(&zBuf[n], &pid, sizeof(pid)); - n += sizeof(pid); ++static int winShmSharedLock( ++ winShmNode *pFile, /* The underlying shared-memory file */ ++ winShm *p, /* The connection to get the shared locks */ ++ u8 readMask /* Mask of shared locks to be acquired */ ++){ ++ int rc; /* Result code */ ++ winShm *pX; /* For looping over all sibling connections */ ++ u8 allShared; /* Union of locks held by connections other than "p" */ ++ ++ /* Access to the winShmNode object is serialized by the caller */ ++ assert( sqlite3_mutex_held(pFile->mutex) ); ++ ++ /* Find out which shared locks are already held by sibling connections. ++ ** If any sibling already holds an exclusive lock, go ahead and return ++ ** SQLITE_BUSY. ++ */ ++ allShared = 0; ++ for(pX=pFile->pFirst; pX; pX=pX->pNext){ ++ if( pX==p ) continue; ++ if( (pX->exclMask & readMask)!=0 ) return SQLITE_BUSY; ++ allShared |= pX->sharedMask; + } - if( sizeof(DWORD)<=nBuf-n ){ - DWORD cnt = GetTickCount(); - memcpy(&zBuf[n], &cnt, sizeof(cnt)); - n += sizeof(cnt); ++ ++ /* Get shared locks at the system level, if necessary */ ++ if( (~allShared) & readMask ){ ++ rc = winShmSystemLock(pFile, _SHM_RDLCK, readMask); ++ }else{ ++ rc = SQLITE_OK; + } - if( sizeof(LARGE_INTEGER)<=nBuf-n ){ - LARGE_INTEGER i; - QueryPerformanceCounter(&i); - memcpy(&zBuf[n], &i, sizeof(i)); - n += sizeof(i); ++ ++ /* Get the local shared locks */ ++ if( rc==SQLITE_OK ){ ++ p->sharedMask |= readMask; + } - #endif - return n; ++ return rc; +} + - +/* - ** Sleep for a little while. Return the amount of time slept. ++** For connection p, get an exclusive lock on all locks identified in ++** the writeMask parameter. +*/ - static int winSleep(sqlite3_vfs *pVfs, int microsec){ - Sleep((microsec+999)/1000); - UNUSED_PARAMETER(pVfs); - return ((microsec+999)/1000)*1000; ++static int winShmExclusiveLock( ++ winShmNode *pFile, /* The underlying shared-memory file */ ++ winShm *p, /* The connection to get the exclusive locks */ ++ u8 writeMask /* Mask of exclusive locks to be acquired */ ++){ ++ int rc; /* Result code */ ++ winShm *pX; /* For looping over all sibling connections */ ++ ++ /* Access to the winShmNode object is serialized by the caller */ ++ assert( sqlite3_mutex_held(pFile->mutex) ); ++ ++ /* Make sure no sibling connections hold locks that will block this ++ ** lock. If any do, return SQLITE_BUSY right away. ++ */ ++ for(pX=pFile->pFirst; pX; pX=pX->pNext){ ++ if( pX==p ) continue; ++ if( (pX->exclMask & writeMask)!=0 ) return SQLITE_BUSY; ++ if( (pX->sharedMask & writeMask)!=0 ) return SQLITE_BUSY; ++ } ++ ++ /* Get the exclusive locks at the system level. Then if successful ++ ** also mark the local connection as being locked. ++ */ ++ rc = winShmSystemLock(pFile, _SHM_WRLCK, writeMask); ++ if( rc==SQLITE_OK ){ ++ p->sharedMask &= ~writeMask; ++ p->exclMask |= writeMask; ++ } ++ return rc; +} + +/* - ** The following variable, if set to a non-zero value, is interpreted as - ** the number of seconds since 1970 and is used to set the result of - ** sqlite3OsCurrentTime() during testing. ++** Purge the winShmNodeList list of all entries with winShmNode.nRef==0. ++** ++** This is not a VFS shared-memory method; it is a utility function called ++** by VFS shared-memory methods. +*/ - #ifdef SQLITE_TEST - int sqlite3_current_time = 0; /* Fake system time in seconds since 1970. */ - #endif ++static void winShmPurge(void){ ++ winShmNode **pp; ++ winShmNode *p; ++ assert( winShmMutexHeld() ); ++ pp = &winShmNodeList; ++ while( (p = *pp)!=0 ){ ++ if( p->nRef==0 ){ ++ if( p->mutex ) sqlite3_mutex_free(p->mutex); ++ if( p->mutexBuf ) sqlite3_mutex_free(p->mutexBuf); ++ if( p->pMMapBuf ){ ++ UnmapViewOfFile(p->pMMapBuf); ++ } ++ if( INVALID_HANDLE_VALUE != p->hMap ){ ++ CloseHandle(p->hMap); ++ } ++ if( p->hFile.h != INVALID_HANDLE_VALUE ) { ++ winClose((sqlite3_file *)&p->hFile); ++ } ++ *pp = p->pNext; ++ sqlite3_free(p); ++ }else{ ++ pp = &p->pNext; ++ } ++ } ++} ++ ++/* Forward references to VFS methods */ ++static int winOpen(sqlite3_vfs*,const char*,sqlite3_file*,int,int*); ++static int winDelete(sqlite3_vfs *,const char*,int); + +/* - ** Find the current time (in Universal Coordinated Time). Write into *piNow - ** the current time and date as a Julian Day number times 86_400_000. In - ** other words, write into *piNow the number of milliseconds since the Julian - ** epoch of noon in Greenwich on November 24, 4714 B.C according to the - ** proleptic Gregorian calendar. ++** Open a shared-memory area. This particular implementation uses ++** mmapped files. +** - ** On success, return 0. Return 1 if the time and date cannot be found. ++** zName is a filename used to identify the shared-memory area. The ++** implementation does not (and perhaps should not) use this name ++** directly, but rather use it as a template for finding an appropriate ++** name for the shared-memory storage. In this implementation, the ++** string "-index" is appended to zName and used as the name of the ++** mmapped file. ++** ++** When opening a new shared-memory file, if no other instances of that ++** file are currently open, in this process or in other processes, then ++** the file must be truncated to zero length or have its header cleared. +*/ - static int winCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *piNow){ - /* FILETIME structure is a 64-bit value representing the number of - 100-nanosecond intervals since January 1, 1601 (= JD 2305813.5). ++static int winShmOpen( ++ sqlite3_file *fd /* The file to which to attach shared memory */ ++){ ++ struct winFile *pDbFd; /* Database to which to attach SHM */ ++ struct winShm *p; /* The connection to be opened */ ++ struct winShmNode *pShmNode = 0; /* The underlying mmapped file */ ++ int rc; /* Result code */ ++ struct winShmNode *pNew; /* Newly allocated winShmNode */ ++ int nName; /* Size of zName in bytes */ ++ ++ pDbFd = (winFile*)fd; ++ assert( pDbFd->pShm==0 ); /* Not previously opened */ ++ ++ /* Allocate space for the new sqlite3_shm object. Also speculatively ++ ** allocate space for a new winShmNode and filename. + */ - FILETIME ft; - static const sqlite3_int64 winFiletimeEpoch = 23058135*(sqlite3_int64)8640000; - #ifdef SQLITE_TEST - static const sqlite3_int64 unixEpoch = 24405875*(sqlite3_int64)8640000; - #endif - /* 2^32 - to avoid use of LL and warnings in gcc */ - static const sqlite3_int64 max32BitValue = - (sqlite3_int64)2000000000 + (sqlite3_int64)2000000000 + (sqlite3_int64)294967296; ++ p = sqlite3_malloc( sizeof(*p) ); ++ if( p==0 ) return SQLITE_NOMEM; ++ memset(p, 0, sizeof(*p)); ++ nName = sqlite3Strlen30(pDbFd->zPath); ++ pNew = sqlite3_malloc( sizeof(*pShmNode) + nName + 15 ); ++ if( pNew==0 ){ ++ sqlite3_free(p); ++ return SQLITE_NOMEM; ++ } ++ memset(pNew, 0, sizeof(*pNew)); ++ pNew->zFilename = (char*)&pNew[1]; ++ sqlite3_snprintf(nName+15, pNew->zFilename, "%s-wal-index", pDbFd->zPath); + - #if SQLITE_OS_WINCE - SYSTEMTIME time; - GetSystemTime(&time); - /* if SystemTimeToFileTime() fails, it returns zero. */ - if (!SystemTimeToFileTime(&time,&ft)){ - return 1; ++ /* Look to see if there is an existing winShmNode that can be used. ++ ** If no matching winShmNode currently exists, create a new one. ++ */ ++ winShmEnterMutex(); ++ for(pShmNode = winShmNodeList; pShmNode; pShmNode=pShmNode->pNext){ ++ /* TBD need to come up with better match here. Perhaps ++ ** use FILE_ID_BOTH_DIR_INFO Structure. ++ */ ++ if( sqlite3StrICmp(pShmNode->zFilename, pNew->zFilename)==0 ) break; + } - #else - GetSystemTimeAsFileTime( &ft ); ++ if( pShmNode ){ ++ sqlite3_free(pNew); ++ }else{ ++ pShmNode = pNew; ++ pNew = 0; ++ pShmNode->pMMapBuf = NULL; ++ pShmNode->hMap = INVALID_HANDLE_VALUE; ++ ((winFile*)(&pShmNode->hFile))->h = INVALID_HANDLE_VALUE; ++ pShmNode->pNext = winShmNodeList; ++ winShmNodeList = pShmNode; ++ ++ pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); ++ if( pShmNode->mutex==0 ){ ++ rc = SQLITE_NOMEM; ++ goto shm_open_err; ++ } ++ pShmNode->mutexBuf = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); ++ if( pShmNode->mutexBuf==0 ){ ++ rc = SQLITE_NOMEM; ++ goto shm_open_err; ++ } ++ rc = winOpen(pDbFd->pVfs, ++ pShmNode->zFilename, /* Name of the file (UTF-8) */ ++ (sqlite3_file*)&pShmNode->hFile, /* File handle here */ ++ SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, /* Mode flags */ ++ 0); ++ if( SQLITE_OK!=rc ){ ++ rc = SQLITE_CANTOPEN_BKPT; ++ goto shm_open_err; ++ } ++ ++ /* Check to see if another process is holding the dead-man switch. ++ ** If not, truncate the file to zero length. ++ */ ++ if( winShmSystemLock(pShmNode, _SHM_WRLCK, WIN_SHM_DMS)==SQLITE_OK ){ ++ rc = winTruncate((sqlite3_file *)&pShmNode->hFile, 0); ++ } ++ if( rc==SQLITE_OK ){ ++ rc = winShmSystemLock(pShmNode, _SHM_RDLCK, WIN_SHM_DMS); ++ } ++ if( rc ) goto shm_open_err; ++ } ++ ++ /* Make the new connection a child of the winShmNode */ ++ p->pShmNode = pShmNode; ++ p->pNext = pShmNode->pFirst; ++#ifdef SQLITE_DEBUG ++ p->id = pShmNode->nextShmId++; +#endif ++ pShmNode->pFirst = p; ++ pShmNode->nRef++; ++ pDbFd->pShm = p; ++ winShmLeaveMutex(); ++ return SQLITE_OK; + - *piNow = winFiletimeEpoch + - ((((sqlite3_int64)ft.dwHighDateTime)*max32BitValue) + - (sqlite3_int64)ft.dwLowDateTime)/(sqlite3_int64)1000; ++ /* Jump here on any error */ ++shm_open_err: ++ winShmSystemLock(pShmNode, _SHM_UNLCK, WIN_SHM_DMS); ++ winShmPurge(); /* This call frees pShmNode if required */ ++ sqlite3_free(p); ++ sqlite3_free(pNew); ++ winShmLeaveMutex(); ++ return rc; ++} + - #ifdef SQLITE_TEST - if( sqlite3_current_time ){ - *piNow = 1000*(sqlite3_int64)sqlite3_current_time + unixEpoch; ++/* ++** Close a connection to shared-memory. Delete the underlying ++** storage if deleteFlag is true. ++*/ ++static int winShmClose( ++ sqlite3_file *fd, /* Database holding shared memory */ ++ int deleteFlag /* Delete after closing if true */ ++){ ++ winFile *pDbFd; /* Database holding shared-memory */ ++ winShm *p; /* The connection to be closed */ ++ winShmNode *pShmNode; /* The underlying shared-memory file */ ++ winShm **pp; /* For looping over sibling connections */ ++ ++ pDbFd = (winFile*)fd; ++ p = pDbFd->pShm; ++ pShmNode = p->pShmNode; ++ ++ /* Verify that the connection being closed holds no locks */ ++ assert( p->exclMask==0 ); ++ assert( p->sharedMask==0 ); ++ ++ /* Remove connection p from the set of connections associated ++ ** with pShmNode */ ++ sqlite3_mutex_enter(pShmNode->mutex); ++ for(pp=&pShmNode->pFirst; (*pp)!=p; pp = &(*pp)->pNext){} ++ *pp = p->pNext; ++ ++ /* Free the connection p */ ++ sqlite3_free(p); ++ pDbFd->pShm = 0; ++ sqlite3_mutex_leave(pShmNode->mutex); ++ ++ /* If pShmNode->nRef has reached 0, then close the underlying ++ ** shared-memory file, too */ ++ winShmEnterMutex(); ++ assert( pShmNode->nRef>0 ); ++ pShmNode->nRef--; ++ if( pShmNode->nRef==0 ){ ++ if( deleteFlag ) winDelete(pDbFd->pVfs, pShmNode->zFilename, 0); ++ winShmPurge(); + } - #endif - UNUSED_PARAMETER(pVfs); - return 0; ++ winShmLeaveMutex(); ++ ++ return SQLITE_OK; +} + +/* - ** 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. ++** Query and/or changes the size of the underlying storage for ++** a shared-memory segment. The reqSize parameter is the new size ++** of the underlying storage, or -1 to do just a query. The size ++** of the underlying storage (after resizing if resizing occurs) is ++** written into pNewSize. ++** ++** This routine does not (necessarily) change the size of the mapping ++** of the underlying storage into memory. Use xShmGet() to change ++** the mapping size. ++** ++** The reqSize parameter is the minimum size requested. The implementation ++** is free to expand the storage to some larger amount if it chooses. +*/ - int winCurrentTime(sqlite3_vfs *pVfs, double *prNow){ - int rc; - sqlite3_int64 i; - rc = winCurrentTimeInt64(pVfs, &i); - if( !rc ){ - *prNow = i/86400000.0; ++static int winShmSize( ++ sqlite3_file *fd, /* Database holding the shared memory */ ++ int reqSize, /* Requested size. -1 for query only */ ++ int *pNewSize /* Write new size here */ ++){ ++ winFile *pDbFd = (winFile*)fd; ++ winShm *p = pDbFd->pShm; ++ winShmNode *pShmNode = p->pShmNode; ++ int rc = SQLITE_OK; ++ ++ *pNewSize = 0; ++ if( reqSize>=0 ){ ++ sqlite3_int64 sz; ++ rc = winFileSize((sqlite3_file *)&pShmNode->hFile, &sz); ++ if( SQLITE_OK==rc ){ ++ reqSize = (reqSize + SQLITE_WIN_SHM_INCR - 1)/SQLITE_WIN_SHM_INCR; ++ reqSize *= SQLITE_WIN_SHM_INCR; ++ if( reqSize>sz ){ ++ rc = winTruncate((sqlite3_file *)&pShmNode->hFile, reqSize); ++ } ++ } ++ } ++ if( SQLITE_OK==rc ){ ++ sqlite3_int64 sz; ++ rc = winFileSize((sqlite3_file *)&pShmNode->hFile, &sz); ++ if( SQLITE_OK==rc ){ ++ *pNewSize = (int)sz; ++ }else{ ++ rc = SQLITE_IOERR; ++ } + } + return rc; +} + ++ +/* - ** The idea is that this function works like a combination of - ** GetLastError() and FormatMessage() on windows (or errno and - ** strerror_r() on unix). After an error is returned by an OS - ** function, SQLite calls this function with zBuf pointing to - ** a buffer of nBuf bytes. The OS layer should populate the - ** buffer with a nul-terminated UTF-8 encoded error message - ** describing the last IO error to have occurred within the calling - ** thread. ++** Map the shared storage into memory. The minimum size of the ++** mapping should be reqMapSize if reqMapSize is positive. If ++** reqMapSize is zero or negative, the implementation can choose ++** whatever mapping size is convenient. +** - ** If the error message is too large for the supplied buffer, - ** it should be truncated. The return value of xGetLastError - ** is zero if the error message fits in the buffer, or non-zero - ** otherwise (if the message was truncated). If non-zero is returned, - ** then it is not necessary to include the nul-terminator character - ** in the output buffer. ++** *ppBuf is made to point to the memory which is a mapping of the ++** underlying storage. A mutex is acquired to prevent other threads ++** from running while *ppBuf is in use in order to prevent other threads ++** remapping *ppBuf out from under this thread. The winShmRelease() ++** call will release the mutex. However, if the lock state is CHECKPOINT, ++** the mutex is not acquired because CHECKPOINT will never remap the ++** buffer. RECOVER might remap, though, so CHECKPOINT will acquire ++** the mutex if and when it promotes to RECOVER. +** - ** Not supplying an error message will have no adverse effect - ** on SQLite. It is fine to have an implementation that never - ** returns an error message: ++** RECOVER needs to be atomic. The same mutex that prevents *ppBuf from ++** being remapped also prevents more than one thread from being in ++** RECOVER at a time. But, RECOVER sometimes wants to remap itself. ++** To prevent RECOVER from losing its lock while remapping, the ++** mutex is not released by winShmRelease() when in RECOVER. +** - ** int xGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ - ** assert(zBuf[0]=='\0'); - ** return 0; - ** } ++** *pNewMapSize is set to the size of the mapping. +** - ** However if an error message is supplied, it will be incorporated - ** by sqlite into the error message available to the user using - ** sqlite3_errmsg(), possibly making IO errors easier to debug. ++** *ppBuf and *pNewMapSize might be NULL and zero if no space has ++** yet been allocated to the underlying storage. +*/ - static int winGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ - UNUSED_PARAMETER(pVfs); - return getLastErrorMsg(nBuf, zBuf); - } ++static int winShmGet( ++ sqlite3_file *fd, /* The database file holding the shared memory */ ++ int reqMapSize, /* Requested size of mapping. -1 means don't care */ ++ int *pNewMapSize, /* Write new size of mapping here */ ++ void **ppBuf /* Write mapping buffer origin here */ ++){ ++ winFile *pDbFd = (winFile*)fd; ++ winShm *p = pDbFd->pShm; ++ winShmNode *pShmNode = p->pShmNode; ++ int rc = SQLITE_OK; + - #ifndef SQLITE_OMIT_WAL ++ if( p->lockState!=SQLITE_SHM_CHECKPOINT && p->hasMutexBuf==0 ){ ++ assert( sqlite3_mutex_notheld(pShmNode->mutex) ); ++ sqlite3_mutex_enter(pShmNode->mutexBuf); ++ p->hasMutexBuf = 1; ++ } ++ sqlite3_mutex_enter(pShmNode->mutex); ++ if( pShmNode->szMap==0 || reqMapSize>pShmNode->szMap ){ ++ int actualSize; ++ if( winShmSize(fd, -1, &actualSize)==SQLITE_OK ++ && reqMapSizepMMapBuf ){ ++ if( !UnmapViewOfFile(pShmNode->pMMapBuf) ){ ++ pShmNode->lastErrno = GetLastError(); ++ rc = SQLITE_IOERR; ++ } ++ CloseHandle(pShmNode->hMap); ++ pShmNode->hMap = INVALID_HANDLE_VALUE; ++ } ++ if( SQLITE_OK == rc ){ ++ pShmNode->pMMapBuf = 0; ++ if( reqMapSize == 0 ){ ++ /* can't create 0 byte file mapping in Windows */ ++ pShmNode->szMap = 0; ++ }else{ ++ /* create the file mapping object */ ++ if( INVALID_HANDLE_VALUE == pShmNode->hMap ){ ++ /* TBD provide an object name to each file ++ ** mapping so it can be re-used across processes. ++ */ ++ pShmNode->hMap = CreateFileMapping(pShmNode->hFile.h, ++ NULL, ++ PAGE_READWRITE, ++ 0, ++ reqMapSize, ++ NULL); ++ } ++ if( NULL==pShmNode->hMap ){ ++ pShmNode->lastErrno = GetLastError(); ++ rc = SQLITE_IOERR; ++ pShmNode->szMap = 0; ++ pShmNode->hMap = INVALID_HANDLE_VALUE; ++ }else{ ++ pShmNode->pMMapBuf = MapViewOfFile(pShmNode->hMap, ++ FILE_MAP_WRITE | FILE_MAP_READ, ++ 0, ++ 0, ++ reqMapSize); ++ if( !pShmNode->pMMapBuf ){ ++ pShmNode->lastErrno = GetLastError(); ++ rc = SQLITE_IOERR; ++ pShmNode->szMap = 0; ++ }else{ ++ pShmNode->szMap = reqMapSize; ++ } ++ } ++ } ++ } ++ } ++ *pNewMapSize = pShmNode->szMap; ++ *ppBuf = pShmNode->pMMapBuf; ++ sqlite3_mutex_leave(pShmNode->mutex); ++ return rc; ++} + +/* - ** Helper functions to obtain and relinquish the global mutex. The - ** global mutex is used to protect the winLockInfo objects used by - ** this file, all of which may be shared by multiple threads. ++** Release the lock held on the shared memory segment so that other ++** threads are free to resize it if necessary. +** - ** Function winShmMutexHeld() is used to assert() that the global mutex - ** is held when required. This function is only used as part of assert() - ** statements. e.g. ++** If the lock is not currently held, this routine is a harmless no-op. +** - ** winShmEnterMutex() - ** assert( winShmMutexHeld() ); - ** winEnterLeave() ++** If the shared-memory object is in lock state RECOVER, then we do not ++** really want to release the lock, so in that case too, this routine ++** is a no-op. +*/ - static void winShmEnterMutex(void){ - sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); - } - static void winShmLeaveMutex(void){ - sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); ++static int winShmRelease(sqlite3_file *fd){ ++ winFile *pDbFd = (winFile*)fd; ++ winShm *p = pDbFd->pShm; ++ if( p->hasMutexBuf && p->lockState!=SQLITE_SHM_RECOVER ){ ++ winShmNode *pShmNode = p->pShmNode; ++ assert( sqlite3_mutex_notheld(pShmNode->mutex) ); ++ sqlite3_mutex_leave(pShmNode->mutexBuf); ++ p->hasMutexBuf = 0; ++ } ++ return SQLITE_OK; +} ++ ++/* ++** Symbolic names for LOCK states used for debugging. ++*/ +#ifdef SQLITE_DEBUG - static int winShmMutexHeld(void) { - return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); - } ++static const char *azLkName[] = { ++ "UNLOCK", ++ "READ", ++ "READ_FULL", ++ "WRITE", ++ "PENDING", ++ "CHECKPOINT", ++ "RECOVER" ++}; +#endif + - /* Forward reference */ - typedef struct winShm winShm; - typedef struct winShmFile winShmFile; + +/* - ** Object used to represent a single file opened and mmapped to provide - ** shared memory. When multiple threads all reference the same - ** log-summary, each thread has its own winFile object, but they all - ** point to a single instance of this object. In other words, each - ** log-summary is opened only once per process. - ** - ** winShmMutexHeld() must be true when creating or destroying - ** this object or while reading or writing the following fields: - ** - ** nRef - ** pNext - ** - ** The following fields are read-only after the object is created: - ** - ** fid - ** zFilename - ** - ** Either winShmFile.mutex must be held or winShmFile.nRef==0 and - ** winShmMutexHeld() is true when reading or writing any other field - ** in this structure. - ** - ** To avoid deadlocks, mutex and mutexBuf are always released in the - ** reverse order that they are acquired. mutexBuf is always acquired - ** first and released last. This invariant is check by asserting - ** sqlite3_mutex_notheld() on mutex whenever mutexBuf is acquired or - ** released. ++** Change the lock state for a shared-memory segment. +*/ - struct winShmFile { - sqlite3_mutex *mutex; /* Mutex to access this object */ - sqlite3_mutex *mutexBuf; /* Mutex to access zBuf[] */ - char *zFilename; /* Name of the file */ - winFile hFile; /* File handle from winOpen */ - HANDLE hMap; /* File handle from CreateFileMapping */ - DWORD lastErrno; /* The Windows errno from the last I/O error */ - int szMap; /* Size of the mapping of file into memory */ - char *pMMapBuf; /* Where currently mmapped(). NULL if unmapped */ - int nRef; /* Number of winShm objects pointing to this */ - winShm *pFirst; /* All winShm objects pointing to this */ - winShmFile *pNext; /* Next in list of all winShmFile objects */ - #ifdef SQLITE_DEBUG - u8 exclMask; /* Mask of exclusive locks held */ - u8 sharedMask; /* Mask of shared locks held */ - u8 nextShmId; /* Next available winShm.id value */ - #endif - }; ++static int winShmLock( ++ sqlite3_file *fd, /* Database holding the shared memory */ ++ int desiredLock, /* One of SQLITE_SHM_xxxxx locking states */ ++ int *pGotLock /* The lock you actually got */ ++){ ++ winFile *pDbFd = (winFile*)fd; ++ winShm *p = pDbFd->pShm; ++ winShmNode *pShmNode = p->pShmNode; ++ int rc = SQLITE_PROTOCOL; ++ ++ /* Note that SQLITE_SHM_READ_FULL and SQLITE_SHM_PENDING are never ++ ** directly requested; they are side effects from requesting ++ ** SQLITE_SHM_READ and SQLITE_SHM_CHECKPOINT, respectively. ++ */ ++ assert( desiredLock==SQLITE_SHM_UNLOCK ++ || desiredLock==SQLITE_SHM_READ ++ || desiredLock==SQLITE_SHM_WRITE ++ || desiredLock==SQLITE_SHM_CHECKPOINT ++ || desiredLock==SQLITE_SHM_RECOVER ); ++ ++ /* Return directly if this is just a lock state query, or if ++ ** the connection is already in the desired locking state. ++ */ ++ if( desiredLock==p->lockState ++ || (desiredLock==SQLITE_SHM_READ && p->lockState==SQLITE_SHM_READ_FULL) ++ ){ ++ OSTRACE(("SHM-LOCK %d shmid-%d, pid-%d request %s and got %s\n", ++ pShmNode->hFile.h, ++ p->id, (int)GetCurrentProcessId(), azLkName[desiredLock], ++ azLkName[p->lockState])); ++ if( pGotLock ) *pGotLock = p->lockState; ++ return SQLITE_OK; ++ } ++ ++ OSTRACE(("SHM-LOCK %d shmid-%d, pid-%d request %s->%s\n", ++ pShmNode->hFile.h, ++ p->id, (int)GetCurrentProcessId(), azLkName[p->lockState], ++ azLkName[desiredLock])); ++ ++ if( desiredLock==SQLITE_SHM_RECOVER && !p->hasMutexBuf ){ ++ assert( sqlite3_mutex_notheld(pShmNode->mutex) ); ++ sqlite3_mutex_enter(pShmNode->mutexBuf); ++ p->hasMutexBuf = 1; ++ } ++ sqlite3_mutex_enter(pShmNode->mutex); ++ switch( desiredLock ){ ++ case SQLITE_SHM_UNLOCK: { ++ assert( p->lockState!=SQLITE_SHM_RECOVER ); ++ winShmUnlock(pShmNode, p, WIN_SHM_A|WIN_SHM_B|WIN_SHM_C|WIN_SHM_D); ++ rc = SQLITE_OK; ++ p->lockState = SQLITE_SHM_UNLOCK; ++ break; ++ } ++ case SQLITE_SHM_READ: { ++ if( p->lockState==SQLITE_SHM_UNLOCK ){ ++ int nAttempt; ++ rc = SQLITE_BUSY; ++ assert( p->lockState==SQLITE_SHM_UNLOCK ); ++ for(nAttempt=0; nAttempt<5 && rc==SQLITE_BUSY; nAttempt++){ ++ rc = winShmSharedLock(pShmNode, p, WIN_SHM_A|WIN_SHM_B); ++ if( rc==SQLITE_BUSY ){ ++ rc = winShmSharedLock(pShmNode, p, WIN_SHM_D); ++ if( rc==SQLITE_OK ){ ++ p->lockState = SQLITE_SHM_READ_FULL; ++ } ++ }else{ ++ winShmUnlock(pShmNode, p, WIN_SHM_B); ++ p->lockState = SQLITE_SHM_READ; ++ } ++ } ++ }else{ ++ assert( p->lockState==SQLITE_SHM_WRITE ++ || p->lockState==SQLITE_SHM_RECOVER ); ++ rc = winShmSharedLock(pShmNode, p, WIN_SHM_A); ++ winShmUnlock(pShmNode, p, WIN_SHM_C|WIN_SHM_D); ++ p->lockState = SQLITE_SHM_READ; ++ } ++ break; ++ } ++ case SQLITE_SHM_WRITE: { ++ assert( p->lockState==SQLITE_SHM_READ ++ || p->lockState==SQLITE_SHM_READ_FULL ); ++ rc = winShmExclusiveLock(pShmNode, p, WIN_SHM_C|WIN_SHM_D); ++ if( rc==SQLITE_OK ){ ++ p->lockState = SQLITE_SHM_WRITE; ++ } ++ break; ++ } ++ case SQLITE_SHM_CHECKPOINT: { ++ assert( p->lockState==SQLITE_SHM_UNLOCK ++ || p->lockState==SQLITE_SHM_PENDING ++ ); ++ if( p->lockState==SQLITE_SHM_UNLOCK ){ ++ rc = winShmExclusiveLock(pShmNode, p, WIN_SHM_B|WIN_SHM_C); ++ if( rc==SQLITE_OK ){ ++ p->lockState = SQLITE_SHM_PENDING; ++ } ++ } ++ if( p->lockState==SQLITE_SHM_PENDING ){ ++ rc = winShmExclusiveLock(pShmNode, p, WIN_SHM_A); ++ if( rc==SQLITE_OK ){ ++ p->lockState = SQLITE_SHM_CHECKPOINT; ++ } ++ } ++ break; ++ } ++ default: { ++ assert( desiredLock==SQLITE_SHM_RECOVER ); ++ assert( p->lockState==SQLITE_SHM_READ ++ || p->lockState==SQLITE_SHM_READ_FULL ++ ); ++ assert( sqlite3_mutex_held(pShmNode->mutexBuf) ); ++ rc = winShmExclusiveLock(pShmNode, p, WIN_SHM_C); ++ if( rc==SQLITE_OK ){ ++ p->lockState = SQLITE_SHM_RECOVER; ++ } ++ break; ++ } ++ } ++ sqlite3_mutex_leave(pShmNode->mutex); ++ OSTRACE(("SHM-LOCK %d shmid-%d, pid-%d got %s\n", ++ pShmNode->hFile.h, ++ p->id, (int)GetCurrentProcessId(), azLkName[p->lockState])); ++ if( pGotLock ) *pGotLock = p->lockState; ++ return rc; ++} + ++#else ++# define winShmOpen 0 ++# define winShmSize 0 ++# define winShmGet 0 ++# define winShmRelease 0 ++# define winShmLock 0 ++# define winShmClose 0 ++#endif /* #ifndef SQLITE_OMIT_WAL */ +/* - ** A global array of all winShmFile objects. - ** - ** The winShmMutexHeld() must be true while reading or writing this list. - */ - static winShmFile *winShmFileList = 0; ++***************************** End Shared Memory ***************************** ++****************************************************************************/ + /* - ** Structure used internally by this VFS to record the state of an - ** open shared memory connection. - ** - ** winShm.pFile->mutex must be held while reading or writing the - ** winShm.pNext and winShm.locks[] elements. - ** - ** The winShm.pFile element is initialized when the object is created - ** and is read-only thereafter. + ** This vector defines all the methods that can operate on an + ** sqlite3_file for win32. */ - struct winShm { - winShmFile *pFile; /* The underlying winShmFile object */ - winShm *pNext; /* Next winShm with the same winShmFile */ - u8 lockState; /* Current lock state */ - u8 hasMutex; /* True if holding the winShmFile mutex */ - u8 hasMutexBuf; /* True if holding pFile->mutexBuf */ - u8 sharedMask; /* Mask of shared locks held */ - u8 exclMask; /* Mask of exclusive locks held */ - #ifdef SQLITE_DEBUG - u8 id; /* Id of this connection with its winShmFile */ - #endif + static const sqlite3_io_methods winIoMethod = { - 1, /* iVersion */ ++ 2, /* iVersion */ + winClose, + winRead, + winWrite, + winTruncate, + winSync, + winFileSize, + winLock, + winUnlock, + winCheckReservedLock, + winFileControl, + winSectorSize, - winDeviceCharacteristics ++ winDeviceCharacteristics, ++ winShmOpen, /* xShmOpen */ ++ winShmSize, /* xShmSize */ ++ winShmGet, /* xShmGet */ ++ winShmRelease, /* xShmRelease */ ++ winShmLock, /* xShmLock */ ++ winShmClose /* xShmClose */ }; - /* - ** Size increment by which shared memory grows - */ - #define SQLITE_WIN_SHM_INCR 4096 + /*************************************************************************** + ** Here ends the I/O methods that form the sqlite3_io_methods object. + ** + ** The next block of code implements the VFS methods. + ****************************************************************************/ /* - ** Constants used for locking + ** Convert a UTF-8 filename into whatever form the underlying + ** operating system wants filenames in. Space to hold the result + ** is obtained from malloc and must be freed by the calling + ** function. */ - #define WIN_SHM_BASE 32 /* Byte offset of the first lock byte */ - #define WIN_SHM_DMS 0x01 /* Mask for Dead-Man-Switch lock */ - #define WIN_SHM_A 0x10 /* Mask for region locks... */ - #define WIN_SHM_B 0x20 - #define WIN_SHM_C 0x40 - #define WIN_SHM_D 0x80 - - #ifdef SQLITE_DEBUG - /* - ** Return a pointer to a nul-terminated string in static memory that - ** describes a locking mask. The string is of the form "MSABCD" with - ** each character representing a lock. "M" for MUTEX, "S" for DMS, - ** and "A" through "D" for the region locks. If a lock is held, the - ** letter is shown. If the lock is not held, the letter is converted - ** to ".". - ** - ** This routine is for debugging purposes only and does not appear - ** in a production build. + static void *convertUtf8Filename(const char *zFilename){ + void *zConverted = 0; + if( isNT() ){ + zConverted = utf8ToUnicode(zFilename); + /* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed. */ - static const char *winShmLockString(u8 mask){ - static char zBuf[48]; - static int iBuf = 0; - char *z; - - z = &zBuf[iBuf]; - iBuf += 8; - if( iBuf>=sizeof(zBuf) ) iBuf = 0; - - z[0] = (mask & WIN_SHM_DMS) ? 'S' : '.'; - z[1] = (mask & WIN_SHM_A) ? 'A' : '.'; - z[2] = (mask & WIN_SHM_B) ? 'B' : '.'; - z[3] = (mask & WIN_SHM_C) ? 'C' : '.'; - z[4] = (mask & WIN_SHM_D) ? 'D' : '.'; - z[5] = 0; - return z; + #if SQLITE_OS_WINCE==0 + }else{ + zConverted = utf8ToMbcs(zFilename); + #endif + } + /* caller will handle out of memory */ + return zConverted; } - #endif /* SQLITE_DEBUG */ /* - ** Apply posix advisory locks for all bytes identified in lockMask. - ** - ** lockMask might contain multiple bits but all bits are guaranteed - ** to be contiguous. - ** - ** Locks block if the mask is exactly WIN_SHM_C and are non-blocking - ** otherwise. + ** Create a temporary file name in zBuf. zBuf must be big enough to + ** hold at pVfs->mxPathname characters. */ - #define _SHM_UNLCK 1 - #define _SHM_RDLCK 2 - #define _SHM_WRLCK 3 - static int winShmSystemLock( - winShmFile *pFile, /* Apply locks to this open shared-memory segment */ - int lockType, /* _SHM_UNLCK, _SHM_RDLCK, or _SHM_WRLCK */ - u8 lockMask /* Which bytes to lock or unlock */ - ){ - OVERLAPPED ovlp; - DWORD dwFlags; - int nBytes; /* Number of bytes to lock */ - int i; /* Offset into the locking byte range */ - int rc = 0; /* Result code form Lock/UnlockFileEx() */ - u8 mask; /* Mask of bits in lockMask */ - - /* Access to the winShmFile object is serialized by the caller */ - assert( sqlite3_mutex_held(pFile->mutex) || pFile->nRef==0 ); - - /* Initialize the locking parameters */ - if( lockMask==WIN_SHM_C && lockType!=_SHM_UNLCK ){ - dwFlags = 0; - OSTRACE(("SHM-LOCK %d requesting blocking lock %s\n", - pFile->hFile.h, - winShmLockString(lockMask))); + static int getTempname(int nBuf, char *zBuf){ + static char zChars[] = + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789"; + size_t i, j; + char zTempPath[MAX_PATH+1]; + if( sqlite3_temp_directory ){ + sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", sqlite3_temp_directory); + }else if( isNT() ){ + char *zMulti; + WCHAR zWidePath[MAX_PATH]; + GetTempPathW(MAX_PATH-30, zWidePath); + zMulti = unicodeToUtf8(zWidePath); + if( zMulti ){ + sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", zMulti); + free(zMulti); + }else{ + return SQLITE_NOMEM; + } + /* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed. + ** Since the ASCII version of these Windows API do not exist for WINCE, + ** it's important to not reference them for WINCE builds. + */ + #if SQLITE_OS_WINCE==0 }else{ - dwFlags = LOCKFILE_FAIL_IMMEDIATELY; - OSTRACE(("SHM-LOCK %d requesting %s %s\n", - pFile->hFile.h, - lockType!=_SHM_UNLCK ? "lock" : "unlock", - winShmLockString(lockMask))); + char *zUtf8; + char zMbcsPath[MAX_PATH]; + GetTempPathA(MAX_PATH-30, zMbcsPath); + zUtf8 = sqlite3_win32_mbcs_to_utf8(zMbcsPath); + if( zUtf8 ){ + sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", zUtf8); + free(zUtf8); + }else{ + return SQLITE_NOMEM; + } + #endif } - if( lockType == _SHM_WRLCK ) dwFlags |= LOCKFILE_EXCLUSIVE_LOCK; - - /* Find the first bit in lockMask that is set */ - for(i=0, mask=0x01; mask!=0 && (lockMask&mask)==0; mask <<= 1, i++){} - assert( mask!=0 ); - memset(&ovlp, 0, sizeof(OVERLAPPED)); - ovlp.Offset = i+WIN_SHM_BASE; - nBytes = 1; - - /* Extend the locking range for each additional bit that is set */ - mask <<= 1; - while( mask!=0 && (lockMask & mask)!=0 ){ - nBytes++; - mask <<= 1; + for(i=sqlite3Strlen30(zTempPath); i>0 && zTempPath[i-1]=='\\'; i--){} + zTempPath[i] = 0; + sqlite3_snprintf(nBuf-30, zBuf, + "%s\\"SQLITE_TEMP_FILE_PREFIX, zTempPath); + j = sqlite3Strlen30(zBuf); + sqlite3_randomness(20, &zBuf[j]); + for(i=0; i<20; i++, j++){ + zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; } + zBuf[j] = 0; + OSTRACE(("TEMP FILENAME: %s\n", zBuf)); + return SQLITE_OK; + } - /* Verify that all bits set in lockMask are contiguous */ - assert( mask==0 || (lockMask & ~(mask | (mask-1)))==0 ); + /* + ** The return value of getLastErrorMsg + ** is zero if the error message fits in the buffer, or non-zero + ** otherwise (if the message was truncated). + */ + static int getLastErrorMsg(int nBuf, char *zBuf){ + /* FormatMessage returns 0 on failure. Otherwise it + ** returns the number of TCHARs written to the output + ** buffer, excluding the terminating null char. + */ + DWORD error = GetLastError(); + DWORD dwLen = 0; + char *zOut = 0; - /* Release/Acquire the system-level lock */ - if( lockType==_SHM_UNLCK ){ - for(i=0; ihFile.h, 0, 1, 0, &ovlp); - if( !rc ) break; + if( isNT() ){ + WCHAR *zTempWide = NULL; + dwLen = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + error, + 0, + (LPWSTR) &zTempWide, + 0, + 0); + if( dwLen > 0 ){ + /* allocate a buffer and convert to UTF8 */ + zOut = unicodeToUtf8(zTempWide); + /* free the system buffer allocated by FormatMessage */ + LocalFree(zTempWide); } + /* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed. + ** Since the ASCII version of these Windows API do not exist for WINCE, + ** it's important to not reference them for WINCE builds. + */ + #if SQLITE_OS_WINCE==0 }else{ - /* release old individual byte locks (if any) - ** and set new individual byte locks */ - for(i=0; ihFile.h, 0, 1, 0, &ovlp); - rc = LockFileEx(pFile->hFile.h, dwFlags, 0, 1, 0, &ovlp); - if( !rc ) break; - } - } - if( !rc ){ - OSTRACE(("SHM-LOCK %d %s ERROR 0x%08lx\n", - pFile->hFile.h, - lockType==_SHM_UNLCK ? "UnlockFileEx" : "LockFileEx", GetLastError())); - /* release individual byte locks (if any) */ - ovlp.Offset-=i; - for(i=0; ihFile.h, 0, 1, 0, &ovlp); + char *zTemp = NULL; + dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + error, + 0, + (LPSTR) &zTemp, + 0, + 0); + if( dwLen > 0 ){ + /* allocate a buffer and convert to UTF8 */ + zOut = sqlite3_win32_mbcs_to_utf8(zTemp); + /* free the system buffer allocated by FormatMessage */ + LocalFree(zTemp); } + #endif } - rc = (rc!=0) ? SQLITE_OK : SQLITE_BUSY; - - /* Update the global lock state and do debug tracing */ - #ifdef SQLITE_DEBUG - OSTRACE(("SHM-LOCK %d ", pFile->hFile.h)); - if( rc==SQLITE_OK ){ - if( lockType==_SHM_UNLCK ){ - OSTRACE(("unlock ok")); - pFile->exclMask &= ~lockMask; - pFile->sharedMask &= ~lockMask; - }else if( lockType==_SHM_RDLCK ){ - OSTRACE(("read-lock ok")); - pFile->exclMask &= ~lockMask; - pFile->sharedMask |= lockMask; - }else{ - assert( lockType==_SHM_WRLCK ); - OSTRACE(("write-lock ok")); - pFile->exclMask |= lockMask; - pFile->sharedMask &= ~lockMask; - } + if( 0 == dwLen ){ + sqlite3_snprintf(nBuf, zBuf, "OsError 0x%x (%u)", error, error); }else{ - if( lockType==_SHM_UNLCK ){ - OSTRACE(("unlock failed")); - }else if( lockType==_SHM_RDLCK ){ - OSTRACE(("read-lock failed")); - }else{ - assert( lockType==_SHM_WRLCK ); - OSTRACE(("write-lock failed")); - } + /* copy a maximum of nBuf chars to output buffer */ + sqlite3_snprintf(nBuf, zBuf, "%s", zOut); + /* free the UTF8 buffer */ + free(zOut); } - OSTRACE((" - change requested %s - afterwards %s:%s\n", - winShmLockString(lockMask), - winShmLockString(pFile->sharedMask), - winShmLockString(pFile->exclMask))); - #endif - - return rc; + return 0; } /* - ** For connection p, unlock all of the locks identified by the unlockMask - ** parameter. + ** Open a file. */ - static int winShmUnlock( - winShmFile *pFile, /* The underlying shared-memory file */ - winShm *p, /* The connection to be unlocked */ - u8 unlockMask /* Mask of locks to be unlocked */ + static 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 */ ){ - int rc; /* Result code */ - winShm *pX; /* For looping over all sibling connections */ - u8 allMask; /* Union of locks held by connections other than "p" */ + HANDLE h; + DWORD dwDesiredAccess; + DWORD dwShareMode; + DWORD dwCreationDisposition; + DWORD dwFlagsAndAttributes = 0; + #if SQLITE_OS_WINCE + int isTemp = 0; + #endif + winFile *pFile = (winFile*)id; + void *zConverted; /* Filename in OS encoding */ + const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */ + char zTmpname[MAX_PATH+1]; /* Buffer used to create temp filename */ - /* Access to the winShmFile object is serialized by the caller */ - assert( sqlite3_mutex_held(pFile->mutex) ); + assert( id!=0 ); + UNUSED_PARAMETER(pVfs); - /* don't attempt to unlock anything we don't have locks for */ - if( (unlockMask & (p->exclMask|p->sharedMask)) != unlockMask ){ - OSTRACE(("SHM-LOCK %d unlocking more than we have locked - requested %s - have %s\n", - pFile->hFile.h, - winShmLockString(unlockMask), - winShmLockString(p->exclMask|p->sharedMask))); - unlockMask &= (p->exclMask|p->sharedMask); ++ pFile->h = INVALID_HANDLE_VALUE; ++ + /* If the second argument to this function is NULL, generate a + ** temporary file name to use + */ + if( !zUtf8Name ){ + int rc = getTempname(MAX_PATH+1, zTmpname); + if( rc!=SQLITE_OK ){ + return rc; + } + zUtf8Name = zTmpname; } - /* Compute locks held by sibling connections */ - allMask = 0; - for(pX=pFile->pFirst; pX; pX=pX->pNext){ - if( pX==p ) continue; - assert( (pX->exclMask & (p->exclMask|p->sharedMask))==0 ); - allMask |= pX->sharedMask; + /* Convert the filename to the system encoding. */ + zConverted = convertUtf8Filename(zUtf8Name); + if( zConverted==0 ){ + return SQLITE_NOMEM; } - /* Unlock the system-level locks */ - if( (unlockMask & allMask)!=unlockMask ){ - rc = winShmSystemLock(pFile, _SHM_UNLCK, unlockMask & ~allMask); + if( flags & SQLITE_OPEN_READWRITE ){ + dwDesiredAccess = GENERIC_READ | GENERIC_WRITE; }else{ - rc = SQLITE_OK; + dwDesiredAccess = GENERIC_READ; } - - /* Undo the local locks */ - if( rc==SQLITE_OK ){ - p->exclMask &= ~unlockMask; - p->sharedMask &= ~unlockMask; - } - return rc; - } - - /* - ** Get reader locks for connection p on all locks in the readMask parameter. - */ - static int winShmSharedLock( - winShmFile *pFile, /* The underlying shared-memory file */ - winShm *p, /* The connection to get the shared locks */ - u8 readMask /* Mask of shared locks to be acquired */ - ){ - int rc; /* Result code */ - winShm *pX; /* For looping over all sibling connections */ - u8 allShared; /* Union of locks held by connections other than "p" */ - - /* Access to the winShmFile object is serialized by the caller */ - assert( sqlite3_mutex_held(pFile->mutex) ); - - /* Find out which shared locks are already held by sibling connections. - ** If any sibling already holds an exclusive lock, go ahead and return - ** SQLITE_BUSY. + /* SQLITE_OPEN_EXCLUSIVE is used to make sure that a new file is + ** created. SQLite doesn't use it to indicate "exclusive access" + ** as it is usually understood. */ - allShared = 0; - for(pX=pFile->pFirst; pX; pX=pX->pNext){ - if( pX==p ) continue; - if( (pX->exclMask & readMask)!=0 ) return SQLITE_BUSY; - allShared |= pX->sharedMask; + assert(!(flags & SQLITE_OPEN_EXCLUSIVE) || (flags & SQLITE_OPEN_CREATE)); + if( flags & SQLITE_OPEN_EXCLUSIVE ){ + /* Creates a new file, only if it does not already exist. */ + /* If the file exists, it fails. */ + dwCreationDisposition = CREATE_NEW; + }else if( flags & SQLITE_OPEN_CREATE ){ + /* Open existing file, or create if it doesn't exist */ + dwCreationDisposition = OPEN_ALWAYS; + }else{ + /* Opens a file, only if it exists. */ + dwCreationDisposition = OPEN_EXISTING; } - - /* Get shared locks at the system level, if necessary */ - if( (~allShared) & readMask ){ - rc = winShmSystemLock(pFile, _SHM_RDLCK, readMask); + dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; + if( flags & SQLITE_OPEN_DELETEONCLOSE ){ + #if SQLITE_OS_WINCE + dwFlagsAndAttributes = FILE_ATTRIBUTE_HIDDEN; + isTemp = 1; + #else + dwFlagsAndAttributes = FILE_ATTRIBUTE_TEMPORARY + | FILE_ATTRIBUTE_HIDDEN + | FILE_FLAG_DELETE_ON_CLOSE; + #endif }else{ - rc = SQLITE_OK; + dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL; + } + /* Reports from the internet are that performance is always + ** better if FILE_FLAG_RANDOM_ACCESS is used. Ticket #2699. */ + #if SQLITE_OS_WINCE + dwFlagsAndAttributes |= FILE_FLAG_RANDOM_ACCESS; + #endif + if( isNT() ){ + h = CreateFileW((WCHAR*)zConverted, + dwDesiredAccess, + dwShareMode, + NULL, + dwCreationDisposition, + dwFlagsAndAttributes, + NULL + ); + /* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed. + ** Since the ASCII version of these Windows API do not exist for WINCE, + ** it's important to not reference them for WINCE builds. + */ + #if SQLITE_OS_WINCE==0 + }else{ + h = CreateFileA((char*)zConverted, + dwDesiredAccess, + dwShareMode, + NULL, + dwCreationDisposition, + dwFlagsAndAttributes, + NULL + ); + #endif + } ++ OSTRACE(("OPEN %d %s 0x%lx %s\n", ++ h, zName, dwDesiredAccess, ++ h==INVALID_HANDLE_VALUE ? "failed" : "ok")); + if( h==INVALID_HANDLE_VALUE ){ + free(zConverted); + if( flags & SQLITE_OPEN_READWRITE ){ + return winOpen(pVfs, zName, id, + ((flags|SQLITE_OPEN_READONLY)&~SQLITE_OPEN_READWRITE), pOutFlags); + }else{ + return SQLITE_CANTOPEN_BKPT; + } + } + if( pOutFlags ){ + if( flags & SQLITE_OPEN_READWRITE ){ + *pOutFlags = SQLITE_OPEN_READWRITE; + }else{ + *pOutFlags = SQLITE_OPEN_READONLY; + } + } + memset(pFile, 0, sizeof(*pFile)); + pFile->pMethod = &winIoMethod; + pFile->h = h; + pFile->lastErrno = NO_ERROR; ++ pFile->pVfs = pVfs; ++ pFile->pShm = 0; ++ pFile->zPath = zName; + pFile->sectorSize = getSectorSize(pVfs, zUtf8Name); + #if SQLITE_OS_WINCE + if( (flags & (SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_DB)) == + (SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_DB) + && !winceCreateLock(zName, pFile) + ){ + CloseHandle(h); + free(zConverted); + return SQLITE_CANTOPEN_BKPT; } - - /* Get the local shared locks */ - if( rc==SQLITE_OK ){ - p->sharedMask |= readMask; + if( isTemp ){ + pFile->zDeleteOnClose = zConverted; + }else + #endif + { + free(zConverted); } - return rc; + OpenCounter(+1); + return SQLITE_OK; } /* - ** For connection p, get an exclusive lock on all locks identified in - ** the writeMask parameter. + ** 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 it 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. */ - static int winShmExclusiveLock( - winShmFile *pFile, /* The underlying shared-memory file */ - winShm *p, /* The connection to get the exclusive locks */ - u8 writeMask /* Mask of exclusive locks to be acquired */ + #define MX_DELETION_ATTEMPTS 5 + static int winDelete( + sqlite3_vfs *pVfs, /* Not used on win32 */ + const char *zFilename, /* Name of file to delete */ + int syncDir /* Not used on win32 */ ){ - int rc; /* Result code */ - winShm *pX; /* For looping over all sibling connections */ - - /* Access to the winShmFile object is serialized by the caller */ - assert( sqlite3_mutex_held(pFile->mutex) ); - - /* Make sure no sibling connections hold locks that will block this - ** lock. If any do, return SQLITE_BUSY right away. - */ - for(pX=pFile->pFirst; pX; pX=pX->pNext){ - if( pX==p ) continue; - if( (pX->exclMask & writeMask)!=0 ) return SQLITE_BUSY; - if( (pX->sharedMask & writeMask)!=0 ) return SQLITE_BUSY; + int cnt = 0; + DWORD rc; + DWORD error = 0; + void *zConverted = convertUtf8Filename(zFilename); + UNUSED_PARAMETER(pVfs); + UNUSED_PARAMETER(syncDir); + if( zConverted==0 ){ + return SQLITE_NOMEM; } - - /* Get the exclusive locks at the system level. Then if successful - ** also mark the local connection as being locked. - */ - rc = winShmSystemLock(pFile, _SHM_WRLCK, writeMask); - if( rc==SQLITE_OK ){ - p->sharedMask &= ~writeMask; - p->exclMask |= writeMask; + SimulateIOError(return SQLITE_IOERR_DELETE); + if( isNT() ){ + do{ + DeleteFileW(zConverted); + }while( ( ((rc = GetFileAttributesW(zConverted)) != INVALID_FILE_ATTRIBUTES) + || ((error = GetLastError()) == ERROR_ACCESS_DENIED)) + && (++cnt < MX_DELETION_ATTEMPTS) + && (Sleep(100), 1) ); + /* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed. + ** Since the ASCII version of these Windows API do not exist for WINCE, + ** it's important to not reference them for WINCE builds. + */ + #if SQLITE_OS_WINCE==0 + }else{ + do{ + DeleteFileA(zConverted); + }while( ( ((rc = GetFileAttributesA(zConverted)) != INVALID_FILE_ATTRIBUTES) + || ((error = GetLastError()) == ERROR_ACCESS_DENIED)) + && (++cnt < MX_DELETION_ATTEMPTS) + && (Sleep(100), 1) ); + #endif } - return rc; + free(zConverted); - OSTRACE(("DELETE \"%s\"\n", zFilename)); ++ OSTRACE(("DELETE \"%s\" %s\n", zFilename, ++ ( (rc==INVALID_FILE_ATTRIBUTES) && (error==ERROR_FILE_NOT_FOUND)) ? ++ "ok" : "failed" )); ++ + return ( (rc == INVALID_FILE_ATTRIBUTES) + && (error == ERROR_FILE_NOT_FOUND)) ? SQLITE_OK : SQLITE_IOERR_DELETE; } /* @@@ -2676,192 -1795,124 +2758,137 @@@ static int winSleep(sqlite3_vfs *pVfs, } /* - ** Symbolic names for LOCK states used for debugging. -** The following variable, if set to a non-zero value, becomes the result -** returned from sqlite3OsCurrentTime(). This is used for testing. ++** The following variable, if set to a non-zero value, is interpreted as ++** the number of seconds since 1970 and is used to set the result of ++** sqlite3OsCurrentTime() during testing. */ - #ifdef SQLITE_DEBUG - static const char *azLkName[] = { - "UNLOCK", - "READ", - "READ_FULL", - "WRITE", - "PENDING", - "CHECKPOINT", - "RECOVER" - }; + #ifdef SQLITE_TEST -int sqlite3_current_time = 0; ++int sqlite3_current_time = 0; /* Fake system time in seconds since 1970. */ #endif - /* - ** Change the lock state for a shared-memory segment. -** 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. ++** Find the current time (in Universal Coordinated Time). Write into *piNow ++** the current time and date as a Julian Day number times 86_400_000. In ++** other words, write into *piNow the number of milliseconds since the Julian ++** epoch of noon in Greenwich on November 24, 4714 B.C according to the ++** proleptic Gregorian calendar. ++** ++** On success, return 0. Return 1 if the time and date cannot be found. */ - static int winShmLock( - sqlite3_vfs *pVfs, /* The VFS */ - sqlite3_shm *pSharedMem, /* Pointer from winShmOpen() */ - int desiredLock, /* One of SQLITE_SHM_xxxxx locking states */ - int *pGotLock /* The lock you actually got */ - ){ - winShm *p = (winShm*)pSharedMem; - winShmFile *pFile = p->pFile; - int rc = SQLITE_PROTOCOL; - - UNUSED_PARAMETER(pVfs); - - /* Note that SQLITE_SHM_READ_FULL and SQLITE_SHM_PENDING are never - ** directly requested; they are side effects from requesting - ** SQLITE_SHM_READ and SQLITE_SHM_CHECKPOINT, respectively. -int winCurrentTime(sqlite3_vfs *pVfs, double *prNow){ - FILETIME ft; ++static int winCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *piNow){ + /* FILETIME structure is a 64-bit value representing the number of + 100-nanosecond intervals since January 1, 1601 (= JD 2305813.5). */ - assert( desiredLock==SQLITE_SHM_UNLOCK - || desiredLock==SQLITE_SHM_READ - || desiredLock==SQLITE_SHM_WRITE - || desiredLock==SQLITE_SHM_CHECKPOINT - || desiredLock==SQLITE_SHM_RECOVER ); - sqlite3_int64 timeW; /* Whole days */ - sqlite3_int64 timeF; /* Fractional Days */ - - /* Number of 100-nanosecond intervals in a single day */ - static const sqlite3_int64 ntuPerDay = - 10000000*(sqlite3_int64)86400; - - /* Number of 100-nanosecond intervals in half of a day */ - static const sqlite3_int64 ntuPerHalfDay = - 10000000*(sqlite3_int64)43200; - ++ FILETIME ft; ++ static const sqlite3_int64 winFiletimeEpoch = 23058135*(sqlite3_int64)8640000; ++#ifdef SQLITE_TEST ++ static const sqlite3_int64 unixEpoch = 24405875*(sqlite3_int64)8640000; ++#endif + /* 2^32 - to avoid use of LL and warnings in gcc */ + static const sqlite3_int64 max32BitValue = + (sqlite3_int64)2000000000 + (sqlite3_int64)2000000000 + (sqlite3_int64)294967296; - /* Return directly if this is just a lock state query, or if - ** the connection is already in the desired locking state. - */ - if( desiredLock==p->lockState - || (desiredLock==SQLITE_SHM_READ && p->lockState==SQLITE_SHM_READ_FULL) - ){ - OSTRACE(("SHM-LOCK %d shmid-%d, pid-%d request %s and got %s\n", - pFile->hFile.h, - p->id, (int)GetCurrentProcessId(), azLkName[desiredLock], azLkName[p->lockState])); - if( pGotLock ) *pGotLock = p->lockState; - return SQLITE_OK; + #if SQLITE_OS_WINCE + SYSTEMTIME time; + GetSystemTime(&time); + /* if SystemTimeToFileTime() fails, it returns zero. */ + if (!SystemTimeToFileTime(&time,&ft)){ + return 1; } + #else + GetSystemTimeAsFileTime( &ft ); + #endif - UNUSED_PARAMETER(pVfs); - timeW = (((sqlite3_int64)ft.dwHighDateTime)*max32BitValue) + (sqlite3_int64)ft.dwLowDateTime; - timeF = timeW % ntuPerDay; /* fractional days (100-nanoseconds) */ - timeW = timeW / ntuPerDay; /* whole days */ - timeW = timeW + 2305813; /* add whole days (from 2305813.5) */ - timeF = timeF + ntuPerHalfDay; /* add half a day (from 2305813.5) */ - timeW = timeW + (timeF/ntuPerDay); /* add whole day if half day made one */ - timeF = timeF % ntuPerDay; /* compute new fractional days */ - *prNow = (double)timeW + ((double)timeF / (double)ntuPerDay); + - OSTRACE(("SHM-LOCK %d shmid-%d, pid-%d request %s->%s\n", - pFile->hFile.h, - p->id, (int)GetCurrentProcessId(), azLkName[p->lockState], azLkName[desiredLock])); - - if( desiredLock==SQLITE_SHM_RECOVER && !p->hasMutexBuf ){ - assert( sqlite3_mutex_notheld(pFile->mutex) ); - sqlite3_mutex_enter(pFile->mutexBuf); - p->hasMutexBuf = 1; ++ *piNow = winFiletimeEpoch + ++ ((((sqlite3_int64)ft.dwHighDateTime)*max32BitValue) + ++ (sqlite3_int64)ft.dwLowDateTime)/(sqlite3_int64)1000; ++ + #ifdef SQLITE_TEST + if( sqlite3_current_time ){ - *prNow = ((double)sqlite3_current_time + (double)43200) / (double)86400 + (double)2440587; ++ *piNow = 1000*(sqlite3_int64)sqlite3_current_time + unixEpoch; } - sqlite3_mutex_enter(pFile->mutex); - switch( desiredLock ){ - case SQLITE_SHM_UNLOCK: { - assert( p->lockState!=SQLITE_SHM_RECOVER ); - winShmUnlock(pFile, p, WIN_SHM_A|WIN_SHM_B|WIN_SHM_C|WIN_SHM_D); - rc = SQLITE_OK; - p->lockState = SQLITE_SHM_UNLOCK; - break; - } - case SQLITE_SHM_READ: { - if( p->lockState==SQLITE_SHM_UNLOCK ){ - int nAttempt; - rc = SQLITE_BUSY; - assert( p->lockState==SQLITE_SHM_UNLOCK ); - for(nAttempt=0; nAttempt<5 && rc==SQLITE_BUSY; nAttempt++){ - rc = winShmSharedLock(pFile, p, WIN_SHM_A|WIN_SHM_B); - if( rc==SQLITE_BUSY ){ - rc = winShmSharedLock(pFile, p, WIN_SHM_D); - if( rc==SQLITE_OK ){ - p->lockState = SQLITE_SHM_READ_FULL; - } - }else{ - winShmUnlock(pFile, p, WIN_SHM_B); - p->lockState = SQLITE_SHM_READ; - } - } - }else{ - assert( p->lockState==SQLITE_SHM_WRITE - || p->lockState==SQLITE_SHM_RECOVER ); - rc = winShmSharedLock(pFile, p, WIN_SHM_A); - winShmUnlock(pFile, p, WIN_SHM_C|WIN_SHM_D); - p->lockState = SQLITE_SHM_READ; - } - break; - } - case SQLITE_SHM_WRITE: { - assert( p->lockState==SQLITE_SHM_READ - || p->lockState==SQLITE_SHM_READ_FULL ); - rc = winShmExclusiveLock(pFile, p, WIN_SHM_C|WIN_SHM_D); - if( rc==SQLITE_OK ){ - p->lockState = SQLITE_SHM_WRITE; - } - break; - } - case SQLITE_SHM_CHECKPOINT: { - assert( p->lockState==SQLITE_SHM_UNLOCK - || p->lockState==SQLITE_SHM_PENDING - ); - if( p->lockState==SQLITE_SHM_UNLOCK ){ - rc = winShmExclusiveLock(pFile, p, WIN_SHM_B|WIN_SHM_C); - if( rc==SQLITE_OK ){ - p->lockState = SQLITE_SHM_PENDING; - } - } - if( p->lockState==SQLITE_SHM_PENDING ){ - rc = winShmExclusiveLock(pFile, p, WIN_SHM_A); - if( rc==SQLITE_OK ){ - p->lockState = SQLITE_SHM_CHECKPOINT; - } - } - break; - } - default: { - assert( desiredLock==SQLITE_SHM_RECOVER ); - assert( p->lockState==SQLITE_SHM_READ - || p->lockState==SQLITE_SHM_READ_FULL - ); - assert( sqlite3_mutex_held(pFile->mutexBuf) ); - rc = winShmExclusiveLock(pFile, p, WIN_SHM_C); - if( rc==SQLITE_OK ){ - p->lockState = SQLITE_SHM_RECOVER; - } - break; - } + #endif ++ UNUSED_PARAMETER(pVfs); + return 0; + } + ++/* ++** 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){ ++ int rc; ++ sqlite3_int64 i; ++ rc = winCurrentTimeInt64(pVfs, &i); ++ if( !rc ){ ++ *prNow = i/86400000.0; + } - sqlite3_mutex_leave(pFile->mutex); - OSTRACE(("SHM-LOCK %d shmid-%d, pid-%d got %s\n", - pFile->hFile.h, - p->id, (int)GetCurrentProcessId(), azLkName[p->lockState])); - if( pGotLock ) *pGotLock = p->lockState; + return rc; +} + - #else - # define winShmOpen 0 - # define winShmSize 0 - # define winShmGet 0 - # define winShmRelease 0 - # define winShmLock 0 - # define winShmClose 0 - #endif /* #ifndef SQLITE_OMIT_WAL */ + /* + ** The idea is that this function works like a combination of + ** GetLastError() and FormatMessage() on windows (or errno and + ** strerror_r() on unix). After an error is returned by an OS + ** function, SQLite calls this function with zBuf pointing to + ** a buffer of nBuf bytes. The OS layer should populate the + ** buffer with a nul-terminated UTF-8 encoded error message + ** describing the last IO error to have occurred within the calling + ** thread. + ** + ** If the error message is too large for the supplied buffer, + ** it should be truncated. The return value of xGetLastError + ** is zero if the error message fits in the buffer, or non-zero + ** otherwise (if the message was truncated). If non-zero is returned, + ** then it is not necessary to include the nul-terminator character + ** in the output buffer. + ** + ** Not supplying an error message will have no adverse effect + ** on SQLite. It is fine to have an implementation that never + ** returns an error message: + ** + ** int xGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ + ** assert(zBuf[0]=='\0'); + ** return 0; + ** } + ** + ** However if an error message is supplied, it will be incorporated + ** by sqlite into the error message available to the user using + ** sqlite3_errmsg(), possibly making IO errors easier to debug. + */ + static int winGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ + UNUSED_PARAMETER(pVfs); + return getLastErrorMsg(nBuf, zBuf); + } + + + /* ** Initialize and deinitialize the operating system interface. */ int sqlite3_os_init(void){ static sqlite3_vfs winVfs = { - 1, /* iVersion */ - sizeof(winFile), /* szOsFile */ - MAX_PATH, /* mxPathname */ - 0, /* pNext */ - "win32", /* zName */ - 0, /* pAppData */ - - winOpen, /* xOpen */ - winDelete, /* xDelete */ - winAccess, /* xAccess */ - winFullPathname, /* xFullPathname */ - winDlOpen, /* xDlOpen */ - winDlError, /* xDlError */ - winDlSym, /* xDlSym */ - winDlClose, /* xDlClose */ - winRandomness, /* xRandomness */ - winSleep, /* xSleep */ - winCurrentTime, /* xCurrentTime */ - winGetLastError, /* xGetLastError */ + 2, /* iVersion */ + sizeof(winFile), /* szOsFile */ + MAX_PATH, /* mxPathname */ + 0, /* pNext */ + "win32", /* zName */ + 0, /* pAppData */ + winOpen, /* xOpen */ + winDelete, /* xDelete */ + winAccess, /* xAccess */ + winFullPathname, /* xFullPathname */ + winDlOpen, /* xDlOpen */ + winDlError, /* xDlError */ + winDlSym, /* xDlSym */ + winDlClose, /* xDlClose */ + winRandomness, /* xRandomness */ + winSleep, /* xSleep */ + winCurrentTime, /* xCurrentTime */ + winGetLastError, /* xGetLastError */ - winShmOpen, /* xShmOpen */ - winShmSize, /* xShmSize */ - winShmGet, /* xShmGet */ - winShmRelease, /* xShmRelease */ - winShmLock, /* xShmLock */ - winShmClose, /* xShmClose */ + 0, /* xRename */ + winCurrentTimeInt64, /* xCurrentTimeInt64 */ }; sqlite3_vfs_register(&winVfs, 1);