/ Check-in [28348f2a]
Login

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

Overview
Comment:Only error out on a database file move when attempting to start a write transaction. Assume read transactions are still safe. And make the error SQLITE_READONLY_DBMOVED instead of SQLITE_IOERR_NODB.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | detect-moved-db
Files: files | file ages | folders
SHA1: 28348f2ada98c616241a51aecb70b63e87e6ddbb
User & Date: drh 2013-12-06 17:23:38
Context
2013-12-06
19:58
Add the SQLITE_READONLY_DBMOVED error code to the sqlite3ErrName() function. check-in: 7789f801 user: mistachkin tags: detect-moved-db
17:23
Only error out on a database file move when attempting to start a write transaction. Assume read transactions are still safe. And make the error SQLITE_READONLY_DBMOVED instead of SQLITE_IOERR_NODB. check-in: 28348f2a user: drh tags: detect-moved-db
15:37
Add code to detect if the database file is moved or deleted out from under SQLite and return an SQLITE_IOERR_NODB. check-in: 8759a8e4 user: drh tags: detect-moved-db
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/pager.c.

4795
4796
4797
4798
4799
4800
4801
4802
4803
4804
4805
4806
4807
4808
4809
....
4810
4811
4812
4813
4814
4815
4816
4817
4818
4819
4820
4821
4822
4823
4824
....
4987
4988
4989
4990
4991
4992
4993
4994
4995
4996
4997
4998
4999
5000
5001
5002
5003
5004
....
5494
5495
5496
5497
5498
5499
5500





5501
5502
5503
5504
5505
5506
5507

5508
5509
5510
5511
5512
5513
5514
  *ppPager = pPager;
  return SQLITE_OK;
}


/* Verify that the database file has not be deleted or renamed out from
** under the pager.  Return SQLITE_OK if the database is still were it ought
** to be on disk.  Return non-zero (SQLITE_IOERR_NODB or some other error
** code from sqlite3OsAccess()) if the database has gone missing.
*/
static int databaseIsUnmoved(Pager *pPager){
  const int fixedFlags = SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN |
                         SQLITE_IOCAP_UNMOVABLE_WHEN_OPEN;
  int dc;
  int x = 0, rc;
................................................................................

  if( pPager->tempFile ) return SQLITE_OK;
  if( pPager->dbSize==0 ) return SQLITE_OK;
  assert( pPager->zFilename && pPager->zFilename[0] );
  dc = sqlite3OsDeviceCharacteristics(pPager->fd);
  if( (dc&fixedFlags)==fixedFlags ) return SQLITE_OK;
  rc = sqlite3OsAccess(pPager->pVfs, pPager->zFilename, SQLITE_ACCESS_EXISTS, &x);
  if( rc==SQLITE_OK && !x ) rc = SQLITE_IOERR_NODB;
  return rc;
}


