From 1b1f30bb5e9fe8e508c8953f4d29d29cb155a510 Mon Sep 17 00:00:00 2001
From: drh <drh@noemail.net>
Date: Fri, 6 Dec 2013 15:37:35 +0000
Subject: [PATCH] Add code to detect if the database file is moved or deleted
 out from under SQLite and return an SQLITE_IOERR_NODB.

FossilOrigin-Name: 8759a8e4d83b163e315eff316cf163f6ea42f2bb
---
 manifest            | 27 ++++++++++++++-------------
 manifest.uuid       |  2 +-
 src/os_win.c        |  1 +
 src/pager.c         | 25 +++++++++++++++++++++++++
 src/sqlite.h.in     |  6 +++++-
 src/test_vfstrace.c |  5 +++++
 test/pager4.test    | 33 +++++++++++++++++++++++++++++++++
 7 files changed, 84 insertions(+), 15 deletions(-)
 create mode 100644 test/pager4.test

diff --git a/manifest b/manifest
index 264b3fc190..feb65446f1 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Version\s3.8.2
-D 2013-12-06T14:53:30.888
+C Add\scode\sto\sdetect\sif\sthe\sdatabase\sfile\sis\smoved\sor\sdeleted\sout\sfrom\sunder\nSQLite\sand\sreturn\san\sSQLITE_IOERR_NODB.
+D 2013-12-06T15:37:35.675
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in e1a9b4258bbde53f5636f4e238c65b7e11459e2b
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -206,8 +206,8 @@ F src/os.c b4ad71336fd96f97776f75587cd9e8218288f5be
 F src/os.h 4a46270a64e9193af4a0aaa3bc2c66dc07c29b3f
 F src/os_common.h 92815ed65f805560b66166e3583470ff94478f04
 F src/os_unix.c f076587029285554a3a65d30d0e71d50cd61f41f
-F src/os_win.c 4323dd0bac4f7a7037fc4cf87fb4692d17f0b108
-F src/pager.c 2aa4444ffe86e9282d03bc349a4a5e49bd77c0e8
+F src/os_win.c f149798adb6f32db835b2c9f9fac83d86015e004
+F src/pager.c 3648e602d484020d10cb514b04ef8f445ed06f30
 F src/pager.h f094af9f6ececfaa8a1e93876905a4f34233fb0c
 F src/parse.y acee1a9958539e21263362b194594c5255ad2fca
 F src/pcache.c f8043b433a57aba85384a531e3937a804432a346
@@ -221,7 +221,7 @@ F src/resolve.c 7eda9097b29fcf3d2b42fdc17d1de672134e09b6
 F src/rowset.c 64655f1a627c9c212d9ab497899e7424a34222e0
 F src/select.c d41381d80a22d3a83352aeca274cccf264ac277a
 F src/shell.c 936a72ff784efff3832cce274a96ed0b036e6758
-F src/sqlite.h.in baf55c31c4e15a4b626acfeaa792f2aaa566657f
+F src/sqlite.h.in 4373b4a913ea460d91a9a4a54906ee18b350d573
 F src/sqlite3.rc 11094cc6a157a028b301a9f06b3d03089ea37c3e
 F src/sqlite3ext.h 886f5a34de171002ad46fae8c36a7d8051c190fc
 F src/sqliteInt.h f3a5d663fe9c6c0b2ee7fc2e20a6204eaea5bc7c
@@ -272,7 +272,7 @@ F src/test_syscall.c 16dbe79fb320fadb5acd7a0a59f49e52ab2d2091
 F src/test_tclvar.c f4dc67d5f780707210d6bb0eb6016a431c04c7fa
 F src/test_thread.c 1e133a40b50e9c035b00174035b846e7eef481cb
 F src/test_vfs.c e72f555ef7a59080f898fcf1a233deb9eb704ea9
-F src/test_vfstrace.c 34b544e80ba7fb77be15395a609c669df2e660a2
+F src/test_vfstrace.c 21b99a6fe3f4c8be13c0fe83006ee13873154d78
 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
 F src/tokenize.c ec4c1a62b890bf1dbcdb966399e140b904c700a4
 F src/trigger.c d84e1f3669e9a217731a14a9d472b1c7b87c87ba
@@ -726,6 +726,7 @@ F test/oserror.test 50417780d0e0d7cd23cf12a8277bb44024765df3
 F test/pager1.test 1acbdb14c5952a72dd43129cabdbf69aaa3ed1fa
 F test/pager2.test 67b8f40ae98112bcdba1f2b2d03ea83266418c71
 F test/pager3.test 3856d9c80839be0668efee1b74811b1b7f7fc95f
