SQLite

Check-in [43c311701b]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Further corrections to read-only SHM file handling on Win32.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | readonly-wal-recovery
Files: files | file ages | folders
SHA3-256: 43c311701bdf1202918cd46fa6133a11458e0ef8ddb09e46290a231083f395ce
User & Date: mistachkin 2017-11-09 18:53:51.070
Context
2017-11-09
20:02
Get read-only SHM file tests passing on Win32. (check-in: abef053535 user: mistachkin tags: readonly-wal-recovery)
18:53
Further corrections to read-only SHM file handling on Win32. (check-in: 43c311701b user: mistachkin tags: readonly-wal-recovery)
18:21
Corrections to Win32 lock detection for SHM files. (check-in: 3a91be975d user: mistachkin tags: readonly-wal-recovery)
Changes
Unified Diff Show Whitespace Changes Patch
Changes to src/os_win.c.
2101
2102
2103
2104
2105
2106
2107
2108
2109

2110
2111
2112
2113
2114
2115
2116
/*
** The "winIsLockingError" macro is used to determine if a particular I/O
** error code is due to file locking.  It must accept the error code DWORD
** as its only argument and should return non-zero if the error code is due
** to file locking.
*/
#if !defined(winIsLockingError)
#define winIsLockingError(a) (((a)==ERROR_ACCESS_DENIED)        || \
                              ((a)==ERROR_LOCK_VIOLATION)       || \

                              ((a)==ERROR_IO_PENDING))
#endif

/*
** The "winIoerrCanRetry1" macro is used to determine if a particular I/O
** error code obtained via GetLastError() is eligible to be retried.  It
** must accept the error code DWORD as its only argument and should return







|

>







2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
/*
** The "winIsLockingError" macro is used to determine if a particular I/O
** error code is due to file locking.  It must accept the error code DWORD
** as its only argument and should return non-zero if the error code is due
** to file locking.
*/
#if !defined(winIsLockingError)
#define winIsLockingError(a) (((a)==NO_ERROR)                   || \
                              ((a)==ERROR_LOCK_VIOLATION)       || \
                              ((a)==ERROR_HANDLE_EOF)           || \
                              ((a)==ERROR_IO_PENDING))
#endif

/*
** The "winIoerrCanRetry1" macro is used to determine if a particular I/O
** error code obtained via GetLastError() is eligible to be retried.  It
** must accept the error code DWORD as its only argument and should return
3843
3844
3845
3846
3847
3848
3849

3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869

3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
**
**       WINSHM_UNLCK -- No locks are held on the DMS.
**       WINSHM_RDLCK -- A SHARED lock is held on the DMS.
**       WINSHM_WRLCK -- An EXCLUSIVE lock is held on the DMS.
*/
static int winGetShmDmsLockType(
  winFile *pFile, /* File handle object */

  int *pLockType  /* WINSHM_UNLCK, WINSHM_RDLCK, or WINSHM_WRLCK */
){
#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED)
  OVERLAPPED overlapped; /* The offset for ReadFile/WriteFile. */
#endif
  LPVOID pOverlapped = 0;
  sqlite3_int64 offset = WIN_SHM_DMS;
  BYTE notUsed1 = 0;
  DWORD notUsed2 = 0;

#if SQLITE_OS_WINCE || defined(SQLITE_WIN32_NO_OVERLAPPED)
  if( winSeekFile(pFile, offset) ){
    return SQLITE_IOERR_SEEK;
  }
#else
  memset(&overlapped, 0, sizeof(OVERLAPPED));
  overlapped.Offset = (LONG)(offset & 0xffffffff);
  overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff);
  pOverlapped = &overlapped;
#endif

  if( !osWriteFile(pFile->h, &notUsed1, 1, &notUsed2, pOverlapped) ){
    DWORD lastErrno = osGetLastError();
    if( !osReadFile(pFile->h, &notUsed1, 1, &notUsed2, pOverlapped) ){
      lastErrno = osGetLastError();
      if( winIsLockingError(lastErrno) ){
        *pLockType = WINSHM_WRLCK;
      }else{
        return SQLITE_IOERR_READ;
      }
    }else{
      if( winIsLockingError(lastErrno) ){
        *pLockType = WINSHM_RDLCK;
      }else{
        return SQLITE_IOERR_WRITE;
      }
    }
  }else{
    *pLockType = WINSHM_UNLCK;
  }
  return SQLITE_OK;
}

