1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-11-12 13:01:09 +03:00

If a readonly_shm connection cannot map the *-shm file because no other

process is holding the DMS lock, have it read from the database file only,
ignoring any content in the wal file.

FossilOrigin-Name: ce5d13c2de69b73378637d4f7e109714f7cd17bf1d1ad995e0be442d517ed1b3
This commit is contained in:
dan
2017-11-01 20:59:28 +00:00
parent 514c4b7e5d
commit 92c02da33e
5 changed files with 183 additions and 59 deletions

View File

@@ -4108,6 +4108,7 @@ struct unixShmNode {
int szRegion; /* Size of shared-memory regions */
u16 nRegion; /* Size of array apRegion */
u8 isReadonly; /* True if read-only */
u8 isUnlocked; /* True if no DMS lock held */
char **apRegion; /* Array of mapped shared-memory regions */
int nRef; /* Number of unixShm objects pointing to this */
unixShm *pFirst; /* All unixShm objects pointing to this */
@@ -4270,6 +4271,64 @@ static void unixShmPurge(unixFile *pFd){
}
}
/*
** 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
** code otherwise.
**
** If the DMS cannot be locked because this is a readonly_shm=1
** connection and no other process already holds a lock, return
** SQLITE_READONLY_CANTLOCK and set pShmNode->isUnlocked=1.
*/
static int unixLockSharedMemory(unixFile *pDbFd, unixShmNode *pShmNode){
struct flock lock;
int rc = SQLITE_OK;
/* Use F_GETLK to determine the locks other processes are holding
** on the DMS byte. If it indicates that another process is holding
** a SHARED lock, then this process may also take a SHARED lock
** and proceed with opening the *-shm file.
**
** Or, if no other process is holding any lock, then this process
** is the first to open it. In this case take an EXCLUSIVE lock on the
** DMS byte and truncate the *-shm file to zero bytes in size. Then
** downgrade to a SHARED lock on the DMS byte.
**
** If another process is holding an EXCLUSIVE lock on the DMS byte,
** 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. */
lock.l_whence = SEEK_SET;
lock.l_start = UNIX_SHM_DMS;
lock.l_len = 1;
lock.l_type = F_WRLCK;
if( osFcntl(pShmNode->h, F_GETLK, &lock)!=0 ) {
rc = SQLITE_IOERR_LOCK;
}else if( lock.l_type==F_UNLCK ){
if( pShmNode->isReadonly ){
pShmNode->isUnlocked = 1;
rc = SQLITE_READONLY_CANTLOCK;
}else{
rc = unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1);
if( rc==SQLITE_OK && robust_ftruncate(pShmNode->h, 0) ){
rc = unixLogError(SQLITE_IOERR_SHMOPEN,"ftruncate",pShmNode->zFilename);
}
}
}else if( lock.l_type==F_WRLCK ){
rc = SQLITE_BUSY;
}
if( rc==SQLITE_OK ){
assert( lock.l_type==F_UNLCK || lock.l_type==F_RDLCK );
rc = unixShmSystemLock(pDbFd, F_RDLCK, UNIX_SHM_DMS, 1);
}
return rc;
}
/*
** Open a shared-memory area associated with open database file pDbFd.
** This particular implementation uses mmapped files.
@@ -4308,7 +4367,7 @@ static void unixShmPurge(unixFile *pFd){
static int unixOpenSharedMemory(unixFile *pDbFd){
struct unixShm *p = 0; /* The connection to be opened */
struct unixShmNode *pShmNode; /* The underlying mmapped file */
int rc; /* Result code */
int rc = SQLITE_OK; /* Result code */
unixInodeInfo *pInode; /* The inode of fd */
char *zShmFilename; /* Name of the file used for SHM */
int nShmFilename; /* Size of the SHM filename in bytes */
@@ -4372,7 +4431,6 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
}
if( pInode->bProcessLock==0 ){
struct flock lock;
int openFlags = O_RDWR | O_CREAT;
if( sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){
openFlags = O_RDONLY;
@@ -4389,50 +4447,9 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
** the original owner will not be able to connect.
*/
robustFchown(pShmNode->h, sStat.st_uid, sStat.st_gid);
/* Use F_GETLK to determine the locks other processes are holding
** on the DMS byte. If it indicates that another process is holding
** a SHARED lock, then this process may also take a SHARED lock
** and proceed with opening the *-shm file.
**
** Or, if no other process is holding any lock, then this process
** is the first to open it. In this case take an EXCLUSIVE lock on the
** DMS byte and truncate the *-shm file to zero bytes in size. Then
** downgrade to a SHARED lock on the DMS byte.
**
** If another process is holding an EXCLUSIVE lock on the DMS byte,
** 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. */
rc = SQLITE_OK;
lock.l_whence = SEEK_SET;
lock.l_start = UNIX_SHM_DMS;
lock.l_len = 1;
lock.l_type = F_WRLCK;
if( osFcntl(pShmNode->h, F_GETLK, &lock)!=0 ) {
rc = SQLITE_IOERR_LOCK;
}else if( lock.l_type==F_UNLCK ){
if( pShmNode->isReadonly ){
rc = SQLITE_CANTOPEN_DIRTYWAL;
}else{
rc = unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1);
if( rc==SQLITE_OK && robust_ftruncate(pShmNode->h, 0) ){
rc = unixLogError(SQLITE_IOERR_SHMOPEN, "ftruncate", zShmFilename);
}
}
}else if( lock.l_type==F_WRLCK ){
rc = SQLITE_BUSY;
}
if( rc==SQLITE_OK ){
assert( lock.l_type==F_UNLCK || lock.l_type==F_RDLCK );
rc = unixShmSystemLock(pDbFd, F_RDLCK, UNIX_SHM_DMS, 1);
}
if( rc ) goto shm_open_err;
rc = unixLockSharedMemory(pDbFd, pShmNode);
if( rc!=SQLITE_OK && rc!=SQLITE_READONLY_CANTLOCK ) goto shm_open_err;
}
}
@@ -4456,7 +4473,7 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
p->pNext = pShmNode->pFirst;
pShmNode->pFirst = p;
sqlite3_mutex_leave(pShmNode->mutex);
return SQLITE_OK;
return rc;
/* Jump here on any error */
shm_open_err:
@@ -4508,6 +4525,11 @@ static int unixShmMap(
p = pDbFd->pShm;
pShmNode = p->pShmNode;
sqlite3_mutex_enter(pShmNode->mutex);
if( pShmNode->isUnlocked ){
rc = unixLockSharedMemory(pDbFd, pShmNode);
if( rc!=SQLITE_OK ) goto shmpage_out;
pShmNode->isUnlocked = 0;
}
assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 );
assert( pShmNode->pInode==pDbFd->pInode );
assert( pShmNode->h>=0 || pDbFd->pInode->bProcessLock==1 );