diff options
author | dan <dan@noemail.net> | 2017-11-01 20:59:28 +0000 |
---|---|---|
committer | dan <dan@noemail.net> | 2017-11-01 20:59:28 +0000 |
commit | 92c02da33ec2717828eba13400355d43a7a6395f (patch) | |
tree | 96ce587324bfb06d0be895769385a704de331696 /src/os_unix.c | |
parent | 514c4b7e5dff5d47eb3a85da93637acdd2a7d294 (diff) | |
download | sqlite-92c02da33ec2717828eba13400355d43a7a6395f.tar.gz sqlite-92c02da33ec2717828eba13400355d43a7a6395f.zip |
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
Diffstat (limited to 'src/os_unix.c')
-rw-r--r-- | src/os_unix.c | 114 |
1 files changed, 68 insertions, 46 deletions
diff --git a/src/os_unix.c b/src/os_unix.c index b94d417f4..e90e335cb 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -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 */ @@ -4271,6 +4272,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 ); |