/ Check-in [5492f457]
Login
SQLite training in Houston TX on 2019-11-05 (details)
Part of the 2019 Tcl Conference

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment: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.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | readonly-wal-recovery
Files: files | file ages | folders
SHA3-256: 5492f457dc7cc5c416de4b4e61e84bd2f10b4e6ce54011b7a60feb47f629c923
User & Date: dan 2017-10-26 17:05:22
Context
2017-10-26
17:34
Fix an error in the previous commit on this branch. check-in: f71dfee0 user: dan tags: readonly-wal-recovery
17:05
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. check-in: 5492f457 user: dan tags: readonly-wal-recovery
2017-10-25
23:28
Use extra locks to prevent a readonly_shm=1 process from connecting to a WAL-mode database if there are no writers. check-in: 35d97908 user: drh tags: readonly-wal-recovery
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/os_unix.c.

  4141   4141   };
  4142   4142   
  4143   4143   /*
  4144   4144   ** Constants used for locking
  4145   4145   */
  4146   4146   #define UNIX_SHM_BASE   ((22+SQLITE_SHM_NLOCK)*4)         /* first lock byte */
  4147   4147   #define UNIX_SHM_DMS    (UNIX_SHM_BASE+SQLITE_SHM_NLOCK)  /* deadman switch */
  4148         -#define UNIX_SHM_N_DMS  1000000000                        /* Size of the DMS */
  4149   4148   
  4150   4149   /*
  4151   4150   ** Apply posix advisory locks for all bytes from ofst through ofst+n-1.
  4152   4151   **
  4153   4152   ** Locks block if the mask is exactly UNIX_SHM_C and are non-blocking
  4154   4153   ** otherwise.
  4155   4154   */