/*
** This function is called after transitioning from PAGER_UNLOCK to
** PAGER_SHARED state. It tests if there is a hot journal present in
................................................................................

    rc = pager_wait_on_lock(pPager, SHARED_LOCK);
    if( rc!=SQLITE_OK ){
      assert( pPager->eLock==NO_LOCK || pPager->eLock==UNKNOWN_LOCK );
      goto failed;
    }

    /* Verify that the database is unmoved and undeleted */
    rc = databaseIsUnmoved(pPager);
    if( rc ) goto failed;

    /* If a journal file exists, and there is no RESERVED lock on the
    ** database file, then it either needs to be played back or deleted.
    */
    if( pPager->eLock<=SHARED_LOCK ){
      rc = hasHotJournal(pPager, &bHotJournal);
    }
    if( rc!=SQLITE_OK ){
................................................................................
      }else{
        const int flags =                   /* VFS flags to open journal file */
          SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|
          (pPager->tempFile ? 
            (SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL):
            (SQLITE_OPEN_MAIN_JOURNAL)
          );





  #ifdef SQLITE_ENABLE_ATOMIC_WRITE
        rc = sqlite3JournalOpen(
            pVfs, pPager->zJournal, pPager->jfd, flags, jrnlBufferSize(pPager)
        );
  #else
        rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, flags, 0);
  #endif

      }
      assert( rc!=SQLITE_OK || isOpen(pPager->jfd) );
    }
  
  
    /* Write the first journal header to the journal file and open 
    ** the sub-journal if necessary.







|







 







|







 







<
<
<
<







 







>
>
>
>
>
|
|
|
|
|
|
|
>







4795
4796
4797
4798
4799
4800
4801
4802
4803
4804
4805
4806
4807
4808
4809
....
4810
4811
4812
4813
4814
4815
4816
4817
4818
4819
4820
4821
4822
4823
4824
....
4987
4988
4989
4990
4991
4992
4993




4994
4995
4996
4997
4998
4999
5000
....
5490
5491
5492
5493
5494
5495
5496
5497
5498
5499
5500
5501
5502
5503
5504
5505
5506
5507
5508
5509
5510
5511
5512
5513
5514
5515
5516
  *ppPager = pPager;
  return SQLITE_OK;
}


/* Verify that the database file has not be deleted or renamed out from
** under the pager.  Return SQLITE_OK if the database is still were it ought
** to be on disk.  Return non-zero (SQLITE_READONLY_DBMOVED or some other error
** code from sqlite3OsAccess()) if the database has gone missing.
*/
static int databaseIsUnmoved(Pager *pPager){
  const int fixedFlags = SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN |
                         SQLITE_IOCAP_UNMOVABLE_WHEN_OPEN;
  int dc;
  int x = 0, rc;
................................................................................

  if( pPager->tempFile ) return SQLITE_OK;
  if( pPager->dbSize==0 ) return SQLITE_OK;
  assert( pPager->zFilename && pPager->zFilename[0] );
  dc = sqlite3OsDeviceCharacteristics(pPager->fd);
  if( (dc&fixedFlags)==fixedFlags ) return SQLITE_OK;
  rc = sqlite3OsAccess(pPager->pVfs, pPager->zFilename, SQLITE_ACCESS_EXISTS, &x);
  if( rc==SQLITE_OK && !x ) rc = SQLITE_READONLY_DBMOVED;
  return rc;
}


