diff options
| author | Hans-Christoph Steiner <hans@eds.org> | 2012-03-30 20:42:12 -0400 | 
|---|---|---|
| committer | Hans-Christoph Steiner <hans@eds.org> | 2012-03-30 20:42:12 -0400 | 
| commit | 7bb481fda9ecb134804b49c2ce77ca28f7eea583 (patch) | |
| tree | 31b520b9914d3e2453968abe375f2c102772c3dc /src/os_win.c | |
Imported Upstream version 2.0.3
Diffstat (limited to 'src/os_win.c')
| -rw-r--r-- | src/os_win.c | 3204 | 
1 files changed, 3204 insertions, 0 deletions
| diff --git a/src/os_win.c b/src/os_win.c new file mode 100644 index 0000000..4518030 --- /dev/null +++ b/src/os_win.c @@ -0,0 +1,3204 @@ +/* +** 2004 May 22 +** +** The author disclaims copyright to this source code.  In place of +** a legal notice, here is a blessing: +** +**    May you do good and not evil. +**    May you find forgiveness for yourself and forgive others. +**    May you share freely, never taking more than you give. +** +****************************************************************************** +** +** This file contains code that is specific to windows. +*/ +#include "sqliteInt.h" +#if SQLITE_OS_WIN               /* This file is used for windows only */ + + +/* +** A Note About Memory Allocation: +** +** This driver uses malloc()/free() directly rather than going through +** the SQLite-wrappers sqlite3_malloc()/sqlite3_free().  Those wrappers +** are designed for use on embedded systems where memory is scarce and +** malloc failures happen frequently.  Win32 does not typically run on +** embedded systems, and when it does the developers normally have bigger +** problems to worry about than running out of memory.  So there is not +** a compelling need to use the wrappers. +** +** But there is a good reason to not use the wrappers.  If we use the +** wrappers then we will get simulated malloc() failures within this +** driver.  And that causes all kinds of problems for our tests.  We +** could enhance SQLite to deal with simulated malloc failures within +** the OS driver, but the code to deal with those failure would not +** be exercised on Linux (which does not need to malloc() in the driver) +** and so we would have difficulty writing coverage tests for that +** code.  Better to leave the code out, we think. +** +** The point of this discussion is as follows:  When creating a new +** OS layer for an embedded system, if you use this file as an example, +** avoid the use of malloc()/free().  Those routines work ok on windows +** desktops but not so well in embedded systems. +*/ + +#include <winbase.h> + +#ifdef __CYGWIN__ +# include <sys/cygwin.h> +#endif + +/* +** Macros used to determine whether or not to use threads. +*/ +#if defined(THREADSAFE) && THREADSAFE +# define SQLITE_W32_THREADS 1 +#endif + +/* +** Include code that is common to all os_*.c files +*/ +#include "os_common.h" + +/* +** Some microsoft compilers lack this definition. +*/ +#ifndef INVALID_FILE_ATTRIBUTES +# define INVALID_FILE_ATTRIBUTES ((DWORD)-1)  +#endif + +/* +** Determine if we are dealing with WindowsCE - which has a much +** reduced API. +*/ +#if SQLITE_OS_WINCE +# define AreFileApisANSI() 1 +# 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. +*/ +#if SQLITE_OS_WINCE +typedef struct winceLock { +  int nReaders;       /* Number of reader locks obtained */ +  BOOL bPending;      /* Indicates a pending lock has been obtained */ +  BOOL bReserved;     /* Indicates a reserved lock has been obtained */ +  BOOL bExclusive;    /* Indicates an exclusive lock has been obtained */ +} winceLock; +#endif + +/* +** The winFile structure is a subclass of sqlite3_file* specific to the win32 +** portability layer. +*/ +typedef struct winFile winFile; +struct winFile { +  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 */ +  u8 locktype;            /* Type of lock currently held on this file */ +  short sharedLockByte;   /* Randomly chosen byte used as a shared lock */ +  u8 bPersistWal;         /* True to persist WAL files */ +  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 */ +  int szChunk;            /* Chunk size configured by FCNTL_CHUNK_SIZE */ +#if SQLITE_OS_WINCE +  WCHAR *zDeleteOnClose;  /* Name of file to delete when closing */ +  HANDLE hMutex;          /* Mutex used to control access to shared lock */   +  HANDLE hShared;         /* Shared memory segment used for locking */ +  winceLock local;        /* Locks obtained by this instance of winFile */ +  winceLock *shared;      /* Global shared lock memory for the file  */ +#endif +}; + +/* + * If compiled with SQLITE_WIN32_MALLOC on Windows, we will use the + * various Win32 API heap functions instead of our own. + */ +#ifdef SQLITE_WIN32_MALLOC +/* + * The initial size of the Win32-specific heap.  This value may be zero. + */ +#ifndef SQLITE_WIN32_HEAP_INIT_SIZE +#  define SQLITE_WIN32_HEAP_INIT_SIZE ((SQLITE_DEFAULT_CACHE_SIZE) * \ +                                       (SQLITE_DEFAULT_PAGE_SIZE) + 4194304) +#endif + +/* + * The maximum size of the Win32-specific heap.  This value may be zero. + */ +#ifndef SQLITE_WIN32_HEAP_MAX_SIZE +#  define SQLITE_WIN32_HEAP_MAX_SIZE  (0) +#endif + +/* + * The extra flags to use in calls to the Win32 heap APIs.  This value may be + * zero for the default behavior. + */ +#ifndef SQLITE_WIN32_HEAP_FLAGS +#  define SQLITE_WIN32_HEAP_FLAGS     (0) +#endif + +/* +** The winMemData structure stores information required by the Win32-specific +** sqlite3_mem_methods implementation. +*/ +typedef struct winMemData winMemData; +struct winMemData { +#ifndef NDEBUG +  u32 magic;    /* Magic number to detect structure corruption. */ +#endif +  HANDLE hHeap; /* The handle to our heap. */ +  BOOL bOwned;  /* Do we own the heap (i.e. destroy it on shutdown)? */ +}; + +#ifndef NDEBUG +#define WINMEM_MAGIC     0x42b2830b +#endif + +static struct winMemData win_mem_data = { +#ifndef NDEBUG +  WINMEM_MAGIC, +#endif +  NULL, FALSE +}; + +#ifndef NDEBUG +#define winMemAssertMagic() assert( win_mem_data.magic==WINMEM_MAGIC ) +#else +#define winMemAssertMagic() +#endif + +#define winMemGetHeap() win_mem_data.hHeap + +static void *winMemMalloc(int nBytes); +static void winMemFree(void *pPrior); +static void *winMemRealloc(void *pPrior, int nBytes); +static int winMemSize(void *p); +static int winMemRoundup(int n); +static int winMemInit(void *pAppData); +static void winMemShutdown(void *pAppData); + +const sqlite3_mem_methods *sqlite3MemGetWin32(void); +#endif /* SQLITE_WIN32_MALLOC */ + +/* +** Forward prototypes. +*/ +static int getSectorSize( +    sqlite3_vfs *pVfs, +    const char *zRelative     /* UTF-8 file name */ +); + +/* +** The following variable is (normally) set once and never changes +** thereafter.  It records whether the operating system is Win95 +** or WinNT. +** +** 0:   Operating system unknown. +** 1:   Operating system is Win95. +** 2:   Operating system is WinNT. +** +** In order to facilitate testing on a WinNT system, the test fixture +** can manually set this value to 1 to emulate Win98 behavior. +*/ +#ifdef SQLITE_TEST +int sqlite3_os_type = 0; +#else +static int sqlite3_os_type = 0; +#endif + +/* +** Return true (non-zero) if we are running under WinNT, Win2K, WinXP, +** or WinCE.  Return false (zero) for Win95, Win98, or WinME. +** +** Here is an interesting observation:  Win95, Win98, and WinME lack +** the LockFileEx() API.  But we can still statically link against that +** API as long as we don't call it when running Win95/98/ME.  A call to +** this routine is used to determine if the host is Win95/98/ME or +** WinNT/2K/XP so that we will know whether or not we can safely call +** the LockFileEx() API. +*/ +#if SQLITE_OS_WINCE +# define isNT()  (1) +#else +  static int isNT(void){ +    if( sqlite3_os_type==0 ){ +      OSVERSIONINFO sInfo; +      sInfo.dwOSVersionInfoSize = sizeof(sInfo); +      GetVersionEx(&sInfo); +      sqlite3_os_type = sInfo.dwPlatformId==VER_PLATFORM_WIN32_NT ? 2 : 1; +    } +    return sqlite3_os_type==2; +  } +#endif /* SQLITE_OS_WINCE */ + +#ifdef SQLITE_WIN32_MALLOC +/* +** Allocate nBytes of memory. +*/ +static void *winMemMalloc(int nBytes){ +  HANDLE hHeap; +  void *p; + +  winMemAssertMagic(); +  hHeap = winMemGetHeap(); +  assert( hHeap!=0 ); +  assert( hHeap!=INVALID_HANDLE_VALUE ); +#ifdef SQLITE_WIN32_MALLOC_VALIDATE +  assert ( HeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) ); +#endif +  assert( nBytes>=0 ); +  p = HeapAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, (SIZE_T)nBytes); +  if( !p ){ +    sqlite3_log(SQLITE_NOMEM, "failed to HeapAlloc %u bytes (%d), heap=%p", +        nBytes, GetLastError(), (void*)hHeap); +  } +  return p; +} + +/* +** Free memory. +*/ +static void winMemFree(void *pPrior){ +  HANDLE hHeap; + +  winMemAssertMagic(); +  hHeap = winMemGetHeap(); +  assert( hHeap!=0 ); +  assert( hHeap!=INVALID_HANDLE_VALUE ); +#ifdef SQLITE_WIN32_MALLOC_VALIDATE +  assert ( HeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) ); +#endif +  if( !pPrior ) return; /* Passing NULL to HeapFree is undefined. */ +  if( !HeapFree(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) ){ +    sqlite3_log(SQLITE_NOMEM, "failed to HeapFree block %p (%d), heap=%p", +        pPrior, GetLastError(), (void*)hHeap); +  } +} + +/* +** Change the size of an existing memory allocation +*/ +static void *winMemRealloc(void *pPrior, int nBytes){ +  HANDLE hHeap; +  void *p; + +  winMemAssertMagic(); +  hHeap = winMemGetHeap(); +  assert( hHeap!=0 ); +  assert( hHeap!=INVALID_HANDLE_VALUE ); +#ifdef SQLITE_WIN32_MALLOC_VALIDATE +  assert ( HeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) ); +#endif +  assert( nBytes>=0 ); +  if( !pPrior ){ +    p = HeapAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, (SIZE_T)nBytes); +  }else{ +    p = HeapReAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior, (SIZE_T)nBytes); +  } +  if( !p ){ +    sqlite3_log(SQLITE_NOMEM, "failed to %s %u bytes (%d), heap=%p", +        pPrior ? "HeapReAlloc" : "HeapAlloc", nBytes, GetLastError(), +        (void*)hHeap); +  } +  return p; +} + +/* +** Return the size of an outstanding allocation, in bytes. +*/ +static int winMemSize(void *p){ +  HANDLE hHeap; +  SIZE_T n; + +  winMemAssertMagic(); +  hHeap = winMemGetHeap(); +  assert( hHeap!=0 ); +  assert( hHeap!=INVALID_HANDLE_VALUE ); +#ifdef SQLITE_WIN32_MALLOC_VALIDATE +  assert ( HeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) ); +#endif +  if( !p ) return 0; +  n = HeapSize(hHeap, SQLITE_WIN32_HEAP_FLAGS, p); +  if( n==(SIZE_T)-1 ){ +    sqlite3_log(SQLITE_NOMEM, "failed to HeapSize block %p (%d), heap=%p", +        p, GetLastError(), (void*)hHeap); +    return 0; +  } +  return (int)n; +} + +/* +** Round up a request size to the next valid allocation size. +*/ +static int winMemRoundup(int n){ +  return n; +} + +/* +** Initialize this module. +*/ +static int winMemInit(void *pAppData){ +  winMemData *pWinMemData = (winMemData *)pAppData; + +  if( !pWinMemData ) return SQLITE_ERROR; +  assert( pWinMemData->magic==WINMEM_MAGIC ); +  if( !pWinMemData->hHeap ){ +    pWinMemData->hHeap = HeapCreate(SQLITE_WIN32_HEAP_FLAGS, +                                    SQLITE_WIN32_HEAP_INIT_SIZE, +                                    SQLITE_WIN32_HEAP_MAX_SIZE); +    if( !pWinMemData->hHeap ){ +      sqlite3_log(SQLITE_NOMEM, +          "failed to HeapCreate (%d), flags=%u, initSize=%u, maxSize=%u", +          GetLastError(), SQLITE_WIN32_HEAP_FLAGS, SQLITE_WIN32_HEAP_INIT_SIZE, +          SQLITE_WIN32_HEAP_MAX_SIZE); +      return SQLITE_NOMEM; +    } +    pWinMemData->bOwned = TRUE; +  } +  assert( pWinMemData->hHeap!=0 ); +  assert( pWinMemData->hHeap!=INVALID_HANDLE_VALUE ); +#ifdef SQLITE_WIN32_MALLOC_VALIDATE +  assert( HeapValidate(pWinMemData->hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) ); +#endif +  return SQLITE_OK; +} + +/* +** Deinitialize this module. +*/ +static void winMemShutdown(void *pAppData){ +  winMemData *pWinMemData = (winMemData *)pAppData; + +  if( !pWinMemData ) return; +  if( pWinMemData->hHeap ){ +    assert( pWinMemData->hHeap!=INVALID_HANDLE_VALUE ); +#ifdef SQLITE_WIN32_MALLOC_VALIDATE +    assert( HeapValidate(pWinMemData->hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) ); +#endif +    if( pWinMemData->bOwned ){ +      if( !HeapDestroy(pWinMemData->hHeap) ){ +        sqlite3_log(SQLITE_NOMEM, "failed to HeapDestroy (%d), heap=%p", +            GetLastError(), (void*)pWinMemData->hHeap); +      } +      pWinMemData->bOwned = FALSE; +    } +    pWinMemData->hHeap = NULL; +  } +} + +/* +** Populate the low-level memory allocation function pointers in +** sqlite3GlobalConfig.m with pointers to the routines in this file. The +** arguments specify the block of memory to manage. +** +** This routine is only called by sqlite3_config(), and therefore +** is not required to be threadsafe (it is not). +*/ +const sqlite3_mem_methods *sqlite3MemGetWin32(void){ +  static const sqlite3_mem_methods winMemMethods = { +    winMemMalloc, +    winMemFree, +    winMemRealloc, +    winMemSize, +    winMemRoundup, +    winMemInit, +    winMemShutdown, +    &win_mem_data +  }; +  return &winMemMethods; +} + +void sqlite3MemSetDefault(void){ +  sqlite3_config(SQLITE_CONFIG_MALLOC, sqlite3MemGetWin32()); +} +#endif /* SQLITE_WIN32_MALLOC */ + +/* +** Convert a UTF-8 string to microsoft unicode (UTF-16?).  +** +** Space to hold the returned string is obtained from malloc. +*/ +static WCHAR *utf8ToUnicode(const char *zFilename){ +  int nChar; +  WCHAR *zWideFilename; + +  nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, NULL, 0); +  zWideFilename = malloc( nChar*sizeof(zWideFilename[0]) ); +  if( zWideFilename==0 ){ +    return 0; +  } +  nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, zWideFilename, nChar); +  if( nChar==0 ){ +    free(zWideFilename); +    zWideFilename = 0; +  } +  return zWideFilename; +} + +/* +** Convert microsoft unicode to UTF-8.  Space to hold the returned string is +** obtained from malloc(). +*/ +static char *unicodeToUtf8(const WCHAR *zWideFilename){ +  int nByte; +  char *zFilename; + +  nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, 0, 0, 0, 0); +  zFilename = malloc( nByte ); +  if( zFilename==0 ){ +    return 0; +  } +  nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, zFilename, nByte, +                              0, 0); +  if( nByte == 0 ){ +    free(zFilename); +    zFilename = 0; +  } +  return zFilename; +} + +/* +** Convert an ansi string to microsoft unicode, based on the +** current codepage settings for file apis. +**  +** Space to hold the returned string is obtained +** from malloc. +*/ +static WCHAR *mbcsToUnicode(const char *zFilename){ +  int nByte; +  WCHAR *zMbcsFilename; +  int codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP; + +  nByte = MultiByteToWideChar(codepage, 0, zFilename, -1, NULL,0)*sizeof(WCHAR); +  zMbcsFilename = malloc( nByte*sizeof(zMbcsFilename[0]) ); +  if( zMbcsFilename==0 ){ +    return 0; +  } +  nByte = MultiByteToWideChar(codepage, 0, zFilename, -1, zMbcsFilename, nByte); +  if( nByte==0 ){ +    free(zMbcsFilename); +    zMbcsFilename = 0; +  } +  return zMbcsFilename; +} + +/* +** Convert microsoft unicode to multibyte character string, based on the +** user's Ansi codepage. +** +** Space to hold the returned string is obtained from +** malloc(). +*/ +static char *unicodeToMbcs(const WCHAR *zWideFilename){ +  int nByte; +  char *zFilename; +  int codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP; + +  nByte = WideCharToMultiByte(codepage, 0, zWideFilename, -1, 0, 0, 0, 0); +  zFilename = malloc( nByte ); +  if( zFilename==0 ){ +    return 0; +  } +  nByte = WideCharToMultiByte(codepage, 0, zWideFilename, -1, zFilename, nByte, +                              0, 0); +  if( nByte == 0 ){ +    free(zFilename); +    zFilename = 0; +  } +  return zFilename; +} + +/* +** Convert multibyte character string to UTF-8.  Space to hold the +** returned string is obtained from malloc(). +*/ +char *sqlite3_win32_mbcs_to_utf8(const char *zFilename){ +  char *zFilenameUtf8; +  WCHAR *zTmpWide; + +  zTmpWide = mbcsToUnicode(zFilename); +  if( zTmpWide==0 ){ +    return 0; +  } +  zFilenameUtf8 = unicodeToUtf8(zTmpWide); +  free(zTmpWide); +  return zFilenameUtf8; +} + +/* +** Convert UTF-8 to multibyte character string.  Space to hold the  +** returned string is obtained from malloc(). +*/ +char *sqlite3_win32_utf8_to_mbcs(const char *zFilename){ +  char *zFilenameMbcs; +  WCHAR *zTmpWide; + +  zTmpWide = utf8ToUnicode(zFilename); +  if( zTmpWide==0 ){ +    return 0; +  } +  zFilenameMbcs = unicodeToMbcs(zTmpWide); +  free(zTmpWide); +  return zFilenameMbcs; +} + + +/* +** 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; + +  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{ +    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; +} + +/* +** +** This function - winLogErrorAtLine() - is only ever called via the macro +** winLogError(). +** +** This routine is invoked after an error occurs in an OS function. +** It logs a message using sqlite3_log() containing the current value of +** error code and, if possible, the human-readable equivalent from  +** FormatMessage. +** +** The first argument passed to the macro should be the error code that +** will be returned to SQLite (e.g. SQLITE_IOERR_DELETE, SQLITE_CANTOPEN).  +** The two subsequent arguments should be the name of the OS function that +** failed and the the associated file-system path, if any. +*/ +#define winLogError(a,b,c)     winLogErrorAtLine(a,b,c,__LINE__) +static int winLogErrorAtLine( +  int errcode,                    /* SQLite error code */ +  const char *zFunc,              /* Name of OS function that failed */ +  const char *zPath,              /* File path associated with error */ +  int iLine                       /* Source line number where error occurred */ +){ +  char zMsg[500];                 /* Human readable error text */ +  int i;                          /* Loop counter */ +  DWORD iErrno = GetLastError();  /* Error code */ + +  zMsg[0] = 0; +  getLastErrorMsg(sizeof(zMsg), zMsg); +  assert( errcode!=SQLITE_OK ); +  if( zPath==0 ) zPath = ""; +  for(i=0; zMsg[i] && zMsg[i]!='\r' && zMsg[i]!='\n'; i++){} +  zMsg[i] = 0; +  sqlite3_log(errcode, +      "os_win.c:%d: (%d) %s(%s) - %s", +      iLine, iErrno, zFunc, zPath, zMsg +  ); + +  return errcode; +} + +/* +** The number of times that a ReadFile(), WriteFile(), and DeleteFile() +** will be retried following a locking error - probably caused by  +** antivirus software.  Also the initial delay before the first retry. +** The delay increases linearly with each retry. +*/ +#ifndef SQLITE_WIN32_IOERR_RETRY +# define SQLITE_WIN32_IOERR_RETRY 10 +#endif +#ifndef SQLITE_WIN32_IOERR_RETRY_DELAY +# define SQLITE_WIN32_IOERR_RETRY_DELAY 25 +#endif +static int win32IoerrRetry = SQLITE_WIN32_IOERR_RETRY; +static int win32IoerrRetryDelay = SQLITE_WIN32_IOERR_RETRY_DELAY; + +/* +** If a ReadFile() or WriteFile() error occurs, invoke this routine +** to see if it should be retried.  Return TRUE to retry.  Return FALSE +** to give up with an error. +*/ +static int retryIoerr(int *pnRetry){ +  DWORD e; +  if( *pnRetry>=win32IoerrRetry ){ +    return 0; +  } +  e = GetLastError(); +  if( e==ERROR_ACCESS_DENIED || +      e==ERROR_LOCK_VIOLATION || +      e==ERROR_SHARING_VIOLATION ){ +    Sleep(win32IoerrRetryDelay*(1+*pnRetry)); +    ++*pnRetry; +    return 1; +  } +  return 0; +} + +/* +** Log a I/O error retry episode. +*/ +static void logIoerr(int nRetry){ +  if( nRetry ){ +    sqlite3_log(SQLITE_IOERR,  +      "delayed %dms for lock/sharing conflict", +      win32IoerrRetryDelay*nRetry*(nRetry+1)/2 +    ); +  } +} + +#if SQLITE_OS_WINCE +/************************************************************************* +** This section contains code for WinCE only. +*/ +/* +** WindowsCE does not have a localtime() function.  So create a +** substitute. +*/ +#include <time.h> +struct tm *__cdecl localtime(const time_t *t) +{ +  static struct tm y; +  FILETIME uTm, lTm; +  SYSTEMTIME pTm; +  sqlite3_int64 t64; +  t64 = *t; +  t64 = (t64 + 11644473600)*10000000; +  uTm.dwLowDateTime = (DWORD)(t64 & 0xFFFFFFFF); +  uTm.dwHighDateTime= (DWORD)(t64 >> 32); +  FileTimeToLocalFileTime(&uTm,&lTm); +  FileTimeToSystemTime(&lTm,&pTm); +  y.tm_year = pTm.wYear - 1900; +  y.tm_mon = pTm.wMonth - 1; +  y.tm_wday = pTm.wDayOfWeek; +  y.tm_mday = pTm.wDay; +  y.tm_hour = pTm.wHour; +  y.tm_min = pTm.wMinute; +  y.tm_sec = pTm.wSecond; +  return &y; +} + +/* This will never be called, but defined to make the code compile */ +#define GetTempPathA(a,b) + +#define LockFile(a,b,c,d,e)       winceLockFile(&a, b, c, d, e) +#define UnlockFile(a,b,c,d,e)     winceUnlockFile(&a, b, c, d, e) +#define LockFileEx(a,b,c,d,e,f)   winceLockFileEx(&a, b, c, d, e, f) + +#define HANDLE_TO_WINFILE(a) (winFile*)&((char*)a)[-(int)offsetof(winFile,h)] + +/* +** Acquire a lock on the handle h +*/ +static void winceMutexAcquire(HANDLE h){ +   DWORD dwErr; +   do { +     dwErr = WaitForSingleObject(h, INFINITE); +   } while (dwErr != WAIT_OBJECT_0 && dwErr != WAIT_ABANDONED); +} +/* +** Release a lock acquired by winceMutexAcquire() +*/ +#define winceMutexRelease(h) ReleaseMutex(h) + +/* +** Create the mutex and shared memory used for locking in the file +** descriptor pFile +*/ +static BOOL winceCreateLock(const char *zFilename, winFile *pFile){ +  WCHAR *zTok; +  WCHAR *zName = utf8ToUnicode(zFilename); +  BOOL bInit = TRUE; + +  /* Initialize the local lockdata */ +  ZeroMemory(&pFile->local, sizeof(pFile->local)); + +  /* Replace the backslashes from the filename and lowercase it +  ** to derive a mutex name. */ +  zTok = CharLowerW(zName); +  for (;*zTok;zTok++){ +    if (*zTok == '\\') *zTok = '_'; +  } + +  /* Create/open the named mutex */ +  pFile->hMutex = CreateMutexW(NULL, FALSE, zName); +  if (!pFile->hMutex){ +    pFile->lastErrno = GetLastError(); +    winLogError(SQLITE_ERROR, "winceCreateLock1", zFilename); +    free(zName); +    return FALSE; +  } + +  /* Acquire the mutex before continuing */ +  winceMutexAcquire(pFile->hMutex); +   +  /* Since the names of named mutexes, semaphores, file mappings etc are  +  ** case-sensitive, take advantage of that by uppercasing the mutex name +  ** and using that as the shared filemapping name. +  */ +  CharUpperW(zName); +  pFile->hShared = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, +                                       PAGE_READWRITE, 0, sizeof(winceLock), +                                       zName);   + +  /* Set a flag that indicates we're the first to create the memory so it  +  ** must be zero-initialized */ +  if (GetLastError() == ERROR_ALREADY_EXISTS){ +    bInit = FALSE; +  } + +  free(zName); + +  /* If we succeeded in making the shared memory handle, map it. */ +  if (pFile->hShared){ +    pFile->shared = (winceLock*)MapViewOfFile(pFile->hShared,  +             FILE_MAP_READ|FILE_MAP_WRITE, 0, 0, sizeof(winceLock)); +    /* If mapping failed, close the shared memory handle and erase it */ +    if (!pFile->shared){ +      pFile->lastErrno = GetLastError(); +      winLogError(SQLITE_ERROR, "winceCreateLock2", zFilename); +      CloseHandle(pFile->hShared); +      pFile->hShared = NULL; +    } +  } + +  /* If shared memory could not be created, then close the mutex and fail */ +  if (pFile->hShared == NULL){ +    winceMutexRelease(pFile->hMutex); +    CloseHandle(pFile->hMutex); +    pFile->hMutex = NULL; +    return FALSE; +  } +   +  /* Initialize the shared memory if we're supposed to */ +  if (bInit) { +    ZeroMemory(pFile->shared, sizeof(winceLock)); +  } + +  winceMutexRelease(pFile->hMutex); +  return TRUE; +} + +/* +** Destroy the part of winFile that deals with wince locks +*/ +static void winceDestroyLock(winFile *pFile){ +  if (pFile->hMutex){ +    /* Acquire the mutex */ +    winceMutexAcquire(pFile->hMutex); + +    /* The following blocks should probably assert in debug mode, but they +       are to cleanup in case any locks remained open */ +    if (pFile->local.nReaders){ +      pFile->shared->nReaders --; +    } +    if (pFile->local.bReserved){ +      pFile->shared->bReserved = FALSE; +    } +    if (pFile->local.bPending){ +      pFile->shared->bPending = FALSE; +    } +    if (pFile->local.bExclusive){ +      pFile->shared->bExclusive = FALSE; +    } + +    /* De-reference and close our copy of the shared memory handle */ +    UnmapViewOfFile(pFile->shared); +    CloseHandle(pFile->hShared); + +    /* Done with the mutex */ +    winceMutexRelease(pFile->hMutex);     +    CloseHandle(pFile->hMutex); +    pFile->hMutex = NULL; +  } +} + +/*  +** An implementation of the LockFile() API of windows for wince +*/ +static BOOL winceLockFile( +  HANDLE *phFile, +  DWORD dwFileOffsetLow, +  DWORD dwFileOffsetHigh, +  DWORD nNumberOfBytesToLockLow, +  DWORD nNumberOfBytesToLockHigh +){ +  winFile *pFile = HANDLE_TO_WINFILE(phFile); +  BOOL bReturn = FALSE; + +  UNUSED_PARAMETER(dwFileOffsetHigh); +  UNUSED_PARAMETER(nNumberOfBytesToLockHigh); + +  if (!pFile->hMutex) return TRUE; +  winceMutexAcquire(pFile->hMutex); + +  /* Wanting an exclusive lock? */ +  if (dwFileOffsetLow == (DWORD)SHARED_FIRST +       && nNumberOfBytesToLockLow == (DWORD)SHARED_SIZE){ +    if (pFile->shared->nReaders == 0 && pFile->shared->bExclusive == 0){ +       pFile->shared->bExclusive = TRUE; +       pFile->local.bExclusive = TRUE; +       bReturn = TRUE; +    } +  } + +  /* Want a read-only lock? */ +  else if (dwFileOffsetLow == (DWORD)SHARED_FIRST && +           nNumberOfBytesToLockLow == 1){ +    if (pFile->shared->bExclusive == 0){ +      pFile->local.nReaders ++; +      if (pFile->local.nReaders == 1){ +        pFile->shared->nReaders ++; +      } +      bReturn = TRUE; +    } +  } + +  /* Want a pending lock? */ +  else if (dwFileOffsetLow == (DWORD)PENDING_BYTE && nNumberOfBytesToLockLow == 1){ +    /* If no pending lock has been acquired, then acquire it */ +    if (pFile->shared->bPending == 0) { +      pFile->shared->bPending = TRUE; +      pFile->local.bPending = TRUE; +      bReturn = TRUE; +    } +  } + +  /* Want a reserved lock? */ +  else if (dwFileOffsetLow == (DWORD)RESERVED_BYTE && nNumberOfBytesToLockLow == 1){ +    if (pFile->shared->bReserved == 0) { +      pFile->shared->bReserved = TRUE; +      pFile->local.bReserved = TRUE; +      bReturn = TRUE; +    } +  } + +  winceMutexRelease(pFile->hMutex); +  return bReturn; +} + +/* +** An implementation of the UnlockFile API of windows for wince +*/ +static BOOL winceUnlockFile( +  HANDLE *phFile, +  DWORD dwFileOffsetLow, +  DWORD dwFileOffsetHigh, +  DWORD nNumberOfBytesToUnlockLow, +  DWORD nNumberOfBytesToUnlockHigh +){ +  winFile *pFile = HANDLE_TO_WINFILE(phFile); +  BOOL bReturn = FALSE; + +  UNUSED_PARAMETER(dwFileOffsetHigh); +  UNUSED_PARAMETER(nNumberOfBytesToUnlockHigh); + +  if (!pFile->hMutex) return TRUE; +  winceMutexAcquire(pFile->hMutex); + +  /* Releasing a reader lock or an exclusive lock */ +  if (dwFileOffsetLow == (DWORD)SHARED_FIRST){ +    /* Did we have an exclusive lock? */ +    if (pFile->local.bExclusive){ +      assert(nNumberOfBytesToUnlockLow == (DWORD)SHARED_SIZE); +      pFile->local.bExclusive = FALSE; +      pFile->shared->bExclusive = FALSE; +      bReturn = TRUE; +    } + +    /* Did we just have a reader lock? */ +    else if (pFile->local.nReaders){ +      assert(nNumberOfBytesToUnlockLow == (DWORD)SHARED_SIZE || nNumberOfBytesToUnlockLow == 1); +      pFile->local.nReaders --; +      if (pFile->local.nReaders == 0) +      { +        pFile->shared->nReaders --; +      } +      bReturn = TRUE; +    } +  } + +  /* Releasing a pending lock */ +  else if (dwFileOffsetLow == (DWORD)PENDING_BYTE && nNumberOfBytesToUnlockLow == 1){ +    if (pFile->local.bPending){ +      pFile->local.bPending = FALSE; +      pFile->shared->bPending = FALSE; +      bReturn = TRUE; +    } +  } +  /* Releasing a reserved lock */ +  else if (dwFileOffsetLow == (DWORD)RESERVED_BYTE && nNumberOfBytesToUnlockLow == 1){ +    if (pFile->local.bReserved) { +      pFile->local.bReserved = FALSE; +      pFile->shared->bReserved = FALSE; +      bReturn = TRUE; +    } +  } + +  winceMutexRelease(pFile->hMutex); +  return bReturn; +} + +/* +** An implementation of the LockFileEx() API of windows for wince +*/ +static BOOL winceLockFileEx( +  HANDLE *phFile, +  DWORD dwFlags, +  DWORD dwReserved, +  DWORD nNumberOfBytesToLockLow, +  DWORD nNumberOfBytesToLockHigh, +  LPOVERLAPPED lpOverlapped +){ +  UNUSED_PARAMETER(dwReserved); +  UNUSED_PARAMETER(nNumberOfBytesToLockHigh); + +  /* If the caller wants a shared read lock, forward this call +  ** to winceLockFile */ +  if (lpOverlapped->Offset == (DWORD)SHARED_FIRST && +      dwFlags == 1 && +      nNumberOfBytesToLockLow == (DWORD)SHARED_SIZE){ +    return winceLockFile(phFile, SHARED_FIRST, 0, 1, 0); +  } +  return FALSE; +} +/* +** End of the special code for wince +*****************************************************************************/ +#endif /* SQLITE_OS_WINCE */ + +/***************************************************************************** +** The next group of routines implement the I/O methods specified +** by the sqlite3_io_methods object. +******************************************************************************/ + +/* +** Some microsoft compilers lack this definition. +*/ +#ifndef INVALID_SET_FILE_POINTER +# define INVALID_SET_FILE_POINTER ((DWORD)-1) +#endif + +/* +** Move the current position of the file handle passed as the first  +** argument to offset iOffset within the file. If successful, return 0.  +** Otherwise, set pFile->lastErrno and return non-zero. +*/ +static int seekWinFile(winFile *pFile, sqlite3_int64 iOffset){ +  LONG upperBits;                 /* Most sig. 32 bits of new offset */ +  LONG lowerBits;                 /* Least sig. 32 bits of new offset */ +  DWORD dwRet;                    /* Value returned by SetFilePointer() */ + +  upperBits = (LONG)((iOffset>>32) & 0x7fffffff); +  lowerBits = (LONG)(iOffset & 0xffffffff); + +  /* API oddity: If successful, SetFilePointer() returns a dword  +  ** containing the lower 32-bits of the new file-offset. Or, if it fails, +  ** it returns INVALID_SET_FILE_POINTER. However according to MSDN,  +  ** INVALID_SET_FILE_POINTER may also be a valid new offset. So to determine  +  ** whether an error has actually occured, it is also necessary to call  +  ** GetLastError(). +  */ +  dwRet = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN); +  if( (dwRet==INVALID_SET_FILE_POINTER && GetLastError()!=NO_ERROR) ){ +    pFile->lastErrno = GetLastError(); +    winLogError(SQLITE_IOERR_SEEK, "seekWinFile", pFile->zPath); +    return 1; +  } + +  return 0; +} + +/* +** Close a file. +** +** It is reported that an attempt to close a handle might sometimes +** fail.  This is a very unreasonable result, but windows is notorious +** for being unreasonable so I do not doubt that it might happen.  If +** the close fails, we pause for 100 milliseconds and try again.  As +** many as MX_CLOSE_ATTEMPT attempts to close the handle are made before +** giving up and returning an error. +*/ +#define MX_CLOSE_ATTEMPT 3 +static int winClose(sqlite3_file *id){ +  int rc, cnt = 0; +  winFile *pFile = (winFile*)id; + +  assert( id!=0 ); +  assert( pFile->pShm==0 ); +  OSTRACE(("CLOSE %d\n", pFile->h)); +  do{ +    rc = CloseHandle(pFile->h); +    /* SimulateIOError( rc=0; cnt=MX_CLOSE_ATTEMPT; ); */ +  }while( rc==0 && ++cnt < MX_CLOSE_ATTEMPT && (Sleep(100), 1) ); +#if SQLITE_OS_WINCE +#define WINCE_DELETION_ATTEMPTS 3 +  winceDestroyLock(pFile); +  if( pFile->zDeleteOnClose ){ +    int cnt = 0; +    while( +           DeleteFileW(pFile->zDeleteOnClose)==0 +        && GetFileAttributesW(pFile->zDeleteOnClose)!=0xffffffff  +        && cnt++ < WINCE_DELETION_ATTEMPTS +    ){ +       Sleep(100);  /* Wait a little before trying again */ +    } +    free(pFile->zDeleteOnClose); +  } +#endif +  OSTRACE(("CLOSE %d %s\n", pFile->h, rc ? "ok" : "failed")); +  OpenCounter(-1); +  return rc ? SQLITE_OK +            : winLogError(SQLITE_IOERR_CLOSE, "winClose", pFile->zPath); +} + +/* +** Read data from a file into a buffer.  Return SQLITE_OK if all +** bytes were read successfully and SQLITE_IOERR if anything goes +** wrong. +*/ +static int winRead( +  sqlite3_file *id,          /* File to read from */ +  void *pBuf,                /* Write content into this buffer */ +  int amt,                   /* Number of bytes to read */ +  sqlite3_int64 offset       /* Begin reading at this offset */ +){ +  winFile *pFile = (winFile*)id;  /* file handle */ +  DWORD nRead;                    /* Number of bytes actually read from file */ +  int nRetry = 0;                 /* Number of retrys */ + +  assert( id!=0 ); +  SimulateIOError(return SQLITE_IOERR_READ); +  OSTRACE(("READ %d lock=%d\n", pFile->h, pFile->locktype)); + +  if( seekWinFile(pFile, offset) ){ +    return SQLITE_FULL; +  } +  while( !ReadFile(pFile->h, pBuf, amt, &nRead, 0) ){ +    if( retryIoerr(&nRetry) ) continue; +    pFile->lastErrno = GetLastError(); +    return winLogError(SQLITE_IOERR_READ, "winRead", pFile->zPath); +  } +  logIoerr(nRetry); +  if( nRead<(DWORD)amt ){ +    /* Unread parts of the buffer must be zero-filled */ +    memset(&((char*)pBuf)[nRead], 0, amt-nRead); +    return SQLITE_IOERR_SHORT_READ; +  } + +  return SQLITE_OK; +} + +/* +** Write data from a buffer into a file.  Return SQLITE_OK on success +** or some other error code on failure. +*/ +static int winWrite( +  sqlite3_file *id,               /* File to write into */ +  const void *pBuf,               /* The bytes to be written */ +  int amt,                        /* Number of bytes to write */ +  sqlite3_int64 offset            /* Offset into the file to begin writing at */ +){ +  int rc;                         /* True if error has occured, else false */ +  winFile *pFile = (winFile*)id;  /* File handle */ +  int nRetry = 0;                 /* Number of retries */ + +  assert( amt>0 ); +  assert( pFile ); +  SimulateIOError(return SQLITE_IOERR_WRITE); +  SimulateDiskfullError(return SQLITE_FULL); + +  OSTRACE(("WRITE %d lock=%d\n", pFile->h, pFile->locktype)); + +  rc = seekWinFile(pFile, offset); +  if( rc==0 ){ +    u8 *aRem = (u8 *)pBuf;        /* Data yet to be written */ +    int nRem = amt;               /* Number of bytes yet to be written */ +    DWORD nWrite;                 /* Bytes written by each WriteFile() call */ + +    while( nRem>0 ){ +      if( !WriteFile(pFile->h, aRem, nRem, &nWrite, 0) ){ +        if( retryIoerr(&nRetry) ) continue; +        break; +      } +      if( nWrite<=0 ) break; +      aRem += nWrite; +      nRem -= nWrite; +    } +    if( nRem>0 ){ +      pFile->lastErrno = GetLastError(); +      rc = 1; +    } +  } + +  if( rc ){ +    if(   ( pFile->lastErrno==ERROR_HANDLE_DISK_FULL ) +       || ( pFile->lastErrno==ERROR_DISK_FULL )){ +      return SQLITE_FULL; +    } +    return winLogError(SQLITE_IOERR_WRITE, "winWrite", pFile->zPath); +  }else{ +    logIoerr(nRetry); +  } +  return SQLITE_OK; +} + +/* +** Truncate an open file to a specified size +*/ +static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){ +  winFile *pFile = (winFile*)id;  /* File handle object */ +  int rc = SQLITE_OK;             /* Return code for this function */ + +  assert( pFile ); + +  OSTRACE(("TRUNCATE %d %lld\n", pFile->h, nByte)); +  SimulateIOError(return SQLITE_IOERR_TRUNCATE); + +  /* If the user has configured a chunk-size for this file, truncate the +  ** file so that it consists of an integer number of chunks (i.e. the +  ** actual file size after the operation may be larger than the requested +  ** size). +  */ +  if( pFile->szChunk>0 ){ +    nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk; +  } + +  /* SetEndOfFile() returns non-zero when successful, or zero when it fails. */ +  if( seekWinFile(pFile, nByte) ){ +    rc = winLogError(SQLITE_IOERR_TRUNCATE, "winTruncate1", pFile->zPath); +  }else if( 0==SetEndOfFile(pFile->h) ){ +    pFile->lastErrno = GetLastError(); +    rc = winLogError(SQLITE_IOERR_TRUNCATE, "winTruncate2", pFile->zPath); +  } + +  OSTRACE(("TRUNCATE %d %lld %s\n", pFile->h, nByte, rc ? "failed" : "ok")); +  return rc; +} + +#ifdef SQLITE_TEST +/* +** Count the number of fullsyncs and normal syncs.  This is used to test +** that syncs and fullsyncs are occuring at the right times. +*/ +int sqlite3_sync_count = 0; +int sqlite3_fullsync_count = 0; +#endif + +/* +** Make sure all writes to a particular file are committed to disk. +*/ +static int winSync(sqlite3_file *id, int flags){ +#ifndef SQLITE_NO_SYNC +  /* +  ** Used only when SQLITE_NO_SYNC is not defined. +   */ +  BOOL rc; +#endif +#if !defined(NDEBUG) || !defined(SQLITE_NO_SYNC) || \ +    (defined(SQLITE_TEST) && defined(SQLITE_DEBUG)) +  /* +  ** Used when SQLITE_NO_SYNC is not defined and by the assert() and/or +  ** OSTRACE() macros. +   */ +  winFile *pFile = (winFile*)id; +#else +  UNUSED_PARAMETER(id); +#endif + +  assert( pFile ); +  /* Check that one of SQLITE_SYNC_NORMAL or FULL was passed */ +  assert((flags&0x0F)==SQLITE_SYNC_NORMAL +      || (flags&0x0F)==SQLITE_SYNC_FULL +  ); + +  OSTRACE(("SYNC %d lock=%d\n", pFile->h, pFile->locktype)); + +  /* Unix cannot, but some systems may return SQLITE_FULL from here. This +  ** line is to test that doing so does not cause any problems. +  */ +  SimulateDiskfullError( return SQLITE_FULL ); + +#ifndef SQLITE_TEST +  UNUSED_PARAMETER(flags); +#else +  if( (flags&0x0F)==SQLITE_SYNC_FULL ){ +    sqlite3_fullsync_count++; +  } +  sqlite3_sync_count++; +#endif + +  /* If we compiled with the SQLITE_NO_SYNC flag, then syncing is a +  ** no-op +  */ +#ifdef SQLITE_NO_SYNC +  return SQLITE_OK; +#else +  rc = FlushFileBuffers(pFile->h); +  SimulateIOError( rc=FALSE ); +  if( rc ){ +    return SQLITE_OK; +  }else{ +    pFile->lastErrno = GetLastError(); +    return winLogError(SQLITE_IOERR_FSYNC, "winSync", pFile->zPath); +  } +#endif +} + +/* +** Determine the current size of a file in bytes +*/ +static int winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){ +  DWORD upperBits; +  DWORD lowerBits; +  winFile *pFile = (winFile*)id; +  DWORD error; + +  assert( id!=0 ); +  SimulateIOError(return SQLITE_IOERR_FSTAT); +  lowerBits = GetFileSize(pFile->h, &upperBits); +  if(   (lowerBits == INVALID_FILE_SIZE) +     && ((error = GetLastError()) != NO_ERROR) ) +  { +    pFile->lastErrno = error; +    return winLogError(SQLITE_IOERR_FSTAT, "winFileSize", pFile->zPath); +  } +  *pSize = (((sqlite3_int64)upperBits)<<32) + lowerBits; +  return SQLITE_OK; +} + +/* +** LOCKFILE_FAIL_IMMEDIATELY is undefined on some Windows systems. +*/ +#ifndef LOCKFILE_FAIL_IMMEDIATELY +# define LOCKFILE_FAIL_IMMEDIATELY 1 +#endif + +/* +** Acquire a reader lock. +** Different API routines are called depending on whether or not this +** is Win95 or WinNT. +*/ +static int getReadLock(winFile *pFile){ +  int res; +  if( isNT() ){ +    OVERLAPPED ovlp; +    ovlp.Offset = SHARED_FIRST; +    ovlp.OffsetHigh = 0; +    ovlp.hEvent = 0; +    res = LockFileEx(pFile->h, LOCKFILE_FAIL_IMMEDIATELY, +                     0, SHARED_SIZE, 0, &ovlp); +/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.  +*/ +#if SQLITE_OS_WINCE==0 +  }else{ +    int lk; +    sqlite3_randomness(sizeof(lk), &lk); +    pFile->sharedLockByte = (short)((lk & 0x7fffffff)%(SHARED_SIZE - 1)); +    res = LockFile(pFile->h, SHARED_FIRST+pFile->sharedLockByte, 0, 1, 0); +#endif +  } +  if( res == 0 ){ +    pFile->lastErrno = GetLastError(); +    /* No need to log a failure to lock */ +  } +  return res; +} + +/* +** Undo a readlock +*/ +static int unlockReadLock(winFile *pFile){ +  int res; +  if( isNT() ){ +    res = UnlockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); +/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.  +*/ +#if SQLITE_OS_WINCE==0 +  }else{ +    res = UnlockFile(pFile->h, SHARED_FIRST + pFile->sharedLockByte, 0, 1, 0); +#endif +  } +  if( res==0 && GetLastError()!=ERROR_NOT_LOCKED ){ +    pFile->lastErrno = GetLastError(); +    winLogError(SQLITE_IOERR_UNLOCK, "unlockReadLock", pFile->zPath); +  } +  return res; +} + +/* +** Lock the file with the lock specified by parameter locktype - one +** of the following: +** +**     (1) SHARED_LOCK +**     (2) RESERVED_LOCK +**     (3) PENDING_LOCK +**     (4) EXCLUSIVE_LOCK +** +** Sometimes when requesting one lock state, additional lock states +** are inserted in between.  The locking might fail on one of the later +** transitions leaving the lock state different from what it started but +** still short of its goal.  The following chart shows the allowed +** transitions and the inserted intermediate states: +** +**    UNLOCKED -> SHARED +**    SHARED -> RESERVED +**    SHARED -> (PENDING) -> EXCLUSIVE +**    RESERVED -> (PENDING) -> EXCLUSIVE +**    PENDING -> EXCLUSIVE +** +** This routine will only increase a lock.  The winUnlock() routine +** erases all locks at once and returns us immediately to locking level 0. +** It is not possible to lower the locking level one step at a time.  You +** must go straight to locking level 0. +*/ +static int winLock(sqlite3_file *id, int locktype){ +  int rc = SQLITE_OK;    /* Return code from subroutines */ +  int res = 1;           /* Result of a windows lock call */ +  int newLocktype;       /* Set pFile->locktype to this value before exiting */ +  int gotPendingLock = 0;/* True if we acquired a PENDING lock this time */ +  winFile *pFile = (winFile*)id; +  DWORD error = NO_ERROR; + +  assert( id!=0 ); +  OSTRACE(("LOCK %d %d was %d(%d)\n", +           pFile->h, locktype, pFile->locktype, pFile->sharedLockByte)); + +  /* If there is already a lock of this type or more restrictive on the +  ** OsFile, do nothing. Don't use the end_lock: exit path, as +  ** sqlite3OsEnterMutex() hasn't been called yet. +  */ +  if( pFile->locktype>=locktype ){ +    return SQLITE_OK; +  } + +  /* Make sure the locking sequence is correct +  */ +  assert( pFile->locktype!=NO_LOCK || locktype==SHARED_LOCK ); +  assert( locktype!=PENDING_LOCK ); +  assert( locktype!=RESERVED_LOCK || pFile->locktype==SHARED_LOCK ); + +  /* Lock the PENDING_LOCK byte if we need to acquire a PENDING lock or +  ** a SHARED lock.  If we are acquiring a SHARED lock, the acquisition of +  ** the PENDING_LOCK byte is temporary. +  */ +  newLocktype = pFile->locktype; +  if(   (pFile->locktype==NO_LOCK) +     || (   (locktype==EXCLUSIVE_LOCK) +         && (pFile->locktype==RESERVED_LOCK)) +  ){ +    int cnt = 3; +    while( cnt-->0 && (res = LockFile(pFile->h, PENDING_BYTE, 0, 1, 0))==0 ){ +      /* Try 3 times to get the pending lock.  The pending lock might be +      ** held by another reader process who will release it momentarily. +      */ +      OSTRACE(("could not get a PENDING lock. cnt=%d\n", cnt)); +      Sleep(1); +    } +    gotPendingLock = res; +    if( !res ){ +      error = GetLastError(); +    } +  } + +  /* Acquire a shared lock +  */ +  if( locktype==SHARED_LOCK && res ){ +    assert( pFile->locktype==NO_LOCK ); +    res = getReadLock(pFile); +    if( res ){ +      newLocktype = SHARED_LOCK; +    }else{ +      error = GetLastError(); +    } +  } + +  /* Acquire a RESERVED lock +  */ +  if( locktype==RESERVED_LOCK && res ){ +    assert( pFile->locktype==SHARED_LOCK ); +    res = LockFile(pFile->h, RESERVED_BYTE, 0, 1, 0); +    if( res ){ +      newLocktype = RESERVED_LOCK; +    }else{ +      error = GetLastError(); +    } +  } + +  /* Acquire a PENDING lock +  */ +  if( locktype==EXCLUSIVE_LOCK && res ){ +    newLocktype = PENDING_LOCK; +    gotPendingLock = 0; +  } + +  /* Acquire an EXCLUSIVE lock +  */ +  if( locktype==EXCLUSIVE_LOCK && res ){ +    assert( pFile->locktype>=SHARED_LOCK ); +    res = unlockReadLock(pFile); +    OSTRACE(("unreadlock = %d\n", res)); +    res = LockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); +    if( res ){ +      newLocktype = EXCLUSIVE_LOCK; +    }else{ +      error = GetLastError(); +      OSTRACE(("error-code = %d\n", error)); +      getReadLock(pFile); +    } +  } + +  /* If we are holding a PENDING lock that ought to be released, then +  ** release it now. +  */ +  if( gotPendingLock && locktype==SHARED_LOCK ){ +    UnlockFile(pFile->h, PENDING_BYTE, 0, 1, 0); +  } + +  /* Update the state of the lock has held in the file descriptor then +  ** return the appropriate result code. +  */ +  if( res ){ +    rc = SQLITE_OK; +  }else{ +    OSTRACE(("LOCK FAILED %d trying for %d but got %d\n", pFile->h, +           locktype, newLocktype)); +    pFile->lastErrno = error; +    rc = SQLITE_BUSY; +  } +  pFile->locktype = (u8)newLocktype; +  return rc; +} + +/* +** This routine checks if there is a RESERVED lock held on the specified +** file by this or any other process. If such a lock is held, return +** non-zero, otherwise zero. +*/ +static int winCheckReservedLock(sqlite3_file *id, int *pResOut){ +  int rc; +  winFile *pFile = (winFile*)id; + +  SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); + +  assert( id!=0 ); +  if( pFile->locktype>=RESERVED_LOCK ){ +    rc = 1; +    OSTRACE(("TEST WR-LOCK %d %d (local)\n", pFile->h, rc)); +  }else{ +    rc = LockFile(pFile->h, RESERVED_BYTE, 0, 1, 0); +    if( rc ){ +      UnlockFile(pFile->h, RESERVED_BYTE, 0, 1, 0); +    } +    rc = !rc; +    OSTRACE(("TEST WR-LOCK %d %d (remote)\n", pFile->h, rc)); +  } +  *pResOut = rc; +  return SQLITE_OK; +} + +/* +** Lower the locking level on file descriptor id to locktype.  locktype +** must be either NO_LOCK or SHARED_LOCK. +** +** If the locking level of the file descriptor is already at or below +** the requested locking level, this routine is a no-op. +** +** It is not possible for this routine to fail if the second argument +** is NO_LOCK.  If the second argument is SHARED_LOCK then this routine +** might return SQLITE_IOERR; +*/ +static int winUnlock(sqlite3_file *id, int locktype){ +  int type; +  winFile *pFile = (winFile*)id; +  int rc = SQLITE_OK; +  assert( pFile!=0 ); +  assert( locktype<=SHARED_LOCK ); +  OSTRACE(("UNLOCK %d to %d was %d(%d)\n", pFile->h, locktype, +          pFile->locktype, pFile->sharedLockByte)); +  type = pFile->locktype; +  if( type>=EXCLUSIVE_LOCK ){ +    UnlockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); +    if( locktype==SHARED_LOCK && !getReadLock(pFile) ){ +      /* This should never happen.  We should always be able to +      ** reacquire the read lock */ +      rc = winLogError(SQLITE_IOERR_UNLOCK, "winUnlock", pFile->zPath); +    } +  } +  if( type>=RESERVED_LOCK ){ +    UnlockFile(pFile->h, RESERVED_BYTE, 0, 1, 0); +  } +  if( locktype==NO_LOCK && type>=SHARED_LOCK ){ +    unlockReadLock(pFile); +  } +  if( type>=PENDING_LOCK ){ +    UnlockFile(pFile->h, PENDING_BYTE, 0, 1, 0); +  } +  pFile->locktype = (u8)locktype; +  return rc; +} + +/* +** Control and query of the open file handle. +*/ +static int winFileControl(sqlite3_file *id, int op, void *pArg){ +  winFile *pFile = (winFile*)id; +  switch( op ){ +    case SQLITE_FCNTL_LOCKSTATE: { +      *(int*)pArg = pFile->locktype; +      return SQLITE_OK; +    } +    case SQLITE_LAST_ERRNO: { +      *(int*)pArg = (int)pFile->lastErrno; +      return SQLITE_OK; +    } +    case SQLITE_FCNTL_CHUNK_SIZE: { +      pFile->szChunk = *(int *)pArg; +      return SQLITE_OK; +    } +    case SQLITE_FCNTL_SIZE_HINT: { +      if( pFile->szChunk>0 ){ +        sqlite3_int64 oldSz; +        int rc = winFileSize(id, &oldSz); +        if( rc==SQLITE_OK ){ +          sqlite3_int64 newSz = *(sqlite3_int64*)pArg; +          if( newSz>oldSz ){ +            SimulateIOErrorBenign(1); +            rc = winTruncate(id, newSz); +            SimulateIOErrorBenign(0); +          } +        } +        return rc; +      } +      return SQLITE_OK; +    } +    case SQLITE_FCNTL_PERSIST_WAL: { +      int bPersist = *(int*)pArg; +      if( bPersist<0 ){ +        *(int*)pArg = pFile->bPersistWal; +      }else{ +        pFile->bPersistWal = bPersist!=0; +      } +      return SQLITE_OK; +    } +    case SQLITE_FCNTL_SYNC_OMITTED: { +      return SQLITE_OK; +    } +    case SQLITE_FCNTL_WIN32_AV_RETRY: { +      int *a = (int*)pArg; +      if( a[0]>0 ){ +        win32IoerrRetry = a[0]; +      }else{ +        a[0] = win32IoerrRetry; +      } +      if( a[1]>0 ){ +        win32IoerrRetryDelay = a[1]; +      }else{ +        a[1] = win32IoerrRetryDelay; +      } +      return SQLITE_OK; +    } +  } +  return SQLITE_NOTFOUND; +} + +/* +** Return the sector size in bytes of the underlying block device for +** the specified file. This is almost always 512 bytes, but may be +** larger for some devices. +** +** SQLite code assumes this function cannot fail. It also assumes that +** if two files are created in the same file-system directory (i.e. +** a database and its journal file) that the sector size will be the +** same for both. +*/ +static int winSectorSize(sqlite3_file *id){ +  assert( id!=0 ); +  return (int)(((winFile*)id)->sectorSize); +} + +/* +** Return a vector of device characteristics. +*/ +static int winDeviceCharacteristics(sqlite3_file *id){ +  UNUSED_PARAMETER(id); +  return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN; +} + +#ifndef SQLITE_OMIT_WAL + +/*  +** Windows will only let you create file view mappings +** on allocation size granularity boundaries. +** During sqlite3_os_init() we do a GetSystemInfo() +** to get the granularity size. +*/ +SYSTEM_INFO winSysInfo; + +/* +** 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. +** +** 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() ); +**   winShmLeaveMutex() +*/ +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 + +/* +** 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. +** +*/ +struct winShmNode { +  sqlite3_mutex *mutex;      /* Mutex to access this object */ +  char *zFilename;           /* Name of the file */ +  winFile hFile;             /* File handle from winOpen */ + +  int szRegion;              /* Size of shared-memory regions */ +  int nRegion;               /* Size of array apRegion */ +  struct ShmRegion { +    HANDLE hMap;             /* File handle from CreateFileMapping */ +    void *pMap; +  } *aRegion; +  DWORD lastErrno;           /* The Windows errno from the last I/O error */ + +  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 nextShmId;              /* Next available winShm.id value */ +#endif +}; + +/* +** A global array of all winShmNode objects. +** +** The winShmMutexHeld() must be true while reading or writing this list. +*/ +static winShmNode *winShmNodeList = 0; + +/* +** Structure used internally by this VFS to record the state of an +** open shared memory connection. +** +** The following fields are initialized when this object is created and +** are read-only thereafter: +** +**    winShm.pShmNode +**    winShm.id +** +** All other fields are read/write.  The winShm.pShmNode->mutex must be held +** while accessing any read/write fields. +*/ +struct winShm { +  winShmNode *pShmNode;      /* The underlying winShmNode object */ +  winShm *pNext;             /* Next winShm with the same winShmNode */ +  u8 hasMutex;               /* True if holding the winShmNode mutex */ +  u16 sharedMask;            /* Mask of shared locks held */ +  u16 exclMask;              /* Mask of exclusive locks held */ +#ifdef SQLITE_DEBUG +  u8 id;                     /* Id of this connection with its winShmNode */ +#endif +}; + +/* +** Constants used for locking +*/ +#define WIN_SHM_BASE   ((22+SQLITE_SHM_NLOCK)*4)        /* first lock byte */ +#define WIN_SHM_DMS    (WIN_SHM_BASE+SQLITE_SHM_NLOCK)  /* deadman switch */ + +/* +** Apply advisory locks for all n bytes beginning at ofst. +*/ +#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 */ +  int ofst,             /* Offset to first byte to be locked/unlocked */ +  int nByte             /* Number of bytes to lock or unlock */ +){ +  OVERLAPPED ovlp; +  DWORD dwFlags; +  int rc = 0;           /* Result code form Lock/UnlockFileEx() */ + +  /* Access to the winShmNode object is serialized by the caller */ +  assert( sqlite3_mutex_held(pFile->mutex) || pFile->nRef==0 ); + +  /* Initialize the locking parameters */ +  dwFlags = LOCKFILE_FAIL_IMMEDIATELY; +  if( lockType == _SHM_WRLCK ) dwFlags |= LOCKFILE_EXCLUSIVE_LOCK; + +  memset(&ovlp, 0, sizeof(OVERLAPPED)); +  ovlp.Offset = ofst; + +  /* Release/Acquire the system-level lock */ +  if( lockType==_SHM_UNLCK ){ +    rc = UnlockFileEx(pFile->hFile.h, 0, nByte, 0, &ovlp); +  }else{ +    rc = LockFileEx(pFile->hFile.h, dwFlags, 0, nByte, 0, &ovlp); +  } +   +  if( rc!= 0 ){ +    rc = SQLITE_OK; +  }else{ +    pFile->lastErrno =  GetLastError(); +    rc = SQLITE_BUSY; +  } + +  OSTRACE(("SHM-LOCK %d %s %s 0x%08lx\n",  +           pFile->hFile.h, +           rc==SQLITE_OK ? "ok" : "failed", +           lockType==_SHM_UNLCK ? "UnlockFileEx" : "LockFileEx", +           pFile->lastErrno)); + +  return rc; +} + +/* Forward references to VFS methods */ +static int winOpen(sqlite3_vfs*,const char*,sqlite3_file*,int,int*); +static int winDelete(sqlite3_vfs *,const char*,int); + +/* +** 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. +*/ +static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){ +  winShmNode **pp; +  winShmNode *p; +  BOOL bRc; +  assert( winShmMutexHeld() ); +  pp = &winShmNodeList; +  while( (p = *pp)!=0 ){ +    if( p->nRef==0 ){ +      int i; +      if( p->mutex ) sqlite3_mutex_free(p->mutex); +      for(i=0; i<p->nRegion; i++){ +        bRc = UnmapViewOfFile(p->aRegion[i].pMap); +        OSTRACE(("SHM-PURGE pid-%d unmap region=%d %s\n", +                 (int)GetCurrentProcessId(), i, +                 bRc ? "ok" : "failed")); +        bRc = CloseHandle(p->aRegion[i].hMap); +        OSTRACE(("SHM-PURGE pid-%d close region=%d %s\n", +                 (int)GetCurrentProcessId(), i, +                 bRc ? "ok" : "failed")); +      } +      if( p->hFile.h != INVALID_HANDLE_VALUE ){ +        SimulateIOErrorBenign(1); +        winClose((sqlite3_file *)&p->hFile); +        SimulateIOErrorBenign(0); +      } +      if( deleteFlag ){ +        SimulateIOErrorBenign(1); +        winDelete(pVfs, p->zFilename, 0); +        SimulateIOErrorBenign(0); +      } +      *pp = p->pNext; +      sqlite3_free(p->aRegion); +      sqlite3_free(p); +    }else{ +      pp = &p->pNext; +    } +  } +} + +/* +** Open the shared-memory area associated with database file pDbFd. +** +** 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 winOpenSharedMemory(winFile *pDbFd){ +  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 */ + +  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. +  */ +  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-shm", pDbFd->zPath); +  sqlite3FileSuffix3(pDbFd->zPath, pNew->zFilename);  + +  /* 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; +  } +  if( pShmNode ){ +    sqlite3_free(pNew); +  }else{ +    pShmNode = pNew; +    pNew = 0; +    ((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; +    } + +    rc = winOpen(pDbFd->pVfs, +                 pShmNode->zFilename,             /* Name of the file (UTF-8) */ +                 (sqlite3_file*)&pShmNode->hFile,  /* File handle here */ +                 SQLITE_OPEN_WAL | 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, 1)==SQLITE_OK ){ +      rc = winTruncate((sqlite3_file *)&pShmNode->hFile, 0); +      if( rc!=SQLITE_OK ){ +        rc = winLogError(SQLITE_IOERR_SHMOPEN, "winOpenShm", pDbFd->zPath); +      } +    } +    if( rc==SQLITE_OK ){ +      winShmSystemLock(pShmNode, _SHM_UNLCK, WIN_SHM_DMS, 1); +      rc = winShmSystemLock(pShmNode, _SHM_RDLCK, WIN_SHM_DMS, 1); +    } +    if( rc ) goto shm_open_err; +  } + +  /* Make the new connection a child of the winShmNode */ +  p->pShmNode = pShmNode; +#ifdef SQLITE_DEBUG +  p->id = pShmNode->nextShmId++; +#endif +  pShmNode->nRef++; +  pDbFd->pShm = p; +  winShmLeaveMutex(); + +  /* The reference count on pShmNode has already been incremented under +  ** the cover of the winShmEnterMutex() mutex and the pointer from the +  ** new (struct winShm) object to the pShmNode has been set. All that is +  ** left to do is to link the new object into the linked list starting +  ** at pShmNode->pFirst. This must be done while holding the pShmNode->mutex  +  ** mutex. +  */ +  sqlite3_mutex_enter(pShmNode->mutex); +  p->pNext = pShmNode->pFirst; +  pShmNode->pFirst = p; +  sqlite3_mutex_leave(pShmNode->mutex); +  return SQLITE_OK; + +  /* Jump here on any error */ +shm_open_err: +  winShmSystemLock(pShmNode, _SHM_UNLCK, WIN_SHM_DMS, 1); +  winShmPurge(pDbFd->pVfs, 0);      /* This call frees pShmNode if required */ +  sqlite3_free(p); +  sqlite3_free(pNew); +  winShmLeaveMutex(); +  return rc; +} + +/* +** Close a connection to shared-memory.  Delete the underlying  +** storage if deleteFlag is true. +*/ +static int winShmUnmap( +  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; +  if( p==0 ) return SQLITE_OK; +  pShmNode = p->pShmNode; + +  /* 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 ){ +    winShmPurge(pDbFd->pVfs, deleteFlag); +  } +  winShmLeaveMutex(); + +  return SQLITE_OK; +} + +/* +** Change the lock state for a shared-memory segment. +*/ +static int winShmLock( +  sqlite3_file *fd,          /* Database file holding the shared memory */ +  int ofst,                  /* First lock to acquire or release */ +  int n,                     /* Number of locks to acquire or release */ +  int flags                  /* What to do with the lock */ +){ +  winFile *pDbFd = (winFile*)fd;        /* Connection holding shared memory */ +  winShm *p = pDbFd->pShm;              /* The shared memory being locked */ +  winShm *pX;                           /* For looping over all siblings */ +  winShmNode *pShmNode = p->pShmNode; +  int rc = SQLITE_OK;                   /* Result code */ +  u16 mask;                             /* Mask of locks to take or release */ + +  assert( ofst>=0 && ofst+n<=SQLITE_SHM_NLOCK ); +  assert( n>=1 ); +  assert( flags==(SQLITE_SHM_LOCK | SQLITE_SHM_SHARED) +       || flags==(SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE) +       || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED) +       || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) ); +  assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 ); + +  mask = (u16)((1U<<(ofst+n)) - (1U<<ofst)); +  assert( n>1 || mask==(1<<ofst) ); +  sqlite3_mutex_enter(pShmNode->mutex); +  if( flags & SQLITE_SHM_UNLOCK ){ +    u16 allMask = 0; /* Mask of locks held by siblings */ + +    /* See if any siblings hold this same lock */ +    for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ +      if( pX==p ) continue; +      assert( (pX->exclMask & (p->exclMask|p->sharedMask))==0 ); +      allMask |= pX->sharedMask; +    } + +    /* Unlock the system-level locks */ +    if( (mask & allMask)==0 ){ +      rc = winShmSystemLock(pShmNode, _SHM_UNLCK, ofst+WIN_SHM_BASE, n); +    }else{ +      rc = SQLITE_OK; +    } + +    /* Undo the local locks */ +    if( rc==SQLITE_OK ){ +      p->exclMask &= ~mask; +      p->sharedMask &= ~mask; +    }  +  }else if( flags & SQLITE_SHM_SHARED ){ +    u16 allShared = 0;  /* Union of locks held by connections other than "p" */ + +    /* 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. +    */ +    for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ +      if( (pX->exclMask & mask)!=0 ){ +        rc = SQLITE_BUSY; +        break; +      } +      allShared |= pX->sharedMask; +    } + +    /* Get shared locks at the system level, if necessary */ +    if( rc==SQLITE_OK ){ +      if( (allShared & mask)==0 ){ +        rc = winShmSystemLock(pShmNode, _SHM_RDLCK, ofst+WIN_SHM_BASE, n); +      }else{ +        rc = SQLITE_OK; +      } +    } + +    /* Get the local shared locks */ +    if( rc==SQLITE_OK ){ +      p->sharedMask |= mask; +    } +  }else{ +    /* Make sure no sibling connections hold locks that will block this +    ** lock.  If any do, return SQLITE_BUSY right away. +    */ +    for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ +      if( (pX->exclMask & mask)!=0 || (pX->sharedMask & mask)!=0 ){ +        rc = SQLITE_BUSY; +        break; +      } +    } +   +    /* Get the exclusive locks at the system level.  Then if successful +    ** also mark the local connection as being locked. +    */ +    if( rc==SQLITE_OK ){ +      rc = winShmSystemLock(pShmNode, _SHM_WRLCK, ofst+WIN_SHM_BASE, n); +      if( rc==SQLITE_OK ){ +        assert( (p->sharedMask & mask)==0 ); +        p->exclMask |= mask; +      } +    } +  } +  sqlite3_mutex_leave(pShmNode->mutex); +  OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x %s\n", +           p->id, (int)GetCurrentProcessId(), p->sharedMask, p->exclMask, +           rc ? "failed" : "ok")); +  return rc; +} + +/* +** Implement a memory barrier or memory fence on shared memory.   +** +** All loads and stores begun before the barrier must complete before +** any load or store begun after the barrier. +*/ +static void winShmBarrier( +  sqlite3_file *fd          /* Database holding the shared memory */ +){ +  UNUSED_PARAMETER(fd); +  /* MemoryBarrier(); // does not work -- do not know why not */ +  winShmEnterMutex(); +  winShmLeaveMutex(); +} + +/* +** This function is called to obtain a pointer to region iRegion of the  +** shared-memory associated with the database file fd. Shared-memory regions  +** are numbered starting from zero. Each shared-memory region is szRegion  +** bytes in size. +** +** If an error occurs, an error code is returned and *pp is set to NULL. +** +** Otherwise, if the isWrite parameter is 0 and the requested shared-memory +** region has not been allocated (by any client, including one running in a +** separate process), then *pp is set to NULL and SQLITE_OK returned. If  +** isWrite is non-zero and the requested shared-memory region has not yet  +** been allocated, it is allocated by this function. +** +** If the shared-memory region has already been allocated or is allocated by +** this call as described above, then it is mapped into this processes  +** address space (if it is not already), *pp is set to point to the mapped  +** memory and SQLITE_OK returned. +*/ +static int winShmMap( +  sqlite3_file *fd,               /* Handle open on database file */ +  int iRegion,                    /* Region to retrieve */ +  int szRegion,                   /* Size of regions */ +  int isWrite,                    /* True to extend file if necessary */ +  void volatile **pp              /* OUT: Mapped memory */ +){ +  winFile *pDbFd = (winFile*)fd; +  winShm *p = pDbFd->pShm; +  winShmNode *pShmNode; +  int rc = SQLITE_OK; + +  if( !p ){ +    rc = winOpenSharedMemory(pDbFd); +    if( rc!=SQLITE_OK ) return rc; +    p = pDbFd->pShm; +  } +  pShmNode = p->pShmNode; + +  sqlite3_mutex_enter(pShmNode->mutex); +  assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 ); + +  if( pShmNode->nRegion<=iRegion ){ +    struct ShmRegion *apNew;           /* New aRegion[] array */ +    int nByte = (iRegion+1)*szRegion;  /* Minimum required file size */ +    sqlite3_int64 sz;                  /* Current size of wal-index file */ + +    pShmNode->szRegion = szRegion; + +    /* The requested region is not mapped into this processes address space. +    ** Check to see if it has been allocated (i.e. if the wal-index file is +    ** large enough to contain the requested region). +    */ +    rc = winFileSize((sqlite3_file *)&pShmNode->hFile, &sz); +    if( rc!=SQLITE_OK ){ +      rc = winLogError(SQLITE_IOERR_SHMSIZE, "winShmMap1", pDbFd->zPath); +      goto shmpage_out; +    } + +    if( sz<nByte ){ +      /* The requested memory region does not exist. If isWrite is set to +      ** zero, exit early. *pp will be set to NULL and SQLITE_OK returned. +      ** +      ** Alternatively, if isWrite is non-zero, use ftruncate() to allocate +      ** the requested memory region. +      */ +      if( !isWrite ) goto shmpage_out; +      rc = winTruncate((sqlite3_file *)&pShmNode->hFile, nByte); +      if( rc!=SQLITE_OK ){ +        rc = winLogError(SQLITE_IOERR_SHMSIZE, "winShmMap2", pDbFd->zPath); +        goto shmpage_out; +      } +    } + +    /* Map the requested memory region into this processes address space. */ +    apNew = (struct ShmRegion *)sqlite3_realloc( +        pShmNode->aRegion, (iRegion+1)*sizeof(apNew[0]) +    ); +    if( !apNew ){ +      rc = SQLITE_IOERR_NOMEM; +      goto shmpage_out; +    } +    pShmNode->aRegion = apNew; + +    while( pShmNode->nRegion<=iRegion ){ +      HANDLE hMap;                /* file-mapping handle */ +      void *pMap = 0;             /* Mapped memory region */ +      +      hMap = CreateFileMapping(pShmNode->hFile.h,  +          NULL, PAGE_READWRITE, 0, nByte, NULL +      ); +      OSTRACE(("SHM-MAP pid-%d create region=%d nbyte=%d %s\n", +               (int)GetCurrentProcessId(), pShmNode->nRegion, nByte, +               hMap ? "ok" : "failed")); +      if( hMap ){ +        int iOffset = pShmNode->nRegion*szRegion; +        int iOffsetShift = iOffset % winSysInfo.dwAllocationGranularity; +        pMap = MapViewOfFile(hMap, FILE_MAP_WRITE | FILE_MAP_READ, +            0, iOffset - iOffsetShift, szRegion + iOffsetShift +        ); +        OSTRACE(("SHM-MAP pid-%d map region=%d offset=%d size=%d %s\n", +                 (int)GetCurrentProcessId(), pShmNode->nRegion, iOffset, szRegion, +                 pMap ? "ok" : "failed")); +      } +      if( !pMap ){ +        pShmNode->lastErrno = GetLastError(); +        rc = winLogError(SQLITE_IOERR_SHMMAP, "winShmMap3", pDbFd->zPath); +        if( hMap ) CloseHandle(hMap); +        goto shmpage_out; +      } + +      pShmNode->aRegion[pShmNode->nRegion].pMap = pMap; +      pShmNode->aRegion[pShmNode->nRegion].hMap = hMap; +      pShmNode->nRegion++; +    } +  } + +shmpage_out: +  if( pShmNode->nRegion>iRegion ){ +    int iOffset = iRegion*szRegion; +    int iOffsetShift = iOffset % winSysInfo.dwAllocationGranularity; +    char *p = (char *)pShmNode->aRegion[iRegion].pMap; +    *pp = (void *)&p[iOffsetShift]; +  }else{ +    *pp = 0; +  } +  sqlite3_mutex_leave(pShmNode->mutex); +  return rc; +} + +#else +# define winShmMap     0 +# define winShmLock    0 +# define winShmBarrier 0 +# define winShmUnmap   0 +#endif /* #ifndef SQLITE_OMIT_WAL */ + +/* +** Here ends the implementation of all sqlite3_file methods. +** +********************** End sqlite3_file Methods ******************************* +******************************************************************************/ + +/* +** This vector defines all the methods that can operate on an +** sqlite3_file for win32. +*/ +static const sqlite3_io_methods winIoMethod = { +  2,                              /* iVersion */ +  winClose,                       /* xClose */ +  winRead,                        /* xRead */ +  winWrite,                       /* xWrite */ +  winTruncate,                    /* xTruncate */ +  winSync,                        /* xSync */ +  winFileSize,                    /* xFileSize */ +  winLock,                        /* xLock */ +  winUnlock,                      /* xUnlock */ +  winCheckReservedLock,           /* xCheckReservedLock */ +  winFileControl,                 /* xFileControl */ +  winSectorSize,                  /* xSectorSize */ +  winDeviceCharacteristics,       /* xDeviceCharacteristics */ +  winShmMap,                      /* xShmMap */ +  winShmLock,                     /* xShmLock */ +  winShmBarrier,                  /* xShmBarrier */ +  winShmUnmap                     /* xShmUnmap */ +}; + +/**************************************************************************** +**************************** sqlite3_vfs methods **************************** +** +** This division contains the implementation of methods on the +** sqlite3_vfs object. +*/ + +/* +** 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.  +*/ +#if SQLITE_OS_WINCE==0 +  }else{ +    zConverted = sqlite3_win32_utf8_to_mbcs(zFilename); +#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. +*/ +static int getTempname(int nBuf, char *zBuf){ +  static char zChars[] = +    "abcdefghijklmnopqrstuvwxyz" +    "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +    "0123456789"; +  size_t i, j; +  char zTempPath[MAX_PATH+1]; + +  /* It's odd to simulate an io-error here, but really this is just +  ** using the io-error infrastructure to test that SQLite handles this +  ** function failing.  +  */ +  SimulateIOError( return SQLITE_IOERR ); + +  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{ +    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 +  } + +  /* Check that the output buffer is large enough for the temporary file  +  ** name. If it is not, return SQLITE_ERROR. +  */ +  if( (sqlite3Strlen30(zTempPath) + sqlite3Strlen30(SQLITE_TEMP_FILE_PREFIX) + 17) >= nBuf ){ +    return SQLITE_ERROR; +  } + +  for(i=sqlite3Strlen30(zTempPath); i>0 && zTempPath[i-1]=='\\'; i--){} +  zTempPath[i] = 0; + +  sqlite3_snprintf(nBuf-17, zBuf, +                   "%s\\"SQLITE_TEMP_FILE_PREFIX, zTempPath); +  j = sqlite3Strlen30(zBuf); +  sqlite3_randomness(15, &zBuf[j]); +  for(i=0; i<15; 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;  +} + +/* +** Open a file. +*/ +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 */ +  int cnt = 0; + +  /* If argument zPath is a NULL pointer, this function is required to open +  ** a temporary file. Use this buffer to store the file name in. +  */ +  char zTmpname[MAX_PATH+1];     /* Buffer used to create temp filename */ + +  int rc = SQLITE_OK;            /* Function Return Code */ +#if !defined(NDEBUG) || SQLITE_OS_WINCE +  int eType = flags&0xFFFFFF00;  /* Type of file to open */ +#endif + +  int isExclusive  = (flags & SQLITE_OPEN_EXCLUSIVE); +  int isDelete     = (flags & SQLITE_OPEN_DELETEONCLOSE); +  int isCreate     = (flags & SQLITE_OPEN_CREATE); +#ifndef NDEBUG +  int isReadonly   = (flags & SQLITE_OPEN_READONLY); +#endif +  int isReadWrite  = (flags & SQLITE_OPEN_READWRITE); + +#ifndef NDEBUG +  int isOpenJournal = (isCreate && ( +        eType==SQLITE_OPEN_MASTER_JOURNAL  +     || eType==SQLITE_OPEN_MAIN_JOURNAL  +     || eType==SQLITE_OPEN_WAL +  )); +#endif + +  /* Check the following statements are true:  +  ** +  **   (a) Exactly one of the READWRITE and READONLY flags must be set, and  +  **   (b) if CREATE is set, then READWRITE must also be set, and +  **   (c) if EXCLUSIVE is set, then CREATE must also be set. +  **   (d) if DELETEONCLOSE is set, then CREATE must also be set. +  */ +  assert((isReadonly==0 || isReadWrite==0) && (isReadWrite || isReadonly)); +  assert(isCreate==0 || isReadWrite); +  assert(isExclusive==0 || isCreate); +  assert(isDelete==0 || isCreate); + +  /* The main DB, main journal, WAL file and master journal are never  +  ** automatically deleted. Nor are they ever temporary files.  */ +  assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_DB ); +  assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_JOURNAL ); +  assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MASTER_JOURNAL ); +  assert( (!isDelete && zName) || eType!=SQLITE_OPEN_WAL ); + +  /* Assert that the upper layer has set one of the "file-type" flags. */ +  assert( eType==SQLITE_OPEN_MAIN_DB      || eType==SQLITE_OPEN_TEMP_DB  +       || eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL  +       || eType==SQLITE_OPEN_SUBJOURNAL   || eType==SQLITE_OPEN_MASTER_JOURNAL  +       || eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL +  ); + +  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 ){ +    assert(isDelete && !isOpenJournal); +    rc = getTempname(MAX_PATH+1, zTmpname); +    if( rc!=SQLITE_OK ){ +      return rc; +    } +    zUtf8Name = zTmpname; +  } + +  /* Convert the filename to the system encoding. */ +  zConverted = convertUtf8Filename(zUtf8Name); +  if( zConverted==0 ){ +    return SQLITE_NOMEM; +  } + +  if( isReadWrite ){ +    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. +  */ +  if( isExclusive ){ +    /* Creates a new file, only if it does not already exist. */ +    /* If the file exists, it fails. */ +    dwCreationDisposition = CREATE_NEW; +  }else if( isCreate ){ +    /* 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( isDelete ){ +#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() ){ +    while( (h = CreateFileW((WCHAR*)zConverted, +                            dwDesiredAccess, +                            dwShareMode, NULL, +                            dwCreationDisposition, +                            dwFlagsAndAttributes, +                            NULL))==INVALID_HANDLE_VALUE && +                            retryIoerr(&cnt) ){} +/* 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{ +    while( (h = CreateFileA((char*)zConverted, +                            dwDesiredAccess, +                            dwShareMode, NULL, +                            dwCreationDisposition, +                            dwFlagsAndAttributes, +                            NULL))==INVALID_HANDLE_VALUE && +                            retryIoerr(&cnt) ){} +#endif +  } + +  logIoerr(cnt); + +  OSTRACE(("OPEN %d %s 0x%lx %s\n",  +           h, zName, dwDesiredAccess,  +           h==INVALID_HANDLE_VALUE ? "failed" : "ok")); + +  if( h==INVALID_HANDLE_VALUE ){ +    pFile->lastErrno = GetLastError(); +    winLogError(SQLITE_CANTOPEN, "winOpen", zUtf8Name); +    free(zConverted); +    if( isReadWrite && !isExclusive ){ +      return winOpen(pVfs, zName, id,  +             ((flags|SQLITE_OPEN_READONLY)&~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE)), pOutFlags); +    }else{ +      return SQLITE_CANTOPEN_BKPT; +    } +  } + +  if( pOutFlags ){ +    if( isReadWrite ){ +      *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( isReadWrite && eType==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 rc; +} + +/* +** 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 winDelete( +  sqlite3_vfs *pVfs,          /* Not used on win32 */ +  const char *zFilename,      /* Name of file to delete */ +  int syncDir                 /* Not used on win32 */ +){ +  int cnt = 0; +  int rc; +  void *zConverted; +  UNUSED_PARAMETER(pVfs); +  UNUSED_PARAMETER(syncDir); + +  SimulateIOError(return SQLITE_IOERR_DELETE); +  zConverted = convertUtf8Filename(zFilename); +  if( zConverted==0 ){ +    return SQLITE_NOMEM; +  } +  if( isNT() ){ +    rc = 1; +    while( GetFileAttributesW(zConverted)!=INVALID_FILE_ATTRIBUTES && +           (rc = DeleteFileW(zConverted))==0 && retryIoerr(&cnt) ){} +    rc = rc ? SQLITE_OK : SQLITE_ERROR; +/* 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{ +    rc = 1; +    while( GetFileAttributesA(zConverted)!=INVALID_FILE_ATTRIBUTES && +           (rc = DeleteFileA(zConverted))==0 && retryIoerr(&cnt) ){} +    rc = rc ? SQLITE_OK : SQLITE_ERROR; +#endif +  } +  if( rc ){ +    rc = winLogError(SQLITE_IOERR_DELETE, "winDelete", zFilename); +  }else{ +    logIoerr(cnt); +  } +  free(zConverted); +  OSTRACE(("DELETE \"%s\" %s\n", zFilename, (rc ? "failed" : "ok" ))); +  return rc; +} + +/* +** 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; +  UNUSED_PARAMETER(pVfs); + +  SimulateIOError( return SQLITE_IOERR_ACCESS; ); +  zConverted = convertUtf8Filename(zFilename); +  if( zConverted==0 ){ +    return SQLITE_NOMEM; +  } +  if( isNT() ){ +    int cnt = 0; +    WIN32_FILE_ATTRIBUTE_DATA sAttrData; +    memset(&sAttrData, 0, sizeof(sAttrData)); +    while( !(rc = GetFileAttributesExW((WCHAR*)zConverted, +                             GetFileExInfoStandard,  +                             &sAttrData)) && retryIoerr(&cnt) ){} +    if( rc ){ +      /* For an SQLITE_ACCESS_EXISTS query, treat a zero-length file +      ** as if it does not exist. +      */ +      if(    flags==SQLITE_ACCESS_EXISTS +          && sAttrData.nFileSizeHigh==0  +          && sAttrData.nFileSizeLow==0 ){ +        attr = INVALID_FILE_ATTRIBUTES; +      }else{ +        attr = sAttrData.dwFileAttributes; +      } +    }else{ +      logIoerr(cnt); +      if( GetLastError()!=ERROR_FILE_NOT_FOUND ){ +        winLogError(SQLITE_IOERR_ACCESS, "winAccess", zFilename); +        free(zConverted); +        return SQLITE_IOERR_ACCESS; +      }else{ +        attr = INVALID_FILE_ATTRIBUTES; +      } +    } +/* 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{ +    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!=INVALID_FILE_ATTRIBUTES && +             (attr & FILE_ATTRIBUTE_READONLY)==0; +      break; +    default: +      assert(!"Invalid flags argument"); +  } +  *pResOut = rc; +  return SQLITE_OK; +} + + +/* +** Turn a relative pathname into a full pathname.  Write the full +** pathname into zOut[].  zOut[] will be at least pVfs->mxPathname +** bytes in size. +*/ +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__) +  SimulateIOError( return SQLITE_ERROR ); +  UNUSED_PARAMETER(nFull); +  cygwin_conv_to_full_win32_path(zRelative, zFull); +  return SQLITE_OK; +#endif + +#if SQLITE_OS_WINCE +  SimulateIOError( return SQLITE_ERROR ); +  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 + +#if !SQLITE_OS_WINCE && !defined(__CYGWIN__) +  int nByte; +  void *zConverted; +  char *zOut; + +  /* If this path name begins with "/X:", where "X" is any alphabetic +  ** character, discard the initial "/" from the pathname. +  */ +  if( zRelative[0]=='/' && sqlite3Isalpha(zRelative[1]) && zRelative[2]==':' ){ +    zRelative++; +  } + +  /* It's odd to simulate an io-error here, but really this is just +  ** using the io-error infrastructure to test that SQLite handles this +  ** function failing. This function could fail if, for example, the +  ** current working directory has been unlinked. +  */ +  SimulateIOError( return SQLITE_ERROR ); +  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 +  }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; +    } +    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; +  }else{ +    return SQLITE_NOMEM; +  } +#endif +} + +/* +** Get the sector size of the device used to store +** file. +*/ +static int getSectorSize( +    sqlite3_vfs *pVfs, +    const char *zRelative     /* UTF-8 file name */ +){ +  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; + +  /* +  ** We need to get the full path name of the file +  ** to get the drive letter to look up the sector +  ** size. +  */ +  SimulateIOErrorBenign(1); +  rc = winFullPathname(pVfs, zRelative, MAX_PATH, zFullpath); +  SimulateIOErrorBenign(0); +  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; +    } +  } +#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; +  } +  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 +  }else{ +    h = LoadLibraryA((char*)zConverted); +#endif +  } +  free(zConverted); +  return (void*)h; +} +static void winDlError(sqlite3_vfs *pVfs, int nBuf, char *zBufOut){ +  UNUSED_PARAMETER(pVfs); +  getLastErrorMsg(nBuf, zBufOut); +} +static 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 +} +static 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 + + +/* +** Write up to nBuf bytes of randomness into zBuf. +*/ +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); +  } +  if( sizeof(DWORD)<=nBuf-n ){ +    DWORD cnt = GetTickCount(); +    memcpy(&zBuf[n], &cnt, sizeof(cnt)); +    n += sizeof(cnt); +  } +  if( sizeof(LARGE_INTEGER)<=nBuf-n ){ +    LARGE_INTEGER i; +    QueryPerformanceCounter(&i); +    memcpy(&zBuf[n], &i, sizeof(i)); +    n += sizeof(i); +  } +#endif +  return n; +} + + +/* +** Sleep for a little while.  Return the amount of time slept. +*/ +static int winSleep(sqlite3_vfs *pVfs, int microsec){ +  Sleep((microsec+999)/1000); +  UNUSED_PARAMETER(pVfs); +  return ((microsec+999)/1000)*1000; +} + +/* +** 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_TEST +int sqlite3_current_time = 0;  /* Fake system time in seconds since 1970. */ +#endif + +/* +** 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 SQLITE_OK.  Return SQLITE_ERROR if the time and date  +** cannot be found. +*/ +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).  +  */ +  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; + +#if SQLITE_OS_WINCE +  SYSTEMTIME time; +  GetSystemTime(&time); +  /* if SystemTimeToFileTime() fails, it returns zero. */ +  if (!SystemTimeToFileTime(&time,&ft)){ +    return SQLITE_ERROR; +  } +#else +  GetSystemTimeAsFileTime( &ft ); +#endif + +  *piNow = winFiletimeEpoch + +            ((((sqlite3_int64)ft.dwHighDateTime)*max32BitValue) +  +               (sqlite3_int64)ft.dwLowDateTime)/(sqlite3_int64)10000; + +#ifdef SQLITE_TEST +  if( sqlite3_current_time ){ +    *piNow = 1000*(sqlite3_int64)sqlite3_current_time + unixEpoch; +  } +#endif +  UNUSED_PARAMETER(pVfs); +  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. +*/ +static int winCurrentTime(sqlite3_vfs *pVfs, double *prNow){ +  int rc; +  sqlite3_int64 i; +  rc = winCurrentTimeInt64(pVfs, &i); +  if( !rc ){ +    *prNow = i/86400000.0; +  } +  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. +** +** 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 = { +    3,                   /* 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 */ +    winCurrentTimeInt64, /* xCurrentTimeInt64 */ +    0,                   /* xSetSystemCall */ +    0,                   /* xGetSystemCall */ +    0,                   /* xNextSystemCall */ +  }; + +#ifndef SQLITE_OMIT_WAL +  /* get memory map allocation granularity */ +  memset(&winSysInfo, 0, sizeof(SYSTEM_INFO)); +  GetSystemInfo(&winSysInfo); +  assert(winSysInfo.dwAllocationGranularity > 0); +#endif + +  sqlite3_vfs_register(&winVfs, 1); +  return SQLITE_OK;  +} +int sqlite3_os_end(void){  +  return SQLITE_OK; +} + +#endif /* SQLITE_OS_WIN */ | 