+F test/pager4.test 1848509f0aa83f99d438a2bae02c3fd64dd1afe6
 F test/pagerfault.test 7285379906ab2f1108b8e82bbdf2d386cc8ff3ff
 F test/pagerfault2.test caf4c7facb914fd3b03a17b31ae2b180c8d6ca1f
 F test/pagerfault3.test 1003fcda009bf48a8e22a516e193b6ef0dd1bbd8
@@ -1145,10 +1146,10 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381
 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
 F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01
 F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff
-P c632567063d81c4be404ce58cbac0eff7412f565
-R 39f74301df16683d1c19ab7ca83b6451
-T +bgcolor * #d0c0ff
-T +sym-release *
-T +sym-version-3.8.2 *
-U dan
-Z bddf39b758268961698f9899546a37dd
+P 27392118af4c38c5203a04b8013e1afdb1cebd0d
+R 0833e0746c0a3bdbdf22e2e06343a85a
+T *branch * detect-moved-db
+T *sym-detect-moved-db *
+T -sym-trunk *
+U drh
+Z 9ba43ea2faea1b2f751f5fd7fd1e8bc8
diff --git a/manifest.uuid b/manifest.uuid
index 5097db9159..a2f643fbba 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-27392118af4c38c5203a04b8013e1afdb1cebd0d
\ No newline at end of file
+8759a8e4d83b163e315eff316cf163f6ea42f2bb
\ No newline at end of file
diff --git a/src/os_win.c b/src/os_win.c
index 22052a3fe7..56b546fb47 100644
--- a/src/os_win.c
+++ b/src/os_win.c
@@ -3192,6 +3192,7 @@ static int winSectorSize(sqlite3_file *id){
 static int winDeviceCharacteristics(sqlite3_file *id){
   winFile *p = (winFile*)id;
   return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN |
+         SQLITE_IOCAP_UNMOVABLE_WHEN_OPEN |
          ((p->ctrlFlags & WINFILE_PSOW)?SQLITE_IOCAP_POWERSAFE_OVERWRITE:0);
 }
 
diff --git a/src/pager.c b/src/pager.c
index d675b85821..78cb78ab23 100644
--- a/src/pager.c
+++ b/src/pager.c
@@ -4797,6 +4797,27 @@ int sqlite3PagerOpen(
 }
 
 
+/* 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
@@ -4970,6 +4991,10 @@ int sqlite3PagerSharedLock(Pager *pPager){
       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.
     */
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index 98c89da322..de6b59b0b7 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -475,6 +475,7 @@ int sqlite3_exec(
 #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))
@@ -553,7 +554,9 @@ int sqlite3_exec(
 ** after reboot following a crash or power loss, the only bytes in a
 ** file that were written at the application level might have changed
 ** and that adjacent bytes, even bytes within the same sector are
-** guaranteed to be unchanged.
+** guaranteed to be unchanged.  The SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN
+** and SQLITE_IOCAP_UNMOVABLE_WHEN_OPEN flags indicate that a file 
+** cannot be deleted or renamed when open, respectively.
 */
 #define SQLITE_IOCAP_ATOMIC                 0x00000001
 #define SQLITE_IOCAP_ATOMIC512              0x00000002
@@ -568,6 +571,7 @@ int sqlite3_exec(
 #define SQLITE_IOCAP_SEQUENTIAL             0x00000400
 #define SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN  0x00000800
 #define SQLITE_IOCAP_POWERSAFE_OVERWRITE    0x00001000
+#define SQLITE_IOCAP_UNMOVABLE_WHEN_OPEN    0x00002000
 
 /*
 ** CAPI3REF: File Locking Levels
diff --git a/src/test_vfstrace.c b/src/test_vfstrace.c
index 0aacc01fe4..8d56cf8946 100644
--- a/src/test_vfstrace.c
+++ b/src/test_vfstrace.c
@@ -258,6 +258,11 @@ static void vfstrace_print_errcode(
     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;
diff --git a/test/pager4.test b/test/pager4.test
new file mode 100644
index 0000000000..dbe9bf8195
--- /dev/null
+++ b/test/pager4.test
@@ -0,0 +1,33 @@
+# 2013-12-06
+#
+# The author disclaims copyright to this source code.  In place of
+# a legal notice, here is a blessing:
+#
+#    May you do good and not evil.
+#    May you find forgiveness for yourself and forgive others.
+#    May you share freely, never taking more than you give.
+#
+#***********************************************************************
+#
+# Tests for the SQLITE_IOERR_NODB error condition: the database file file
+# is unlinked or renamed out from under SQLite.
+#
+
+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