/ Check-in [2b1884dc]
Login

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

Overview
Comment:Actually look at i-node numbers to determine whether or not the database file has moved.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | detect-moved-db
Files: files | file ages | folders
SHA1: 2b1884dc14f9a04a04eebb3245fbe0daaff399eb
User & Date: drh 2013-12-07 12:29:22
Context
2013-12-07
16:45
Back out the new device capability. The determination of whether or not a file has moved is now done strictly using a file-control. Closed-Leaf check-in: 9c59f5af user: drh tags: detect-moved-db
12:29
Actually look at i-node numbers to determine whether or not the database file has moved. check-in: 2b1884dc user: drh tags: detect-moved-db
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
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/os_unix.c.

  1311   1311     }else{
  1312   1312       pInode->nRef++;
  1313   1313     }
  1314   1314     *ppInode = pInode;
  1315   1315     return SQLITE_OK;
  1316   1316   }
  1317   1317   
         1318  +/*
         1319  +** Return TRUE if pFile has been renamed or unlinked since it was first opened.
         1320  +*/
         1321  +static int fileHasMoved(unixFile *pFile){
         1322  +  struct stat buf;
         1323  +  return pFile->pInode!=0 &&
         1324  +         (osStat(pFile->zPath, &buf)!=0 || buf.st_ino!=pFile->pInode->fileId.ino);
         1325  +}
         1326  +
  1318   1327   
  1319   1328   /*
  1320   1329   ** Check a unixFile that is a database.  Verify the following:
  1321   1330   **
  1322   1331   ** (1) There is exactly one hard link on the file
  1323   1332   ** (2) The file is not a symbolic link
  1324   1333   ** (3) The file has not been renamed or unlinked
................................................................................
  1345   1354       return;
  1346   1355     }
  1347   1356     if( buf.st_nlink>1 ){
  1348   1357       sqlite3_log(SQLITE_WARNING, "multiple links to file: %s", pFile->zPath);
  1349   1358       pFile->ctrlFlags |= UNIXFILE_WARNED;
  1350   1359       return;
  1351   1360     }
  1352         -  if( pFile->pInode!=0
  1353         -   && ((rc = osStat(pFile->zPath, &buf))!=0
  1354         -       || buf.st_ino!=pFile->pInode->fileId.ino)
  1355         -  ){
         1361  +  if( fileHasMoved(pFile) ){
  1356   1362       sqlite3_log(SQLITE_WARNING, "file renamed while open: %s", pFile->zPath);
  1357   1363       pFile->ctrlFlags |= UNIXFILE_WARNED;
  1358   1364       return;
  1359   1365     }
  1360   1366   }
  1361   1367   
  1362   1368   
................................................................................
  3796   3802       case SQLITE_FCNTL_TEMPFILENAME: {
  3797   3803         char *zTFile = sqlite3_malloc( pFile->pVfs->mxPathname );
  3798   3804         if( zTFile ){
  3799   3805           unixGetTempname(pFile->pVfs->mxPathname, zTFile);
  3800   3806           *(char**)pArg = zTFile;
  3801   3807         }
  3802   3808         return SQLITE_OK;
         3809  +    }
         3810  +    case SQLITE_FCNTL_HAS_MOVED: {
         3811  +      *(int*)pArg = fileHasMoved(pFile);
         3812  +      return SQLITE_OK;
  3803   3813       }
  3804   3814   #if SQLITE_MAX_MMAP_SIZE>0
  3805   3815       case SQLITE_FCNTL_MMAP_SIZE: {
  3806   3816         i64 newLimit = *(i64*)pArg;
  3807   3817         int rc = SQLITE_OK;
  3808   3818         if( newLimit>sqlite3GlobalConfig.mxMmap ){
  3809   3819           newLimit = sqlite3GlobalConfig.mxMmap;

Changes to src/pager.c.

  4802   4802   ** to be on disk.  Return non-zero (SQLITE_READONLY_DBMOVED or some other error
  4803   4803   ** code from sqlite3OsAccess()) if the database has gone missing.
  4804   4804   */
  4805   4805   static int databaseIsUnmoved(Pager *pPager){
  4806   4806     const int fixedFlags = SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN |
  4807   4807                            SQLITE_IOCAP_UNMOVABLE_WHEN_OPEN;
  4808   4808     int dc;
  4809         -  int x = 0, rc;
         4809  +  int bHasMoved = 0;
         4810  +  int rc;
  4810   4811   
  4811   4812     if( pPager->tempFile ) return SQLITE_OK;
  4812   4813     if( pPager->dbSize==0 ) return SQLITE_OK;
  4813   4814     assert( pPager->zFilename && pPager->zFilename[0] );
  4814   4815     dc = sqlite3OsDeviceCharacteristics(pPager->fd);
  4815   4816     if( (dc&fixedFlags)==fixedFlags ) return SQLITE_OK;
  4816         -  rc = sqlite3OsAccess(pPager->pVfs, pPager->zFilename, SQLITE_ACCESS_EXISTS, &x);
  4817         -  if( rc==SQLITE_OK && !x ) rc = SQLITE_READONLY_DBMOVED;
         4817  +  rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_HAS_MOVED, &bHasMoved);
         4818  +  if( rc==SQLITE_OK && bHasMoved ) rc = SQLITE_READONLY_DBMOVED;
  4818   4819     return rc;
  4819   4820   }
  4820   4821   
  4821   4822   
  4822   4823   /*
  4823   4824   ** This function is called after transitioning from PAGER_UNLOCK to
  4824   4825   ** PAGER_SHARED state. It tests if there is a hot journal present in

Changes to src/sqlite.h.in.

   916    916   ** The [SQLITE_FCNTL_TRACE] file control provides advisory information
   917    917   ** to the VFS about what the higher layers of the SQLite stack are doing.
   918    918   ** This file control is used by some VFS activity tracing [shims].
   919    919   ** The argument is a zero-terminated string.  Higher layers in the
   920    920   ** SQLite stack may generate instances of this file control if
   921    921   ** the [SQLITE_USE_FCNTL_TRACE] compile-time option is enabled.
   922    922   **
          923  +** <li>[[SQLITE_FCNTL_HAS_MOVED]]
          924  +** The [SQLITE_FCNTL_HAS_MOVED] file control interprets its argument as a
          925  +** pointer to an integer and it writes a boolean into that integer depending
          926  +** on whether or not the file has been renamed, moved, or deleted since it
          927  +** was first opened.
          928  +**
   923    929   ** </ul>
   924    930   */
   925    931   #define SQLITE_FCNTL_LOCKSTATE               1
   926    932   #define SQLITE_GET_LOCKPROXYFILE             2
   927    933   #define SQLITE_SET_LOCKPROXYFILE             3
   928    934   #define SQLITE_LAST_ERRNO                    4
   929    935   #define SQLITE_FCNTL_SIZE_HINT               5
