]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Fix issues on unix with opening database files via symlinks that are not in the curre...
authordan <dan@noemail.net>
Mon, 25 Jan 2016 17:04:48 +0000 (17:04 +0000)
committerdan <dan@noemail.net>
Mon, 25 Jan 2016 17:04:48 +0000 (17:04 +0000)
FossilOrigin-Name: 80398fd44fb232193450103808e1854e0eba5652

manifest
manifest.uuid
src/os_unix.c
test/symlink.test

index 827a3d51264789072c8f2e77495081e0e82e4d53..0b395a1661ae45dda8b720c9647e7f4de7bbc9c0 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-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
@@ -329,7 +329,7 @@ F src/os.c 8fd25588eeba74068d41102d26810e216999b6c8
 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
@@ -1061,7 +1061,7 @@ F test/subselect.test d24fd8757daf97dafd2e889c73ea4c4272dcf4e4
 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
@@ -1419,7 +1419,10 @@ F tool/vdbe_profile.tcl 246d0da094856d72d2c12efec03250d71639d19f
 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
index 276ef7a03f4cd0360d2414b215d9c31a1ca2c6af..6dda1af216faac028352c2659cf775e278d9490a 100644 (file)
@@ -1 +1 @@
-7ac017a498b6fb28343eef2d24e400c7800660d6
\ No newline at end of file
+80398fd44fb232193450103808e1854e0eba5652
\ No newline at end of file
index ee9b55674baa1ac5912f1c4f8d505e2f367383cb..9cd1fa19adac292ed8240b199c97b1f5542b15ed 100644 (file)
 */
 #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()
@@ -5927,52 +5932,22 @@ static int unixAccess(
   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:
   **
@@ -5989,6 +5964,7 @@ static int unixFullPathname(
   ** 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 ){
@@ -6008,6 +5984,100 @@ static int unixFullPathname(
   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
 /*
index 790624161f3aca9e34c7b602edbc2d1de78a2a6b..e9e5f3c305415b55626445fd01abb248f78c5f26 100644 (file)
@@ -83,38 +83,49 @@ do_test 1.5 {
 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.
@@ -125,5 +136,57 @@ do_test 3.1 {
   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