At the start of each write transaction, check to see if the database file has
been moved, and if it has refuse to start the transaction, returning an SQLITE_READONLY_DBMOVED error code. FossilOrigin-Name: 369a23e00644f3ff8b6a2d2ed73b8cb65e9f124b
This commit is contained in:
commit
689156fdbc
22
manifest
22
manifest
@ -1,5 +1,5 @@
|
||||
C Performance\soptimization\sto\sthe\smoveToRoot()\ssubroutine\sin\sbtree.c.
|
||||
D 2013-12-06T23:25:27.601
|
||||
C At\sthe\sstart\sof\seach\swrite\stransaction,\scheck\sto\ssee\sif\sthe\sdatabase\sfile\shas\nbeen\smoved,\sand\sif\sit\shas\srefuse\sto\sstart\sthe\stransaction,\sreturning\san\nSQLITE_READONLY_DBMOVED\serror\scode.
|
||||
D 2013-12-07T16:56:09.769
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in e1a9b4258bbde53f5636f4e238c65b7e11459e2b
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
@ -188,7 +188,7 @@ F src/journal.c b4124532212b6952f42eb2c12fa3c25701d8ba8d
|
||||
F src/legacy.c 0df0b1550b9cc1f58229644735e317ac89131f12
|
||||
F src/lempar.c cdf0a000315332fc9b50b62f3b5e22e080a0952b
|
||||
F src/loadext.c 867c7b330b740c6c917af9956b13b81d0a048303
|
||||
F src/main.c 74bdee8f574588868e67c25cd377df7cba80102e
|
||||
F src/main.c 355fc9ab213b43a4d8a96aadf2a84622e6668f0a
|
||||
F src/malloc.c 543a8eb5508eaf4cadf55a9b503379eba2088128
|
||||
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
|
||||
F src/mem1.c c0c990fcaddff810ea277b4fb5d9138603dd5d4b
|
||||
@ -205,9 +205,9 @@ F src/notify.c 976dd0f6171d4588e89e874fcc765e92914b6d30
|
||||
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_unix.c 60a7b3b23e6fcf83a50d1e320b280b551724e11f
|
||||
F src/os_win.c 4323dd0bac4f7a7037fc4cf87fb4692d17f0b108
|
||||
F src/pager.c 2aa4444ffe86e9282d03bc349a4a5e49bd77c0e8
|
||||
F src/pager.c d27dc8aaeac16c90eb0c859c074683bfc4533c41
|
||||
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 125dc0b76f0116f1cd6f13536db52ba981e1c5bd
|
||||
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 3a0ab304682fecbceb689e7d9b904211fde11d78
|
||||
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 b40ecb4cc7dff957ee7916e41ab21d1ed702a642
|
||||
F test/pagerfault.test 7285379906ab2f1108b8e82bbdf2d386cc8ff3ff
|
||||
F test/pagerfault2.test caf4c7facb914fd3b03a17b31ae2b180c8d6ca1f
|
||||
F test/pagerfault3.test 1003fcda009bf48a8e22a516e193b6ef0dd1bbd8
|
||||
@ -1145,7 +1146,8 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381
|
||||
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
|
||||
F tool/warnings.sh d1a6de74685f360ab718efda6265994b99bbea01
|
||||
F tool/win/sqlite.vsix 030f3eeaf2cb811a3692ab9c14d021a75ce41fff
|
||||
P c5a3a1af3c7bd34886e944a1fef2f6340ded24a0
|
||||
R 12065e363c2320b45db52efcb2788f0d
|
||||
P b5842a70f8e26456a8f0d39539bc266f097480a4 9c59f5af7ac4908583fab85d37241f200c40f02d
|
||||
R b5502e212abebd79211cbde2351c1e6b
|
||||
T +closed 9c59f5af7ac4908583fab85d37241f200c40f02d
|
||||
U drh
|
||||
Z 6564d15f297e6c8ddf25ac421a3f69fc
|
||||
Z aeb423432b55400118887674a2a0ba39
|
||||
|
@ -1 +1 @@
|
||||
b5842a70f8e26456a8f0d39539bc266f097480a4
|
||||
369a23e00644f3ff8b6a2d2ed73b8cb65e9f124b
|
@ -1086,6 +1086,7 @@ const char *sqlite3ErrName(int rc){
|
||||
case SQLITE_READONLY_RECOVERY: zName = "SQLITE_READONLY_RECOVERY"; break;
|
||||
case SQLITE_READONLY_CANTLOCK: zName = "SQLITE_READONLY_CANTLOCK"; break;
|
||||
case SQLITE_READONLY_ROLLBACK: zName = "SQLITE_READONLY_ROLLBACK"; break;
|
||||
case SQLITE_READONLY_DBMOVED: zName = "SQLITE_READONLY_DBMOVED"; break;
|
||||
case SQLITE_INTERRUPT: zName = "SQLITE_INTERRUPT"; break;
|
||||
case SQLITE_IOERR: zName = "SQLITE_IOERR"; break;
|
||||
case SQLITE_IOERR_READ: zName = "SQLITE_IOERR_READ"; break;
|
||||
|
@ -1315,6 +1315,15 @@ static int findInodeInfo(
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return TRUE if pFile has been renamed or unlinked since it was first opened.
|
||||
*/
|
||||
static int fileHasMoved(unixFile *pFile){
|
||||
struct stat buf;
|
||||
return pFile->pInode!=0 &&
|
||||
(osStat(pFile->zPath, &buf)!=0 || buf.st_ino!=pFile->pInode->fileId.ino);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Check a unixFile that is a database. Verify the following:
|
||||
@ -1349,10 +1358,7 @@ static void verifyDbFile(unixFile *pFile){
|
||||
pFile->ctrlFlags |= UNIXFILE_WARNED;
|
||||
return;
|
||||
}
|
||||
if( pFile->pInode!=0
|
||||
&& ((rc = osStat(pFile->zPath, &buf))!=0
|
||||
|| buf.st_ino!=pFile->pInode->fileId.ino)
|
||||
){
|
||||
if( fileHasMoved(pFile) ){
|
||||
sqlite3_log(SQLITE_WARNING, "file renamed while open: %s", pFile->zPath);
|
||||
pFile->ctrlFlags |= UNIXFILE_WARNED;
|
||||
return;
|
||||
@ -3801,6 +3807,10 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
case SQLITE_FCNTL_HAS_MOVED: {
|
||||
*(int*)pArg = fileHasMoved(pFile);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
#if SQLITE_MAX_MMAP_SIZE>0
|
||||
case SQLITE_FCNTL_MMAP_SIZE: {
|
||||
i64 newLimit = *(i64*)pArg;
|
||||
|
44
src/pager.c
44
src/pager.c
@ -4797,6 +4797,30 @@ 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_READONLY_DBMOVED or some other error
|
||||
** code from sqlite3OsAccess()) if the database has gone missing.
|
||||
*/
|
||||
static int databaseIsUnmoved(Pager *pPager){
|
||||
int bHasMoved = 0;
|
||||
int rc;
|
||||
|
||||
if( pPager->tempFile ) return SQLITE_OK;
|
||||
if( pPager->dbSize==0 ) return SQLITE_OK;
|
||||
assert( pPager->zFilename && pPager->zFilename[0] );
|
||||
rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_HAS_MOVED, &bHasMoved);
|
||||
if( rc==SQLITE_NOTFOUND ){
|
||||
/* If the HAS_MOVED file-control is unimplemented, assume that the file
|
||||
** has not been moved. That is the historical behavior of SQLite: prior to
|
||||
** version 3.8.3, it never checked */
|
||||
rc = SQLITE_OK;
|
||||
}else if( rc==SQLITE_OK && bHasMoved ){
|
||||
rc = SQLITE_READONLY_DBMOVED;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** This function is called after transitioning from PAGER_UNLOCK to
|
||||
@ -5473,13 +5497,19 @@ static int pager_open_journal(Pager *pPager){
|
||||
(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
|
||||
|
||||
/* 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) );
|
||||
}
|
||||
|
@ -486,6 +486,7 @@ int sqlite3_exec(
|
||||
#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))
|
||||
@ -553,7 +554,8 @@ 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
|
||||
** flag indicate that a file cannot be deleted when open.
|
||||
*/
|
||||
#define SQLITE_IOCAP_ATOMIC 0x00000001
|
||||
#define SQLITE_IOCAP_ATOMIC512 0x00000002
|
||||
@ -916,6 +918,12 @@ struct sqlite3_io_methods {
|
||||
** SQLite stack may generate instances of this file control if
|
||||
** the [SQLITE_USE_FCNTL_TRACE] compile-time option is enabled.
|
||||
**
|
||||
** <li>[[SQLITE_FCNTL_HAS_MOVED]]
|
||||
** The [SQLITE_FCNTL_HAS_MOVED] file control interprets its argument as a
|
||||
** pointer to an integer and it writes a boolean into that integer depending
|
||||
** on whether or not the file has been renamed, moved, or deleted since it
|
||||
** was first opened.
|
||||
**
|
||||
** </ul>
|
||||
*/
|
||||
#define SQLITE_FCNTL_LOCKSTATE 1
|
||||
@ -936,6 +944,7 @@ struct sqlite3_io_methods {
|
||||
#define SQLITE_FCNTL_TEMPFILENAME 16
|
||||
#define SQLITE_FCNTL_MMAP_SIZE 18
|
||||
#define SQLITE_FCNTL_TRACE 19
|
||||
#define SQLITE_FCNTL_HAS_MOVED 20
|
||||
|
||||
/*
|
||||
** CAPI3REF: Mutex Handle
|
||||
|
@ -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_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;
|
||||
|
94
test/pager4.test
Normal file
94
test/pager4.test
Normal file
@ -0,0 +1,94 @@
|
||||
# 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
|
||||
|
||||
# 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}}
|
||||
|
||||
# Creating a different database file with the same name of the original
|
||||
# is detected and still leaves the database read-only.
|
||||
#
|
||||
sqlite3 db2 test.db
|
||||
db2 eval {CREATE TABLE t2(x,y,z)}
|
||||
do_catchsql_test pager4-1.4 {
|
||||
UPDATE t1 SET a=948;
|
||||
} {1 {attempt to write a readonly database}}
|
||||
|
||||
# Changing the name back clears the READONLY error
|
||||
#
|
||||
db2 close
|
||||
file delete -force test.db
|
||||
file rename test-xyz.db test.db
|
||||
do_catchsql_test pager4-1.5 {
|
||||
SELECT * FROM t1;
|
||||
} {0 {673 stone philips}}
|
||||
do_catchsql_test pager4-1.6 {
|
||||
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.7 {
|
||||
PRAGMA journal_mode=OFF;
|
||||
UPDATE t1 SET a=107;
|
||||
SELECT * FROM t1;
|
||||
} {0 {off 107 stone philips}}
|
||||
do_catchsql_test pager4-1.8 {
|
||||
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.9 {
|
||||
PRAGMA journal_mode=DELETE;
|
||||
UPDATE t1 SET c='jaguar';
|
||||
} {1 {attempt to write a readonly database}}
|
||||
do_catchsql_test pager4-1.10 {
|
||||
PRAGMA journal_mode=TRUNCATE;
|
||||
UPDATE t1 SET c='jaguar';
|
||||
} {1 {attempt to write a readonly database}}
|
||||
do_catchsql_test pager4-1.11 {
|
||||
PRAGMA journal_mode=PERSIST;
|
||||
UPDATE t1 SET c='jaguar';
|
||||
} {1 {attempt to write a readonly database}}
|
||||
|
||||
|
||||
finish_test
|
Loading…
x
Reference in New Issue
Block a user