-C Replace\sthe\sOP_SetIfNotPos\soperator\swith\sOP_OffsetLimit\sin\sthe\sVDBE,\sfor\nsimpler\sand\ssmaller\scode.
-D 2016-01-25T15:57:29.060
+C Fix\sissues\son\sunix\swith\sopening\sdatabase\sfiles\svia\ssymlinks\sthat\sare\snot\sin\sthe\scurrent\sworking\sdirectory.\sAnd\swith\snested\ssymlinks.
+D 2016-01-25T17:04:48.546
F Makefile.in 027c1603f255390c43a426671055a31c0a65fdb4
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
F Makefile.msc 1708a78eda223b6daa302b140037fcc214a779f9
F src/os.h 3e57a24e2794a94d3cf2342c6d9a884888cd96bf
F src/os_common.h abdb9a191a367793268fe553d25bab894e986a0e
F src/os_setup.h c9d4553b5aaa6f73391448b265b89bed0b890faa
-F src/os_unix.c 0eb7f469fcd4e1fbedf30060438e26b839ec5486
+F src/os_unix.c cf524029242b4f878d6b97bad25cc2c0b66c2b31
F src/os_win.c 386fba30419e8458b13209781c2af5590eab2811
F src/os_win.h eb7a47aa17b26b77eb97e4823f20a00b8bda12ca
F src/pager.c 2916c66aee50f69d9ec56a7619b62d9c6a3bee61
F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a
F test/subtype1.test 7fe09496352f97053af1437150751be2d0a0cae8
F test/superlock.test 1cde669f68d2dd37d6c9bd35eee1d95491ae3fc2
-F test/symlink.test cbf6cb8c6c4b63a39e9f0f6b0d5c99e249dbc102
+F test/symlink.test 511db82662446bb0d3619002422760ef8e4b1122
F test/sync.test a34cd43e98b7fb84eabbf38f7ed8f7349b3f3d85
F test/syscall.test 2aa9e111b79fb385681ff8940124def6f8faab87
F test/sysfault.test c9f2b0d8d677558f74de750c75e12a5454719d04
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b
F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f
-P 30671345b1c1ee55a2d1aa17273213f1849efd81
-R f342889ba3409bd63b1cf54d88bfe0e2
-U drh
-Z e7c5fc86067d4269675714897cec0b51
+P 7ac017a498b6fb28343eef2d24e400c7800660d6
+R 0289127606d03bb3b649ecbd39bb14a5
+T *branch * follow-symlinks
+T *sym-follow-symlinks *
+T -sym-trunk *
+U dan
+Z 3e18b3d01ba737d3635110839e378935
*/
#define MAX_PATHNAME 512
+/*
+** Maximum supported symbolic links
+*/
+#define SQLITE_MAX_SYMLINKS 100
+
/* Always cast the getpid() return type for compatibility with
** kernel modules in VxWorks. */
#define osGetpid(X) (pid_t)getpid()
return SQLITE_OK;
}
-
/*
-** Turn a relative pathname into a full pathname. The relative path
-** is stored as a nul-terminated string in the buffer pointed to by
-** zPath.
+** Buffer zOut contains a (possibly) relative pathname. Overwrite it with
+** the corresponding full pathname.
**
-** zOut points to a buffer of at least sqlite3_vfs.mxPathname bytes
-** (in this case, MAX_PATHNAME bytes). The full-path is written to
-** this buffer before returning.
+** Parameter nOut is the allocated size of buffer zOut. nByte is the number
+** of bytes in the nul-terminated string that it contains (not including
+** the nul-terminator itself).
+**
+** Return SQLITE_OK if successful, or an SQLite error code otherwise.
*/
-static int unixFullPathname(
- sqlite3_vfs *pVfs, /* Pointer to vfs object */
- const char *zPath, /* Possibly relative input path */
- int nOut, /* Size of output buffer in bytes */
- char *zOut /* Output buffer */
+static int mkFullPathname(
+ const char *zPath, /* Use this path to log any errors */
+ char *zOut, /* IN/OUT: Buffer to modify */
+ int nByte, /* size of nul-terminated zOut in bytes */
+ int nOut /* Allocated size of buffer zOut */
){
- int nByte;
-
- /* It's odd to simulate an io-error here, but really this is just
- ** using the io-error infrastructure to test that SQLite handles this
- ** function failing. This function could fail if, for example, the
- ** current working directory has been unlinked.
- */
- SimulateIOError( return SQLITE_ERROR );
-
- assert( pVfs->mxPathname==MAX_PATHNAME );
- UNUSED_PARAMETER(pVfs);
-
-#if defined(HAVE_READLINK)
- /* Attempt to resolve the path as if it were a symbolic link. If it is
- ** a symbolic link, the resolved path is stored in buffer zOut[]. Or, if
- ** the identified file is not a symbolic link or does not exist, then
- ** zPath is copied directly into zOut. Either way, nByte is left set to
- ** the size of the string copied into zOut[] in bytes. */
- nByte = osReadlink(zPath, zOut, nOut-1);
- if( nByte<0 ){
- if( errno!=EINVAL && errno!=ENOENT ){
- return unixLogError(SQLITE_CANTOPEN_BKPT, "readlink", zPath);
- }
- sqlite3_snprintf(nOut, zOut, "%s", zPath);
- nByte = sqlite3Strlen30(zOut);
- }else{
- zOut[nByte] = '\0';
- }
-#endif
-
/* If buffer zOut[] now contains an absolute path there is nothing more
** to do. If it contains a relative path, do the following:
**
** truncated to make it fit. This is Ok, as SQLite refuses to open any
** file for which this function returns a full path larger than (nOut-8)
** bytes in size. */
+ assert( nByte<nOut );
testcase( nByte==nOut-5 );
testcase( nByte==nOut-4 );
if( zOut[0]!='/' && nByte<nOut-4 ){
return SQLITE_OK;
}
+/*
+** Turn a relative pathname into a full pathname. The relative path
+** is stored as a nul-terminated string in the buffer pointed to by
+** zPath.
+**
+** zOut points to a buffer of at least sqlite3_vfs.mxPathname bytes
+** (in this case, MAX_PATHNAME bytes). The full-path is written to
+** this buffer before returning.
+*/
+static int unixFullPathname(
+ sqlite3_vfs *pVfs, /* Pointer to vfs object */
+ const char *zPath, /* Possibly relative input path */
+ int nOut, /* Size of output buffer in bytes */
+ char *zOut /* Output buffer */
+){
+#if !defined(HAVE_READLINK)
+ sqlite3_snprintf(nOut, zOut, "%s", zIn);
+ nByte = sqlite3Strlen30(zOut);
+ return mkFullPathname(zPath, zOut, sqlite3Strlen30(zOut), nOut);
+#else
+ int rc = SQLITE_OK;
+ int nByte;
+ int nLink = 0; /* Number of symbolic links followed so far */
+ int bLink; /* True for a symbolic link */
+ const char *zIn = zPath; /* Input path for each iteration of loop */
+ char *zDel = 0;
+
+ assert( pVfs->mxPathname==MAX_PATHNAME );
+ UNUSED_PARAMETER(pVfs);
+
+ /* It's odd to simulate an io-error here, but really this is just
+ ** using the io-error infrastructure to test that SQLite handles this
+ ** function failing. This function could fail if, for example, the
+ ** current working directory has been unlinked.
+ */
+ SimulateIOError( return SQLITE_ERROR );
+
+ do {
+
+ /* Attempt to resolve the path as if it were a symbolic link. If it is
+ ** a symbolic link, the resolved path is stored in buffer zOut[]. Or, if
+ ** the identified file is not a symbolic link or does not exist, then
+ ** zPath is copied directly into zOut. Either way, nByte is left set to
+ ** the size of the string copied into zOut[] in bytes. */
+ assert( (zDel==0 && zIn==zPath) || (zDel!=0 && zIn==zDel) );
+ if( zDel ){
+ assert( zIn==zDel );
+ sqlite3_snprintf(nOut, zDel, "%s", zOut);
+ }
+ nByte = osReadlink(zIn, zOut, nOut-1);
+ if( nByte<0 ){
+ if( errno!=EINVAL && errno!=ENOENT ){
+ rc = unixLogError(SQLITE_CANTOPEN_BKPT, "readlink", zPath);
+ }else{
+ sqlite3_snprintf(nOut, zOut, "%s", zIn);
+ nByte = sqlite3Strlen30(zOut);
+ }
+ bLink = 0;
+ }else if( ++nLink>SQLITE_MAX_SYMLINKS ){
+ sqlite3_log(SQLITE_CANTOPEN,
+ "too many symbolic links (max=%d)", SQLITE_MAX_SYMLINKS
+ );
+ rc = SQLITE_CANTOPEN_BKPT;
+ }else{
+ zOut[nByte] = '\0';
+ if( zOut[0]!='/' ){
+ int n;
+ for(n = sqlite3Strlen30(zIn); n>0 && zIn[n-1]!='/'; n--);
+ if( nByte+n+1>nOut ){
+ rc = SQLITE_CANTOPEN_BKPT;
+ }else{
+ memmove(&zOut[n], zOut, nByte+1);
+ memcpy(zOut, zIn, n);
+ nByte += n;
+ }
+ }
+ if( zDel==0 ){
+ zDel = sqlite3_malloc(nOut);
+ if( zDel==0 ) rc = SQLITE_NOMEM;
+ zIn = (const char*)zDel;
+ }
+ bLink = 1;
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = mkFullPathname(zPath, zOut, nByte, nOut);
+ }
+ }while( bLink && rc==SQLITE_OK );
+
+ sqlite3_free(zDel);
+ return rc;
+#endif /* HAVE_READLINK */
+}
+
#ifndef SQLITE_OMIT_LOAD_EXTENSION
/*
do_test 2.0 {
catch { db close }
catch { db2 close }
- forcedelete test.db test.db2
+ forcedelete test.db test.db2 test.db3
sqlite3 db test.db
execsql { CREATE TABLE t1(x) }
file link test.db2 test.db
- sqlite3 db2 test.db2
- file exists test.db-journal
-} 0
+ file link test.db3 test.db2
+ set {} {}
+} {}
-do_test 2.1 {
- execsql {
- BEGIN;
- INSERT INTO t1 VALUES(1);
- } db2
- file exists test.db-journal
-} 1
-do_test 2.2 {
- file exists test.db2-journal
-} 0
-do_test 2.3 {
- execsql {
- COMMIT;
- PRAGMA journal_mode = wal;
- INSERT INTO t1 VALUES(2);
- } db2
- file exists test.db-wal
-} 1
-do_test 2.4 {
- file exists test.db2-wal
-} 0
-do_execsql_test 2.5 {
- SELECT * FROM t1;
-} {1 2}
+foreach {tn f} {1 test.db2 2 test.db3} {
+ do_test 2.$tn.1 {
+ sqlite3 db2 $f
+ file exists test.db-journal
+ } 0
+ do_test 2.$tn.2 {
+ execsql {
+ BEGIN;
+ INSERT INTO t1 VALUES(1);
+ } db2
+ file exists test.db-journal
+ } 1
+ do_test 2.$tn.3 {
+ list [file exists test2.db-journal] [file exists test3.db-journal]
+ } {0 0}
+ do_test 2.$tn.4 {
+ execsql {
+ COMMIT;
+ PRAGMA journal_mode = wal;
+ INSERT INTO t1 VALUES(2);
+ } db2
+ file exists test.db-wal
+ } 1
+ do_test 2.$tn.5 {
+ list [file exists test2.db-wal] [file exists test3.db-wal]
+ } {0 0}
+ do_execsql_test 2.$tn.6 {
+ SELECT * FROM t1;
+ } {1 2}
+ db2 close
+ do_execsql_test 2.$tn.7 {
+ DELETE FROM t1;
+ PRAGMA journal_mode = delete;
+ } delete
+}
# Try to open a ridiculously long pathname. Bug found by
# Kostya Serebryany using libFuzzer on 2015-11-30.
set res
} {unable to open database file}
+#-------------------------------------------------------------------------
+# Test that relative symlinks that are not located in the cwd work.
+#
+do_test 4.1 {
+ forcedelete x y z
+ file mkdir x
+ file mkdir y
+ file mkdir z
+ sqlite3 db x/test.db
+ file link y/test.db ../x/test.db
+ file link z/test.db ../y/test.db
+ execsql {
+ PRAGMA journal_mode = wal;
+ CREATE TABLE t1(x, y);
+ INSERT INTO t1 VALUES('hello', 'world');
+ }
+} {wal}
+
+do_test 4.2.1 {
+ db close
+ sqlite3 db y/test.db
+ db eval { SELECT * FROM t1 }
+} {hello world}
+do_test 4.2.2 {
+ list [file exists x/test.db-wal] [file exists y/test.db-wal]
+} {1 0}
+
+do_test 4.3.1 {
+ db close
+ sqlite3 db z/test.db
+ db eval { SELECT * FROM t1 }
+} {hello world}
+do_test 4.3.2 {
+ list [file exists x/test.db-wal] [file exists y/test.db-wal] \
+ [file exists z/test.db-wal]
+} {1 0 0}
+
+do_test 4.4.0 {
+ forcedelete w
+ file mkdir w
+ file link w/test.db [file join [pwd] x/test.db]
+ set {} {}
+} {}
+do_test 4.4.1 {
+ db close
+ breakpoint
+ sqlite3 db w/test.db
+ db eval { SELECT * FROM t1 }
+} {hello world}
+do_test 4.4.2 {
+ list [file exists x/test.db-wal] [file exists w/test.db-wal]
+} {1 0}
finish_test