mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-05 15:55:57 +03:00
Fix a race condition in the locking code that would sometimes cause
SQLITE_PROTOCOL or SQLITE_CORRUPT to be returned when SQLITE_BUSY should have been returned. (CVS 326) FossilOrigin-Name: b0d218876442187af08161d989e6887b1cb4130c
This commit is contained in:
141
src/pager.c
141
src/pager.c
@@ -18,7 +18,7 @@
|
||||
** file simultaneously, or one process from reading the database while
|
||||
** another is writing.
|
||||
**
|
||||
** @(#) $Id: pager.c,v 1.32 2001/12/05 00:21:20 drh Exp $
|
||||
** @(#) $Id: pager.c,v 1.33 2001/12/14 15:09:57 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "pager.h"
|
||||
@@ -218,7 +218,7 @@ static void pager_reset(Pager *pPager){
|
||||
if( pPager->state==SQLITE_WRITELOCK ){
|
||||
sqlitepager_rollback(pPager);
|
||||
}
|
||||
sqliteOsUnlock(pPager->fd);
|
||||
sqliteOsUnlock(&pPager->fd);
|
||||
pPager->state = SQLITE_UNLOCK;
|
||||
pPager->dbSize = -1;
|
||||
pPager->nRef = 0;
|
||||
@@ -246,25 +246,18 @@ static int pager_unwritelock(Pager *pPager){
|
||||
int rc;
|
||||
PgHdr *pPg;
|
||||
if( pPager->state!=SQLITE_WRITELOCK ) return SQLITE_OK;
|
||||
sqliteOsUnlock(pPager->fd);
|
||||
rc = sqliteOsLock(pPager->fd, 0);
|
||||
sqliteOsClose(pPager->jfd);
|
||||
sqliteOsClose(&pPager->jfd);
|
||||
pPager->journalOpen = 0;
|
||||
sqliteOsDelete(pPager->zJournal);
|
||||
rc = sqliteOsReadLock(&pPager->fd);
|
||||
assert( rc==SQLITE_OK );
|
||||
sqliteFree( pPager->aInJournal );
|
||||
pPager->aInJournal = 0;
|
||||
for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
|
||||
pPg->inJournal = 0;
|
||||
pPg->dirty = 0;
|
||||
}
|
||||
if( rc!=SQLITE_OK ){
|
||||
pPager->state = SQLITE_UNLOCK;
|
||||
rc = SQLITE_PROTOCOL;
|
||||
pPager->errMask |= PAGER_ERR_LOCK;
|
||||
}else{
|
||||
rc = SQLITE_OK;
|
||||
pPager->state = SQLITE_READLOCK;
|
||||
}
|
||||
pPager->state = SQLITE_READLOCK;
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -304,8 +297,8 @@ static int pager_playback(Pager *pPager){
|
||||
** the journal is empty.
|
||||
*/
|
||||
assert( pPager->journalOpen );
|
||||
sqliteOsSeek(pPager->jfd, 0);
|
||||
rc = sqliteOsFileSize(pPager->jfd, &nRec);
|
||||
sqliteOsSeek(&pPager->jfd, 0);
|
||||
rc = sqliteOsFileSize(&pPager->jfd, &nRec);
|
||||
if( rc!=SQLITE_OK ){
|
||||
goto end_playback;
|
||||
}
|
||||
@@ -317,16 +310,16 @@ static int pager_playback(Pager *pPager){
|
||||
/* Read the beginning of the journal and truncate the
|
||||
** database file back to its original size.
|
||||
*/
|
||||
rc = sqliteOsRead(pPager->jfd, aMagic, sizeof(aMagic));
|
||||
rc = sqliteOsRead(&pPager->jfd, aMagic, sizeof(aMagic));
|
||||
if( rc!=SQLITE_OK || memcmp(aMagic,aJournalMagic,sizeof(aMagic))!=0 ){
|
||||
rc = SQLITE_PROTOCOL;
|
||||
goto end_playback;
|
||||
}
|
||||
rc = sqliteOsRead(pPager->jfd, &mxPg, sizeof(mxPg));
|
||||
rc = sqliteOsRead(&pPager->jfd, &mxPg, sizeof(mxPg));
|
||||
if( rc!=SQLITE_OK ){
|
||||
goto end_playback;
|
||||
}
|
||||
rc = sqliteOsTruncate(pPager->fd, mxPg*SQLITE_PAGE_SIZE);
|
||||
rc = sqliteOsTruncate(&pPager->fd, mxPg*SQLITE_PAGE_SIZE);
|
||||
if( rc!=SQLITE_OK ){
|
||||
goto end_playback;
|
||||
}
|
||||
@@ -339,9 +332,9 @@ static int pager_playback(Pager *pPager){
|
||||
/* Seek to the beginning of the segment */
|
||||
int ofst;
|
||||
ofst = i*sizeof(PageRecord) + sizeof(aMagic) + sizeof(Pgno);
|
||||
rc = sqliteOsSeek(pPager->jfd, ofst);
|
||||
rc = sqliteOsSeek(&pPager->jfd, ofst);
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
rc = sqliteOsRead(pPager->jfd, &pgRec, sizeof(pgRec));
|
||||
rc = sqliteOsRead(&pPager->jfd, &pgRec, sizeof(pgRec));
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
|
||||
/* Sanity checking on the page */
|
||||
@@ -358,9 +351,9 @@ static int pager_playback(Pager *pPager){
|
||||
memcpy(PGHDR_TO_DATA(pPg), pgRec.aData, SQLITE_PAGE_SIZE);
|
||||
memset(PGHDR_TO_EXTRA(pPg), 0, pPager->nExtra);
|
||||
}
|
||||
rc = sqliteOsSeek(pPager->fd, (pgRec.pgno-1)*SQLITE_PAGE_SIZE);
|
||||
rc = sqliteOsSeek(&pPager->fd, (pgRec.pgno-1)*SQLITE_PAGE_SIZE);
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
rc = sqliteOsWrite(pPager->fd, pgRec.aData, SQLITE_PAGE_SIZE);
|
||||
rc = sqliteOsWrite(&pPager->fd, pgRec.aData, SQLITE_PAGE_SIZE);
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
}
|
||||
|
||||
@@ -432,7 +425,7 @@ int sqlitepager_open(
|
||||
nameLen = strlen(zFilename);
|
||||
pPager = sqliteMalloc( sizeof(*pPager) + nameLen*2 + 30 );
|
||||
if( pPager==0 ){
|
||||
sqliteOsClose(fd);
|
||||
sqliteOsClose(&fd);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
pPager->zFilename = (char*)&pPager[1];
|
||||
@@ -481,7 +474,7 @@ int sqlitepager_pagecount(Pager *pPager){
|
||||
if( pPager->dbSize>=0 ){
|
||||
return pPager->dbSize;
|
||||
}
|
||||
if( sqliteOsFileSize(pPager->fd, &n)!=SQLITE_OK ){
|
||||
if( sqliteOsFileSize(&pPager->fd, &n)!=SQLITE_OK ){
|
||||
pPager->errMask |= PAGER_ERR_DISK;
|
||||
return 0;
|
||||
}
|
||||
@@ -506,12 +499,12 @@ int sqlitepager_close(Pager *pPager){
|
||||
switch( pPager->state ){
|
||||
case SQLITE_WRITELOCK: {
|
||||
sqlitepager_rollback(pPager);
|
||||
sqliteOsUnlock(pPager->fd);
|
||||
sqliteOsUnlock(&pPager->fd);
|
||||
assert( pPager->journalOpen==0 );
|
||||
break;
|
||||
}
|
||||
case SQLITE_READLOCK: {
|
||||
sqliteOsUnlock(pPager->fd);
|
||||
sqliteOsUnlock(&pPager->fd);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
@@ -523,7 +516,7 @@ int sqlitepager_close(Pager *pPager){
|
||||
pNext = pPg->pNextAll;
|
||||
sqliteFree(pPg);
|
||||
}
|
||||
sqliteOsClose(pPager->fd);
|
||||
sqliteOsClose(&pPager->fd);
|
||||
assert( pPager->journalOpen==0 );
|
||||
if( pPager->tempFile ){
|
||||
sqliteOsDelete(pPager->zFilename);
|
||||
@@ -590,14 +583,14 @@ static int syncAllPages(Pager *pPager){
|
||||
PgHdr *pPg;
|
||||
int rc = SQLITE_OK;
|
||||
if( pPager->needSync ){
|
||||
rc = sqliteOsSync(pPager->jfd);
|
||||
rc = sqliteOsSync(&pPager->jfd);
|
||||
if( rc!=0 ) return rc;
|
||||
pPager->needSync = 0;
|
||||
}
|
||||
for(pPg=pPager->pFirst; pPg; pPg=pPg->pNextFree){
|
||||
if( pPg->dirty ){
|
||||
sqliteOsSeek(pPager->fd, (pPg->pgno-1)*SQLITE_PAGE_SIZE);
|
||||
rc = sqliteOsWrite(pPager->fd, PGHDR_TO_DATA(pPg), SQLITE_PAGE_SIZE);
|
||||
sqliteOsSeek(&pPager->fd, (pPg->pgno-1)*SQLITE_PAGE_SIZE);
|
||||
rc = sqliteOsWrite(&pPager->fd, PGHDR_TO_DATA(pPg), SQLITE_PAGE_SIZE);
|
||||
if( rc!=SQLITE_OK ) break;
|
||||
pPg->dirty = 0;
|
||||
}
|
||||
@@ -644,7 +637,7 @@ int sqlitepager_get(Pager *pPager, Pgno pgno, void **ppPage){
|
||||
** on the database file.
|
||||
*/
|
||||
if( pPager->nRef==0 ){
|
||||
if( sqliteOsLock(pPager->fd, 0)!=SQLITE_OK ){
|
||||
if( sqliteOsReadLock(&pPager->fd)!=SQLITE_OK ){
|
||||
*ppPage = 0;
|
||||
return SQLITE_BUSY;
|
||||
}
|
||||
@@ -655,6 +648,17 @@ int sqlitepager_get(Pager *pPager, Pgno pgno, void **ppPage){
|
||||
if( sqliteOsFileExists(pPager->zJournal) ){
|
||||
int rc, dummy;
|
||||
|
||||
/* Get a write lock on the database
|
||||
*/
|
||||
rc = sqliteOsWriteLock(&pPager->fd);
|
||||
if( rc!=SQLITE_OK ){
|
||||
rc = sqliteOsReadLock(&pPager->fd);
|
||||
assert( rc==SQLITE_OK );
|
||||
*ppPage = 0;
|
||||
return SQLITE_BUSY;
|
||||
}
|
||||
pPager->state = SQLITE_WRITELOCK;
|
||||
|
||||
/* Open the journal for exclusive access. Return SQLITE_BUSY if
|
||||
** we cannot get exclusive access to the journal file.
|
||||
**
|
||||
@@ -663,28 +667,13 @@ int sqlitepager_get(Pager *pPager, Pgno pgno, void **ppPage){
|
||||
** exclusive access lock.
|
||||
*/
|
||||
rc = sqliteOsOpenReadWrite(pPager->zJournal, &pPager->jfd, &dummy);
|
||||
if( rc==SQLITE_OK ){
|
||||
pPager->journalOpen = 1;
|
||||
}
|
||||
if( rc!=SQLITE_OK || sqliteOsLock(pPager->jfd, 1)!=SQLITE_OK ){
|
||||
if( pPager->journalOpen ){
|
||||
sqliteOsClose(pPager->jfd);
|
||||
pPager->journalOpen = 0;
|
||||
}
|
||||
sqliteOsUnlock(pPager->fd);
|
||||
if( rc!=SQLITE_OK ){
|
||||
rc = sqliteOsUnlock(&pPager->fd);
|
||||
assert( rc==SQLITE_OK );
|
||||
*ppPage = 0;
|
||||
return SQLITE_BUSY;
|
||||
}
|
||||
|
||||
/* Get a write lock on the database */
|
||||
sqliteOsUnlock(pPager->fd);
|
||||
if( sqliteOsLock(pPager->fd, 1)!=SQLITE_OK ){
|
||||
sqliteOsClose(pPager->jfd);
|
||||
pPager->journalOpen = 0;
|
||||
*ppPage = 0;
|
||||
return SQLITE_PROTOCOL;
|
||||
}
|
||||
pPager->state = SQLITE_WRITELOCK;
|
||||
pPager->journalOpen = 1;
|
||||
|
||||
/* Playback and delete the journal. Drop the database write
|
||||
** lock and reacquire the read lock.
|
||||
@@ -804,8 +793,8 @@ int sqlitepager_get(Pager *pPager, Pgno pgno, void **ppPage){
|
||||
memset(PGHDR_TO_DATA(pPg), 0, SQLITE_PAGE_SIZE);
|
||||
}else{
|
||||
int rc;
|
||||
sqliteOsSeek(pPager->fd, (pgno-1)*SQLITE_PAGE_SIZE);
|
||||
rc = sqliteOsRead(pPager->fd, PGHDR_TO_DATA(pPg), SQLITE_PAGE_SIZE);
|
||||
sqliteOsSeek(&pPager->fd, (pgno-1)*SQLITE_PAGE_SIZE);
|
||||
rc = sqliteOsRead(&pPager->fd, PGHDR_TO_DATA(pPg), SQLITE_PAGE_SIZE);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
@@ -934,48 +923,30 @@ int sqlitepager_write(void *pData){
|
||||
assert( pPager->state!=SQLITE_UNLOCK );
|
||||
if( pPager->state==SQLITE_READLOCK ){
|
||||
assert( pPager->aInJournal==0 );
|
||||
rc = sqliteOsWriteLock(&pPager->fd);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
pPager->aInJournal = sqliteMalloc( pPager->dbSize/8 + 1 );
|
||||
if( pPager->aInJournal==0 ){
|
||||
sqliteFree(pPager->aInJournal);
|
||||
sqliteOsReadLock(&pPager->fd);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
rc = sqliteOsOpenExclusive(pPager->zJournal, &pPager->jfd);
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqliteFree(pPager->aInJournal);
|
||||
pPager->aInJournal = 0;
|
||||
sqliteOsReadLock(&pPager->fd);
|
||||
return SQLITE_CANTOPEN;
|
||||
}
|
||||
pPager->journalOpen = 1;
|
||||
pPager->needSync = 0;
|
||||
if( sqliteOsLock(pPager->jfd, 1)!=SQLITE_OK ){
|
||||
sqliteFree(pPager->aInJournal);
|
||||
sqliteOsClose(pPager->jfd);
|
||||
sqliteOsDelete(pPager->zJournal);
|
||||
pPager->journalOpen = 0;
|
||||
return SQLITE_BUSY;
|
||||
}
|
||||
sqliteOsUnlock(pPager->fd);
|
||||
if( sqliteOsLock(pPager->fd, 1)!=SQLITE_OK ){
|
||||
sqliteOsUnlock(pPager->fd);
|
||||
rc = sqliteOsLock(pPager->fd, 0);
|
||||
sqliteFree(pPager->aInJournal);
|
||||
sqliteOsClose(pPager->jfd);
|
||||
sqliteOsDelete(pPager->zJournal);
|
||||
pPager->journalOpen = 0;
|
||||
if( rc ){
|
||||
pPager->state = SQLITE_UNLOCK;
|
||||
pPager->errMask |= PAGER_ERR_LOCK;
|
||||
return SQLITE_PROTOCOL;
|
||||
}else{
|
||||
pPager->state = SQLITE_READLOCK;
|
||||
return SQLITE_BUSY;
|
||||
}
|
||||
}
|
||||
pPager->state = SQLITE_WRITELOCK;
|
||||
sqlitepager_pagecount(pPager);
|
||||
pPager->origDbSize = pPager->dbSize;
|
||||
rc = sqliteOsWrite(pPager->jfd, aJournalMagic, sizeof(aJournalMagic));
|
||||
rc = sqliteOsWrite(&pPager->jfd, aJournalMagic, sizeof(aJournalMagic));
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqliteOsWrite(pPager->jfd, &pPager->dbSize, sizeof(Pgno));
|
||||
rc = sqliteOsWrite(&pPager->jfd, &pPager->dbSize, sizeof(Pgno));
|
||||
}
|
||||
if( rc!=SQLITE_OK ){
|
||||
rc = pager_unwritelock(pPager);
|
||||
@@ -986,9 +957,9 @@ int sqlitepager_write(void *pData){
|
||||
assert( pPager->state==SQLITE_WRITELOCK );
|
||||
assert( pPager->journalOpen );
|
||||
if( pPg->pgno <= pPager->origDbSize ){
|
||||
rc = sqliteOsWrite(pPager->jfd, &pPg->pgno, sizeof(Pgno));
|
||||
rc = sqliteOsWrite(&pPager->jfd, &pPg->pgno, sizeof(Pgno));
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqliteOsWrite(pPager->jfd, pData, SQLITE_PAGE_SIZE);
|
||||
rc = sqliteOsWrite(&pPager->jfd, pData, SQLITE_PAGE_SIZE);
|
||||
}
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlitepager_rollback(pPager);
|
||||
@@ -1040,17 +1011,17 @@ int sqlitepager_commit(Pager *pPager){
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
assert( pPager->journalOpen );
|
||||
if( pPager->needSync && sqliteOsSync(pPager->jfd)!=SQLITE_OK ){
|
||||
if( pPager->needSync && sqliteOsSync(&pPager->jfd)!=SQLITE_OK ){
|
||||
goto commit_abort;
|
||||
}
|
||||
for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
|
||||
if( pPg->dirty==0 ) continue;
|
||||
rc = sqliteOsSeek(pPager->fd, (pPg->pgno-1)*SQLITE_PAGE_SIZE);
|
||||
rc = sqliteOsSeek(&pPager->fd, (pPg->pgno-1)*SQLITE_PAGE_SIZE);
|
||||
if( rc!=SQLITE_OK ) goto commit_abort;
|
||||
rc = sqliteOsWrite(pPager->fd, PGHDR_TO_DATA(pPg), SQLITE_PAGE_SIZE);
|
||||
rc = sqliteOsWrite(&pPager->fd, PGHDR_TO_DATA(pPg), SQLITE_PAGE_SIZE);
|
||||
if( rc!=SQLITE_OK ) goto commit_abort;
|
||||
}
|
||||
if( sqliteOsSync(pPager->fd)!=SQLITE_OK ) goto commit_abort;
|
||||
if( sqliteOsSync(&pPager->fd)!=SQLITE_OK ) goto commit_abort;
|
||||
rc = pager_unwritelock(pPager);
|
||||
pPager->dbSize = -1;
|
||||
return rc;
|
||||
|
Reference in New Issue
Block a user