SQLite

Check-in [5492f457dc]
Login

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
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.656
Context
2017-10-26
17:34
Fix an error in the previous commit on this branch. (check-in: f71dfee06c 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: 5492f457dc 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: 35d979082b user: drh tags: readonly-wal-recovery)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/os_unix.c.
4141
4142
4143
4144
4145
4146
4147
4148
4149
4150
4151
4152
4153
4154
4155
};

/*
** Constants used for locking
*/
#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.
**
** Locks block if the mask is exactly UNIX_SHM_C and are non-blocking
** otherwise.
*/







<







4141
4142
4143
4144
4145
4146
4147

4148
4149
4150
4151
4152
4153
4154
};

/*
** Constants used for locking
*/
#define UNIX_SHM_BASE   ((22+SQLITE_SHM_NLOCK)*4)         /* first lock byte */
#define UNIX_SHM_DMS    (UNIX_SHM_BASE+SQLITE_SHM_NLOCK)  /* deadman switch */


/*
** Apply posix advisory locks for all bytes from ofst through ofst+n-1.
**
** Locks block if the mask is exactly UNIX_SHM_C and are non-blocking
** otherwise.
*/
4163
4164
4165
4166
4167
4168
4169






4170
4171
4172
4173
4174
4175
4176
4177
4178
4179
4180
4181
4182
4183
4184
4185
4186
4187
4188
4189
4190
4191
4192
4193
4194
4195
4196
4197
4198
4199
4200
4201
4202
4203
4204
4205
4206
4207
4208
4209
4210
4211
4212
4213
4214
4215
4216
4217
4218
4219
4220
4221
4222
4223
4224
4225
4226
4227
4228
  struct flock f;        /* The posix advisory locking structure */
  int rc = SQLITE_OK;    /* Result code form fcntl() */

  /* Access to the unixShmNode object is serialized by the caller */
  pShmNode = pFile->pInode->pShmNode;
  assert( sqlite3_mutex_held(pShmNode->mutex) || pShmNode->nRef==0 );







  if( pShmNode->h>=0 ){
    /* Initialize the locking parameters */
    memset(&f, 0, sizeof(f));
    f.l_type = lockType;
    f.l_whence = SEEK_SET;
    f.l_start = ofst;
    f.l_len = n;

    rc = osFcntl(pShmNode->h, F_SETLK, &f);
    rc = (rc!=(-1)) ? SQLITE_OK : SQLITE_BUSY;
  }

  /* 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;
      }
    }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

  return rc;        
}

/*







>
>
>
>
>
>














<
|
<
<
<
<
<
<
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|







4162
4163
4164
4165
4166
4167
4168
4169
4170
4171
4172
4173
4174
4175
4176
4177
4178
4179
4180
4181
4182
4183
4184
4185
4186
4187
4188

4189







4190
4191
4192
4193
4194
4195
4196
4197
4198
4199
4200
4201
4202
4203
4204
4205
4206
4207
4208
4209
4210
4211
4212
4213
4214
4215
4216
4217
4218
4219
4220
4221
4222
4223
4224
4225
  struct flock f;        /* The posix advisory locking structure */
  int rc = SQLITE_OK;    /* Result code form fcntl() */

  /* Access to the unixShmNode object is serialized by the caller */
  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));
    f.l_type = lockType;
    f.l_whence = SEEK_SET;
    f.l_start = ofst;
    f.l_len = n;

    rc = osFcntl(pShmNode->h, F_SETLK, &f);
    rc = (rc!=(-1)) ? SQLITE_OK : SQLITE_BUSY;
  }

  /* Update the global lock state and do debug tracing */
#ifdef SQLITE_DEBUG

  { 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{
      assert( lockType==F_WRLCK );
      OSTRACE(("write-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));
    }
  }
  OSTRACE((" - afterwards %03x,%03x\n",
           pShmNode->sharedMask, pShmNode->exclMask));
  }
#endif

  return rc;        
}

/*
4388
4389
4390
4391
4392
4393
4394
4395
4396
4397
4398

4399
4400


4401





4402
4403
4404
4405
4406
4407
4408
4409
4410
4411
4412
4413
4414
4415
4416
4417
4418
4419
4420
4421
4422
4423
4424
4425
4426
4427
4428
4429
4430
4431
4432
4433

      /* If this process is running as root, make sure that the SHM file
      ** is owned by the same user that owns the original database.  Otherwise,
      ** the original owner will not be able to connect.
      */
      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.
      */

      rc = SQLITE_OK;
      if( pShmNode->isReadonly ){


        if( !unixShmSystemLock(pDbFd, F_RDLCK, UNIX_SHM_DMS, UNIX_SHM_N_DMS) ){





          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 ){
        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;
    }
  }

  /* Make the new connection a child of the unixShmNode */
  p->pShmNode = pShmNode;







|
|
|
|
>


>
>
|
>
>
>
>
>


<
<
<
<
<
<
<
|




<
<
<
<
<

<

<
<
<







4385
4386
4387
4388
4389
4390
4391
4392
4393
4394
4395
4396
4397
4398
4399
4400
4401
4402
4403
4404
4405
4406
4407
4408







4409
4410
4411
4412
4413





4414

4415



4416
4417
4418
4419
4420
4421
4422

      /* If this process is running as root, make sure that the SHM file
      ** is owned by the same user that owns the original database.  Otherwise,
      ** the original owner will not be able to connect.
      */
      robustFchown(pShmNode->h, sStat.st_uid, sStat.st_gid);
  
      /* 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 ){
        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;
        }







      }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);
        }
      }





      if( rc==SQLITE_OK ){

        rc = unixShmSystemLock(pDbFd, F_RDLCK, UNIX_SHM_DMS, 1);



      }
      if( rc ) goto shm_open_err;
    }
  }

  /* Make the new connection a child of the unixShmNode */
  p->pShmNode = pShmNode;