................................................................................
   936    942   #define SQLITE_FCNTL_VFSNAME                12
   937    943   #define SQLITE_FCNTL_POWERSAFE_OVERWRITE    13
   938    944   #define SQLITE_FCNTL_PRAGMA                 14
   939    945   #define SQLITE_FCNTL_BUSYHANDLER            15
   940    946   #define SQLITE_FCNTL_TEMPFILENAME           16
   941    947   #define SQLITE_FCNTL_MMAP_SIZE              18
   942    948   #define SQLITE_FCNTL_TRACE                  19
          949  +#define SQLITE_FCNTL_HAS_MOVED              20
   943    950   
   944    951   /*
   945    952   ** CAPI3REF: Mutex Handle
   946    953   **
   947    954   ** The mutex module within SQLite defines [sqlite3_mutex] to be an
   948    955   ** abstract type for a mutex object.  The SQLite core never looks
   949    956   ** at the internal representation of an [sqlite3_mutex].  It only

Changes to test/pager4.test.

    33     33   file rename test.db test-xyz.db
    34     34   do_catchsql_test pager4-1.2 {
    35     35     SELECT * FROM t1;
    36     36   } {0 {673 stone philips}}
    37     37   do_catchsql_test pager4-1.3 {
    38     38     UPDATE t1 SET a=537;
    39     39   } {1 {attempt to write a readonly database}}
           40  +
           41  +# Creating a different database file with the same name of the original
           42  +# is detected and still leaves the database read-only.
           43  +#
           44  +sqlite3 db2 test.db
           45  +db2 eval {CREATE TABLE t2(x,y,z)}
           46  +do_catchsql_test pager4-1.4 {
           47  +  UPDATE t1 SET a=948;
           48  +} {1 {attempt to write a readonly database}}
    40     49   
    41     50   # Changing the name back clears the READONLY error
    42     51   #
           52  +db2 close
           53  +file delete -force test.db
    43     54   file rename test-xyz.db test.db
    44         -do_catchsql_test pager4-1.4 {
           55  +do_catchsql_test pager4-1.5 {
    45     56     SELECT * FROM t1;
    46     57   } {0 {673 stone philips}}
    47         -do_catchsql_test pager4-1.5 {
           58  +do_catchsql_test pager4-1.6 {
    48     59     UPDATE t1 SET a=537;
    49     60     SELECT * FROM t1;
    50     61   } {0 {537 stone philips}}
    51     62   
    52     63   # We can write to a renamed database if journal_mode=OFF or
    53     64   # journal_mode=MEMORY.
    54     65   #
    55     66   file rename test.db test-xyz.db
    56         -do_catchsql_test pager4-1.6 {
           67  +do_catchsql_test pager4-1.7 {
    57     68     PRAGMA journal_mode=OFF;
    58     69     UPDATE t1 SET a=107;
    59     70     SELECT * FROM t1;
    60     71   } {0 {off 107 stone philips}}
    61         -do_catchsql_test pager4-1.7 {
           72  +do_catchsql_test pager4-1.8 {
    62     73     PRAGMA journal_mode=MEMORY;
    63     74     UPDATE t1 SET b='magpie';
    64     75     SELECT * FROM t1;
    65     76   } {0 {memory 107 magpie philips}}
    66     77   
    67     78   # Any other journal mode gives a READONLY error
    68     79   #
    69         -do_catchsql_test pager4-1.8 {
           80  +do_catchsql_test pager4-1.9 {
    70     81     PRAGMA journal_mode=DELETE;
    71     82     UPDATE t1 SET c='jaguar';
    72     83   } {1 {attempt to write a readonly database}}
    73         -do_catchsql_test pager4-1.9 {
           84  +do_catchsql_test pager4-1.10 {
    74     85     PRAGMA journal_mode=TRUNCATE;
    75     86     UPDATE t1 SET c='jaguar';
    76     87   } {1 {attempt to write a readonly database}}
    77         -do_catchsql_test pager4-1.10 {
           88  +do_catchsql_test pager4-1.11 {
    78     89     PRAGMA journal_mode=PERSIST;
    79     90     UPDATE t1 SET c='jaguar';
    80     91   } {1 {attempt to write a readonly database}}
    81     92   
    82     93   
    83     94   finish_test