/*
** The DMS lock has not yet been taken on shm file pShmNode. Attempt to
** take it now. Return SQLITE_OK if successful, or an SQLite error







>




















>
|
|



|





|





|







3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
**
**       WINSHM_UNLCK -- No locks are held on the DMS.
**       WINSHM_RDLCK -- A SHARED lock is held on the DMS.
**       WINSHM_WRLCK -- An EXCLUSIVE lock is held on the DMS.
*/
static int winGetShmDmsLockType(
  winFile *pFile, /* File handle object */
  int bReadOnly,  /* Non-zero if the SHM was opened read-only */
  int *pLockType  /* WINSHM_UNLCK, WINSHM_RDLCK, or WINSHM_WRLCK */
){
#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED)
  OVERLAPPED overlapped; /* The offset for ReadFile/WriteFile. */
#endif
  LPVOID pOverlapped = 0;
  sqlite3_int64 offset = WIN_SHM_DMS;
  BYTE notUsed1 = 0;
  DWORD notUsed2 = 0;

#if SQLITE_OS_WINCE || defined(SQLITE_WIN32_NO_OVERLAPPED)
  if( winSeekFile(pFile, offset) ){
    return SQLITE_IOERR_SEEK;
  }
#else
  memset(&overlapped, 0, sizeof(OVERLAPPED));
  overlapped.Offset = (LONG)(offset & 0xffffffff);
  overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff);
  pOverlapped = &overlapped;
#endif
  if( bReadOnly ||
      !osWriteFile(pFile->h, &notUsed1, 1, &notUsed2, pOverlapped) ){
    DWORD lastErrno = bReadOnly ? NO_ERROR : osGetLastError();
    if( !osReadFile(pFile->h, &notUsed1, 1, &notUsed2, pOverlapped) ){
      lastErrno = osGetLastError();
      if( winIsLockingError(lastErrno) ){
        if( pLockType ) *pLockType = WINSHM_WRLCK;
      }else{
        return SQLITE_IOERR_READ;
      }
    }else{
      if( winIsLockingError(lastErrno) ){
        if( pLockType ) *pLockType = WINSHM_RDLCK;
      }else{
        return SQLITE_IOERR_WRITE;
      }
    }
  }else{
    if( pLockType ) *pLockType = WINSHM_UNLCK;
  }
  return SQLITE_OK;
}

/*
** The DMS lock has not yet been taken on shm file pShmNode. Attempt to
** take it now. Return SQLITE_OK if successful, or an SQLite error
3916
3917
3918
3919
3920
3921
3922
3923

3924
3925
3926
3927
3928
3929
3930
  ** return SQLITE_BUSY to the caller (it will try again). An earlier
  ** version of this code attempted the SHARED lock at this point. But
  ** this introduced a subtle race condition: if the process holding
  ** EXCLUSIVE failed just before truncating the *-shm file, then this
  ** process might open and use the *-shm file without truncating it.
  ** And if the *-shm file has been corrupted by a power failure or
  ** system crash, the database itself may also become corrupt.  */
  if( winGetShmDmsLockType(&pShmNode->hFile, &lockType)!=SQLITE_OK ){

    rc = SQLITE_IOERR_LOCK;
  }else if( lockType==WINSHM_UNLCK ){
    if( pShmNode->isReadonly ){
      pShmNode->isUnlocked = 1;
      rc = SQLITE_READONLY_CANTINIT;
    }else{
      winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1);







|
>







3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
  ** return SQLITE_BUSY to the caller (it will try again). An earlier
  ** version of this code attempted the SHARED lock at this point. But
  ** this introduced a subtle race condition: if the process holding
  ** EXCLUSIVE failed just before truncating the *-shm file, then this
  ** process might open and use the *-shm file without truncating it.
  ** And if the *-shm file has been corrupted by a power failure or
  ** system crash, the database itself may also become corrupt.  */
  if( winGetShmDmsLockType(&pShmNode->hFile, pShmNode->isReadonly,
                           &lockType)!=SQLITE_OK ){
    rc = SQLITE_IOERR_LOCK;
  }else if( lockType==WINSHM_UNLCK ){
    if( pShmNode->isReadonly ){
      pShmNode->isUnlocked = 1;
      rc = SQLITE_READONLY_CANTINIT;
    }else{
      winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1);