1
0
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:
drh
2001-12-14 15:09:55 +00:00
parent 6a6cfb9b31
commit a7fcb05988
8 changed files with 267 additions and 228 deletions

View File

@@ -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;