mirror of
https://github.com/sqlite/sqlite.git
synced 2025-11-12 13:01:09 +03:00
Instead of extra locks, use F_GETLK to ensure that readonly_shm clients cannot
connect to a wal-mode database if there are no writers. FossilOrigin-Name: 5492f457dc7cc5c416de4b4e61e84bd2f10b4e6ce54011b7a60feb47f629c923
This commit is contained in:
107
src/os_unix.c
107
src/os_unix.c
@@ -4145,7 +4145,6 @@ struct unixShm {
|
||||
*/
|
||||
#define UNIX_SHM_BASE ((22+SQLITE_SHM_NLOCK)*4) /* first lock byte */
|
||||
#define UNIX_SHM_DMS (UNIX_SHM_BASE+SQLITE_SHM_NLOCK) /* deadman switch */
|
||||
#define UNIX_SHM_N_DMS 1000000000 /* Size of the DMS */
|
||||
|
||||
/*
|
||||
** Apply posix advisory locks for all bytes from ofst through ofst+n-1.
|
||||
@@ -4167,6 +4166,12 @@ static int unixShmSystemLock(
|
||||
pShmNode = pFile->pInode->pShmNode;
|
||||
assert( sqlite3_mutex_held(pShmNode->mutex) || pShmNode->nRef==0 );
|
||||
|
||||
/* Shared locks never span more than one byte */
|
||||
assert( n==1 || lockType!=F_RDLCK );
|
||||
|
||||
/* Locks are within range */
|
||||
assert( n>=1 && n<=SQLITE_SHM_NLOCK );
|
||||
|
||||
if( pShmNode->h>=0 ){
|
||||
/* Initialize the locking parameters */
|
||||
memset(&f, 0, sizeof(f));
|
||||
@@ -4181,44 +4186,36 @@ static int unixShmSystemLock(
|
||||
|
||||
/* Update the global lock state and do debug tracing */
|
||||
#ifdef SQLITE_DEBUG
|
||||
if( ofst<UNIX_SHM_DMS ){
|
||||
u16 mask;
|
||||
|
||||
/* Shared locks never span more than one byte */
|
||||
assert( n==1 || lockType!=F_RDLCK );
|
||||
|
||||
/* Locks are within range */
|
||||
assert( n>=1 && n<=SQLITE_SHM_NLOCK );
|
||||
|
||||
OSTRACE(("SHM-LOCK "));
|
||||
mask = ofst>31 ? 0xffff : (1<<(ofst+n)) - (1<<ofst);
|
||||
if( rc==SQLITE_OK ){
|
||||
if( lockType==F_UNLCK ){
|
||||
OSTRACE(("unlock %d ok", ofst));
|
||||
pShmNode->exclMask &= ~mask;
|
||||
pShmNode->sharedMask &= ~mask;
|
||||
}else if( lockType==F_RDLCK ){
|
||||
OSTRACE(("read-lock %d ok", ofst));
|
||||
pShmNode->exclMask &= ~mask;
|
||||
pShmNode->sharedMask |= mask;
|
||||
}else{
|
||||
assert( lockType==F_WRLCK );
|
||||
OSTRACE(("write-lock %d ok", ofst));
|
||||
pShmNode->exclMask |= mask;
|
||||
pShmNode->sharedMask &= ~mask;
|
||||
}
|
||||
{ u16 mask;
|
||||
OSTRACE(("SHM-LOCK "));
|
||||
mask = ofst>31 ? 0xffff : (1<<(ofst+n)) - (1<<ofst);
|
||||
if( rc==SQLITE_OK ){
|
||||
if( lockType==F_UNLCK ){
|
||||
OSTRACE(("unlock %d ok", ofst));
|
||||
pShmNode->exclMask &= ~mask;
|
||||
pShmNode->sharedMask &= ~mask;
|
||||
}else if( lockType==F_RDLCK ){
|
||||
OSTRACE(("read-lock %d ok", ofst));
|
||||
pShmNode->exclMask &= ~mask;
|
||||
pShmNode->sharedMask |= mask;
|
||||
}else{
|
||||
if( lockType==F_UNLCK ){
|
||||
OSTRACE(("unlock %d failed", ofst));
|
||||
}else if( lockType==F_RDLCK ){
|
||||
OSTRACE(("read-lock failed"));
|
||||
}else{
|
||||
assert( lockType==F_WRLCK );
|
||||
OSTRACE(("write-lock %d failed", ofst));
|
||||
}
|
||||
assert( lockType==F_WRLCK );
|
||||
OSTRACE(("write-lock %d ok", ofst));
|
||||
pShmNode->exclMask |= mask;
|
||||
pShmNode->sharedMask &= ~mask;
|
||||
}
|
||||
OSTRACE((" - afterwards %03x,%03x\n",
|
||||
pShmNode->sharedMask, pShmNode->exclMask));
|
||||
}else{
|
||||
if( lockType==F_UNLCK ){
|
||||
OSTRACE(("unlock %d failed", ofst));
|
||||
}else if( lockType==F_RDLCK ){
|
||||
OSTRACE(("read-lock failed"));
|
||||
}else{
|
||||
assert( lockType==F_WRLCK );
|
||||
OSTRACE(("write-lock %d failed", ofst));
|
||||
}
|
||||
}
|
||||
OSTRACE((" - afterwards %03x,%03x\n",
|
||||
pShmNode->sharedMask, pShmNode->exclMask));
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -4392,38 +4389,30 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
|
||||
*/
|
||||
robustFchown(pShmNode->h, sStat.st_uid, sStat.st_gid);
|
||||
|
||||
/* Do not allow a read-only process to connect if there are no
|
||||
** writers, because a read-only process is unable to recover the
|
||||
** shm file following a system crash.
|
||||
*/
|
||||
/* Check to see if another process is holding the dead-man switch.
|
||||
** For a readonly_shm client, if no other process holds the DMS lock,
|
||||
** the file cannot be opened and SQLITE_CANTOPEN_DIRTYWAL is returned.
|
||||
** Or, for a read-write connection, if no other process holds a
|
||||
** DMS lock the file is truncated to zero bytes in size. */
|
||||
rc = SQLITE_OK;
|
||||
if( pShmNode->isReadonly ){
|
||||
if( !unixShmSystemLock(pDbFd, F_RDLCK, UNIX_SHM_DMS, UNIX_SHM_N_DMS) ){
|
||||
struct flock lock;
|
||||
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, &lockInfo)!=0 ) {
|
||||
rc = SQLITE_IOERR_LOCK;
|
||||
}else if( lock.l_type==F_UNLCK ){
|
||||
rc = SQLITE_CANTOPEN_DIRTYWAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we are able to grab the dead-man switch, that means this is the
|
||||
** first (write-enable) process to connect to the database. In that
|
||||
** case, truncate the shm file because the contents found on disk might
|
||||
** be invalid leftovers from a system crash. The shm will be rebuilt
|
||||
*/
|
||||
if( unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1)==SQLITE_OK ){
|
||||
}else if( unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1)==SQLITE_OK ){
|
||||
if( robust_ftruncate(pShmNode->h, 0) ){
|
||||
rc = unixLogError(SQLITE_IOERR_SHMOPEN, "ftruncate", zShmFilename);
|
||||
}
|
||||
}
|
||||
|
||||
/* Acquires locks to tell other processes that a this process is
|
||||
** running and therefore the shm is valid they do not need to run
|
||||
** recovery.
|
||||
*/
|
||||
if( rc==SQLITE_OK ){
|
||||
unsigned r;
|
||||
rc = unixShmSystemLock(pDbFd, F_RDLCK, UNIX_SHM_DMS, 1);
|
||||
sqlite3_randomness(sizeof(r), &r);
|
||||
r = 1 + (r%(UNIX_SHM_N_DMS-1));
|
||||
(void)unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS+r, 1);
|
||||
}
|
||||
if( rc ) goto shm_open_err;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user