/*
** This function is called after transitioning from PAGER_UNLOCK to
** PAGER_SHARED state. It tests if there is a hot journal present in
................................................................................

    rc = pager_wait_on_lock(pPager, SHARED_LOCK);
    if( rc!=SQLITE_OK ){
      assert( pPager->eLock==NO_LOCK || pPager->eLock==UNKNOWN_LOCK );
      goto failed;
    }





    /* If a journal file exists, and there is no RESERVED lock on the
    ** database file, then it either needs to be played back or deleted.
    */
    if( pPager->eLock<=SHARED_LOCK ){
      rc = hasHotJournal(pPager, &bHotJournal);
    }
    if( rc!=SQLITE_OK ){
................................................................................
      }else{
        const int flags =                   /* VFS flags to open journal file */
          SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|
          (pPager->tempFile ? 
            (SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL):
            (SQLITE_OPEN_MAIN_JOURNAL)
          );

        /* Verify that the database still has the same name as it did when
        ** it was originally opened. */
        rc = databaseIsUnmoved(pPager);
        if( rc==SQLITE_OK ){
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
          rc = sqlite3JournalOpen(
              pVfs, pPager->zJournal, pPager->jfd, flags, jrnlBufferSize(pPager)
          );
#else
          rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, flags, 0);
#endif
        }
      }
      assert( rc!=SQLITE_OK || isOpen(pPager->jfd) );
    }
  
  
    /* Write the first journal header to the journal file and open 
    ** the sub-journal if necessary.

Changes to src/sqlite.h.in.

471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489

490
491
492
493
494
495
496
#define SQLITE_IOERR_SHMLOCK           (SQLITE_IOERR | (20<<8))
#define SQLITE_IOERR_SHMMAP            (SQLITE_IOERR | (21<<8))
#define SQLITE_IOERR_SEEK              (SQLITE_IOERR | (22<<8))
#define SQLITE_IOERR_DELETE_NOENT      (SQLITE_IOERR | (23<<8))
#define SQLITE_IOERR_MMAP              (SQLITE_IOERR | (24<<8))
#define SQLITE_IOERR_GETTEMPPATH       (SQLITE_IOERR | (25<<8))
#define SQLITE_IOERR_CONVPATH          (SQLITE_IOERR | (26<<8))
#define SQLITE_IOERR_NODB              (SQLITE_IOERR | (27<<8))
#define SQLITE_LOCKED_SHAREDCACHE      (SQLITE_LOCKED |  (1<<8))
#define SQLITE_BUSY_RECOVERY           (SQLITE_BUSY   |  (1<<8))
#define SQLITE_BUSY_SNAPSHOT           (SQLITE_BUSY   |  (2<<8))
#define SQLITE_CANTOPEN_NOTEMPDIR      (SQLITE_CANTOPEN | (1<<8))
#define SQLITE_CANTOPEN_ISDIR          (SQLITE_CANTOPEN | (2<<8))
#define SQLITE_CANTOPEN_FULLPATH       (SQLITE_CANTOPEN | (3<<8))
#define SQLITE_CANTOPEN_CONVPATH       (SQLITE_CANTOPEN | (4<<8))
#define SQLITE_CORRUPT_VTAB            (SQLITE_CORRUPT | (1<<8))
#define SQLITE_READONLY_RECOVERY       (SQLITE_READONLY | (1<<8))
#define SQLITE_READONLY_CANTLOCK       (SQLITE_READONLY | (2<<8))
#define SQLITE_READONLY_ROLLBACK       (SQLITE_READONLY | (3<<8))

#define SQLITE_ABORT_ROLLBACK          (SQLITE_ABORT | (2<<8))
#define SQLITE_CONSTRAINT_CHECK        (SQLITE_CONSTRAINT | (1<<8))
#define SQLITE_CONSTRAINT_COMMITHOOK   (SQLITE_CONSTRAINT | (2<<8))
#define SQLITE_CONSTRAINT_FOREIGNKEY   (SQLITE_CONSTRAINT | (3<<8))
#define SQLITE_CONSTRAINT_FUNCTION     (SQLITE_CONSTRAINT | (4<<8))
#define SQLITE_CONSTRAINT_NOTNULL      (SQLITE_CONSTRAINT | (5<<8))
#define SQLITE_CONSTRAINT_PRIMARYKEY   (SQLITE_CONSTRAINT | (6<<8))







<











>







471
472
473
474
475
476
477

478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
#define SQLITE_IOERR_SHMLOCK           (SQLITE_IOERR | (20<<8))
#define SQLITE_IOERR_SHMMAP            (SQLITE_IOERR | (21<<8))
#define SQLITE_IOERR_SEEK              (SQLITE_IOERR | (22<<8))
#define SQLITE_IOERR_DELETE_NOENT      (SQLITE_IOERR | (23<<8))
#define SQLITE_IOERR_MMAP              (SQLITE_IOERR | (24<<8))
#define SQLITE_IOERR_GETTEMPPATH       (SQLITE_IOERR | (25<<8))
#define SQLITE_IOERR_CONVPATH          (SQLITE_IOERR | (26<<8))

#define SQLITE_LOCKED_SHAREDCACHE      (SQLITE_LOCKED |  (1<<8))
#define SQLITE_BUSY_RECOVERY           (SQLITE_BUSY   |  (1<<8))
#define SQLITE_BUSY_SNAPSHOT           (SQLITE_BUSY   |  (2<<8))
#define SQLITE_CANTOPEN_NOTEMPDIR      (SQLITE_CANTOPEN | (1<<8))
#define SQLITE_CANTOPEN_ISDIR          (SQLITE_CANTOPEN | (2<<8))
#define SQLITE_CANTOPEN_FULLPATH       (SQLITE_CANTOPEN | (3<<8))
#define SQLITE_CANTOPEN_CONVPATH       (SQLITE_CANTOPEN | (4<<8))
#define SQLITE_CORRUPT_VTAB            (SQLITE_CORRUPT | (1<<8))
#define SQLITE_READONLY_RECOVERY       (SQLITE_READONLY | (1<<8))
#define SQLITE_READONLY_CANTLOCK       (SQLITE_READONLY | (2<<8))
#define SQLITE_READONLY_ROLLBACK       (SQLITE_READONLY | (3<<8))
#define SQLITE_READONLY_DBMOVED        (SQLITE_READONLY | (4<<8))
#define SQLITE_ABORT_ROLLBACK          (SQLITE_ABORT | (2<<8))
#define SQLITE_CONSTRAINT_CHECK        (SQLITE_CONSTRAINT | (1<<8))
#define SQLITE_CONSTRAINT_COMMITHOOK   (SQLITE_CONSTRAINT | (2<<8))
#define SQLITE_CONSTRAINT_FOREIGNKEY   (SQLITE_CONSTRAINT | (3<<8))
#define SQLITE_CONSTRAINT_FUNCTION     (SQLITE_CONSTRAINT | (4<<8))
#define SQLITE_CONSTRAINT_NOTNULL      (SQLITE_CONSTRAINT | (5<<8))
#define SQLITE_CONSTRAINT_PRIMARYKEY   (SQLITE_CONSTRAINT | (6<<8))

Changes to src/test_vfstrace.c.

258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
    case SQLITE_IOERR_SHMOPEN:      zVal = "SQLITE_IOERR_SHMOPEN";      break;
    case SQLITE_IOERR_SHMSIZE:      zVal = "SQLITE_IOERR_SHMSIZE";      break;
    case SQLITE_IOERR_SHMLOCK:      zVal = "SQLITE_IOERR_SHMLOCK";      break;
    case SQLITE_IOERR_SHMMAP:       zVal = "SQLITE_IOERR_SHMMAP";       break;
    case SQLITE_IOERR_SEEK:         zVal = "SQLITE_IOERR_SEEK";         break;
    case SQLITE_IOERR_GETTEMPPATH:  zVal = "SQLITE_IOERR_GETTEMPPATH";  break;
    case SQLITE_IOERR_CONVPATH:     zVal = "SQLITE_IOERR_CONVPATH";     break;
    case SQLITE_IOERR_NODB:         zVal = "SQLITE_IOERR_NODB";         break;
    case SQLITE_LOCKED_SHAREDCACHE: zVal = "SQLITE_LOCKED_SHAREDCACHE"; break;
    case SQLITE_BUSY_RECOVERY:      zVal = "SQLITE_BUSY_RECOVERY";      break;
    case SQLITE_CANTOPEN_NOTEMPDIR: zVal = "SQLITE_CANTOPEN_NOTEMPDIR"; break;
    default: {
       sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", rc);
       zVal = zBuf;
       break;







|







258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
    case SQLITE_IOERR_SHMOPEN:      zVal = "SQLITE_IOERR_SHMOPEN";      break;
    case SQLITE_IOERR_SHMSIZE:      zVal = "SQLITE_IOERR_SHMSIZE";      break;
    case SQLITE_IOERR_SHMLOCK:      zVal = "SQLITE_IOERR_SHMLOCK";      break;
    case SQLITE_IOERR_SHMMAP:       zVal = "SQLITE_IOERR_SHMMAP";       break;
    case SQLITE_IOERR_SEEK:         zVal = "SQLITE_IOERR_SEEK";         break;
    case SQLITE_IOERR_GETTEMPPATH:  zVal = "SQLITE_IOERR_GETTEMPPATH";  break;
    case SQLITE_IOERR_CONVPATH:     zVal = "SQLITE_IOERR_CONVPATH";     break;
    case SQLITE_READONLY_DBMOVED:   zVal = "SQLITE_READONLY_DBMOVED";   break;
    case SQLITE_LOCKED_SHAREDCACHE: zVal = "SQLITE_LOCKED_SHAREDCACHE"; break;
    case SQLITE_BUSY_RECOVERY:      zVal = "SQLITE_BUSY_RECOVERY";      break;
    case SQLITE_CANTOPEN_NOTEMPDIR: zVal = "SQLITE_CANTOPEN_NOTEMPDIR"; break;
    default: {
       sqlite3_snprintf(sizeof(zBuf), zBuf, "%d", rc);
       zVal = zBuf;
       break;

Changes to test/pager4.test.

14
15
16
17
18
19
20


21
22
23
24
25




26
27
28
29
30




31

32








































33
#

if {$tcl_platform(platform)!="unix"} return

set testdir [file dirname $argv0]
source $testdir/tester.tcl



do_execsql_test pager4-1.1 {
  CREATE TABLE t1(a,b,c);
  INSERT INTO t1 VALUES(673,'stone','philips');
  SELECT * FROM t1;
} {673 stone philips}




file delete -force test-xyz.db
file rename test.db test-xyz.db
do_catchsql_test pager4-1.2 {
  SELECT * FROM t1;
} {1 {disk I/O error}}















































finish_test







>
>





>
>
>
>




<
>
>
>
>

>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#

if {$tcl_platform(platform)!="unix"} return

set testdir [file dirname $argv0]
source $testdir/tester.tcl

# Create a database file for testing
#
do_execsql_test pager4-1.1 {
  CREATE TABLE t1(a,b,c);
  INSERT INTO t1 VALUES(673,'stone','philips');
  SELECT * FROM t1;
} {673 stone philips}

# After renaming the database file while it is open, one can still
# read from the database, but writing returns a READONLY error.
#
file delete -force test-xyz.db
file rename test.db test-xyz.db
do_catchsql_test pager4-1.2 {
  SELECT * FROM t1;

} {0 {673 stone philips}}
do_catchsql_test pager4-1.3 {
  UPDATE t1 SET a=537;
} {1 {attempt to write a readonly database}}

# Changing the name back clears the READONLY error
#
file rename test-xyz.db test.db
do_catchsql_test pager4-1.4 {
  SELECT * FROM t1;
} {0 {673 stone philips}}
do_catchsql_test pager4-1.5 {
  UPDATE t1 SET a=537;
  SELECT * FROM t1;
} {0 {537 stone philips}}

# We can write to a renamed database if journal_mode=OFF or
# journal_mode=MEMORY.
#
file rename test.db test-xyz.db
do_catchsql_test pager4-1.6 {
  PRAGMA journal_mode=OFF;
  UPDATE t1 SET a=107;
  SELECT * FROM t1;
} {0 {off 107 stone philips}}
do_catchsql_test pager4-1.7 {
  PRAGMA journal_mode=MEMORY;
  UPDATE t1 SET b='magpie';
  SELECT * FROM t1;
} {0 {memory 107 magpie philips}}

# Any other journal mode gives a READONLY error
#
do_catchsql_test pager4-1.8 {
  PRAGMA journal_mode=DELETE;
  UPDATE t1 SET c='jaguar';
} {1 {attempt to write a readonly database}}
do_catchsql_test pager4-1.9 {
  PRAGMA journal_mode=TRUNCATE;
  UPDATE t1 SET c='jaguar';
} {1 {attempt to write a readonly database}}
do_catchsql_test pager4-1.10 {
  PRAGMA journal_mode=PERSIST;
  UPDATE t1 SET c='jaguar';
} {1 {attempt to write a readonly database}}


finish_test