................................................................................
  4163   4162     struct flock f;        /* The posix advisory locking structure */
  4164   4163     int rc = SQLITE_OK;    /* Result code form fcntl() */
  4165   4164   
  4166   4165     /* Access to the unixShmNode object is serialized by the caller */
  4167   4166     pShmNode = pFile->pInode->pShmNode;
  4168   4167     assert( sqlite3_mutex_held(pShmNode->mutex) || pShmNode->nRef==0 );
  4169   4168   
         4169  +  /* Shared locks never span more than one byte */
         4170  +  assert( n==1 || lockType!=F_RDLCK );
         4171  +
         4172  +  /* Locks are within range */
         4173  +  assert( n>=1 && n<=SQLITE_SHM_NLOCK );
         4174  +
  4170   4175     if( pShmNode->h>=0 ){
  4171   4176       /* Initialize the locking parameters */
  4172   4177       memset(&f, 0, sizeof(f));
  4173   4178       f.l_type = lockType;
  4174   4179       f.l_whence = SEEK_SET;
  4175   4180       f.l_start = ofst;
  4176   4181       f.l_len = n;
................................................................................
  4177   4182   
  4178   4183       rc = osFcntl(pShmNode->h, F_SETLK, &f);
  4179   4184       rc = (rc!=(-1)) ? SQLITE_OK : SQLITE_BUSY;
  4180   4185     }
  4181   4186   
  4182   4187     /* Update the global lock state and do debug tracing */
  4183   4188   #ifdef SQLITE_DEBUG
  4184         -  if( ofst<UNIX_SHM_DMS ){
  4185         -    u16 mask;
  4186         -
  4187         -    /* Shared locks never span more than one byte */
  4188         -    assert( n==1 || lockType!=F_RDLCK );
  4189         -
  4190         -    /* Locks are within range */
  4191         -    assert( n>=1 && n<=SQLITE_SHM_NLOCK );
  4192         -
  4193         -    OSTRACE(("SHM-LOCK "));
  4194         -    mask = ofst>31 ? 0xffff : (1<<(ofst+n)) - (1<<ofst);
  4195         -    if( rc==SQLITE_OK ){
  4196         -      if( lockType==F_UNLCK ){
  4197         -        OSTRACE(("unlock %d ok", ofst));
  4198         -        pShmNode->exclMask &= ~mask;
  4199         -        pShmNode->sharedMask &= ~mask;
  4200         -      }else if( lockType==F_RDLCK ){
  4201         -        OSTRACE(("read-lock %d ok", ofst));
  4202         -        pShmNode->exclMask &= ~mask;
  4203         -        pShmNode->sharedMask |= mask;
  4204         -      }else{
  4205         -        assert( lockType==F_WRLCK );
  4206         -        OSTRACE(("write-lock %d ok", ofst));
  4207         -        pShmNode->exclMask |= mask;
  4208         -        pShmNode->sharedMask &= ~mask;
  4209         -      }
         4189  +  { u16 mask;
         4190  +  OSTRACE(("SHM-LOCK "));
         4191  +  mask = ofst>31 ? 0xffff : (1<<(ofst+n)) - (1<<ofst);
         4192  +  if( rc==SQLITE_OK ){
         4193  +    if( lockType==F_UNLCK ){
         4194  +      OSTRACE(("unlock %d ok", ofst));
         4195  +      pShmNode->exclMask &= ~mask;
         4196  +      pShmNode->sharedMask &= ~mask;
         4197  +    }else if( lockType==F_RDLCK ){
         4198  +      OSTRACE(("read-lock %d ok", ofst));
         4199  +      pShmNode->exclMask &= ~mask;
         4200  +      pShmNode->sharedMask |= mask;
         4201  +    }else{
         4202  +      assert( lockType==F_WRLCK );
         4203  +      OSTRACE(("write-lock %d ok", ofst));
         4204  +      pShmNode->exclMask |= mask;
         4205  +      pShmNode->sharedMask &= ~mask;
         4206  +    }
         4207  +  }else{
         4208  +    if( lockType==F_UNLCK ){
         4209  +      OSTRACE(("unlock %d failed", ofst));
         4210  +    }else if( lockType==F_RDLCK ){
         4211  +      OSTRACE(("read-lock failed"));
  4210   4212       }else{
  4211         -      if( lockType==F_UNLCK ){
  4212         -        OSTRACE(("unlock %d failed", ofst));
  4213         -      }else if( lockType==F_RDLCK ){
  4214         -        OSTRACE(("read-lock failed"));
  4215         -      }else{
  4216         -        assert( lockType==F_WRLCK );
  4217         -        OSTRACE(("write-lock %d failed", ofst));
  4218         -      }
         4213  +      assert( lockType==F_WRLCK );
         4214  +      OSTRACE(("write-lock %d failed", ofst));
  4219   4215       }
  4220         -    OSTRACE((" - afterwards %03x,%03x\n",
  4221         -             pShmNode->sharedMask, pShmNode->exclMask));
         4216  +  }
         4217  +  OSTRACE((" - afterwards %03x,%03x\n",
         4218  +           pShmNode->sharedMask, pShmNode->exclMask));
  4222   4219     }
  4223   4220   #endif
  4224   4221   
  4225   4222     return rc;        
  4226   4223   }
  4227   4224   
  4228   4225   /*
................................................................................
  4388   4385   
  4389   4386         /* If this process is running as root, make sure that the SHM file
  4390   4387         ** is owned by the same user that owns the original database.  Otherwise,
  4391   4388         ** the original owner will not be able to connect.
  4392   4389         */
  4393   4390         robustFchown(pShmNode->h, sStat.st_uid, sStat.st_gid);
  4394   4391     
  4395         -      /* Do not allow a read-only process to connect if there are no
  4396         -      ** writers, because a read-only process is unable to recover the
  4397         -      ** shm file following a system crash.
  4398         -      */
         4392  +      /* Check to see if another process is holding the dead-man switch.
         4393  +      ** For a readonly_shm client, if no other process holds the DMS lock,
         4394  +      ** the file cannot be opened and SQLITE_CANTOPEN_DIRTYWAL is returned.
         4395  +      ** Or, for a read-write connection, if no other process holds a
         4396  +      ** DMS lock the file is truncated to zero bytes in size.  */
  4399   4397         rc = SQLITE_OK;
  4400   4398         if( pShmNode->isReadonly ){
  4401         -        if( !unixShmSystemLock(pDbFd, F_RDLCK, UNIX_SHM_DMS, UNIX_SHM_N_DMS) ){
         4399  +        struct flock lock;
         4400  +        lock.l_whence = SEEK_SET;
         4401  +        lock.l_start = UNIX_SHM_DMS;
         4402  +        lock.l_len = 1;
         4403  +        lock.l_type = F_WRLCK;
         4404  +        if( osFcntl(pShmNode->h, F_GETLK, &lockInfo)!=0 ) {
         4405  +          rc = SQLITE_IOERR_LOCK;
         4406  +        }else if( lock.l_type==F_UNLCK ){
  4402   4407             rc = SQLITE_CANTOPEN_DIRTYWAL;
  4403   4408           }
  4404         -      }
  4405         -
  4406         -      /* If we are able to grab the dead-man switch, that means this is the
  4407         -      ** first (write-enable) process to connect to the database.  In that
  4408         -      ** case, truncate the shm file because the contents found on disk might
  4409         -      ** be invalid leftovers from a system crash.  The shm will be rebuilt
  4410         -      */
  4411         -      if( unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1)==SQLITE_OK ){
         4409  +      }else if( unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1)==SQLITE_OK ){
  4412   4410           if( robust_ftruncate(pShmNode->h, 0) ){
  4413   4411             rc = unixLogError(SQLITE_IOERR_SHMOPEN, "ftruncate", zShmFilename);
  4414   4412           }
  4415   4413         }
  4416         -
  4417         -      /* Acquires locks to tell other processes that a this process is
  4418         -      ** running and therefore the shm is valid they do not need to run
  4419         -      ** recovery.
  4420         -      */
  4421   4414         if( rc==SQLITE_OK ){
  4422         -        unsigned r;
  4423   4415           rc = unixShmSystemLock(pDbFd, F_RDLCK, UNIX_SHM_DMS, 1);
  4424         -        sqlite3_randomness(sizeof(r), &r);
  4425         -        r = 1 + (r%(UNIX_SHM_N_DMS-1));
  4426         -        (void)unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS+r, 1);
  4427   4416         }
  4428   4417         if( rc ) goto shm_open_err;
  4429   4418       }
  4430   4419     }
  4431   4420   
  4432   4421     /* Make the new connection a child of the unixShmNode */
  4433   4422     p->pShmNode = pShmNode;