List:Commits« Previous MessageNext Message »
From:Ingo Struewing Date:November 19 2008 2:01pm
Subject:bzr commit into mysql-5.1 branch (ingo.struewing:2712) Bug#39277
View as plain text  
#At file:///home2/mydev/bzrroot/mysql-5.1-bug39277/

 2712 Ingo Struewing	2008-11-19
      Bug#39277 - symlink.test fails on Debian
      When the data directory contained a symbolic link to another
      file system, and the DATA or INDEX DIRECTORY clause of a
      CREATE TABLE statement referred to a subdirectory of the data
      directory by using another path, this was accepted.
      The problem was the use of a table file path name, which included
      the table name without an extension, for the comparison against
      the data directory path name. This was almost always a
      non-existent file. The internal algorithm failed to resolve
      symbolic links for non-existent files. So we compared unrelated
      path names.
      Fixed by doing a second comparison with a path name that has
      stripped off the last path element. In the case above it makes the
      directory for the table. If this is also a non-existent path, the
      creation of the table will fail anyway.

per-file messages:
    Bug#39277 - symlink.test fails on Debian
    Renamed test_if_data_home_dir() to static test_for_data_home_dir()
    and added a new test_if_data_home_dir(), which calls the renamed
    one twice. The second time after stripping off the last path
=== modified file 'sql/'
--- a/sql/	2008-10-24 12:50:59 +0000
+++ b/sql/	2008-11-19 14:01:36 +0000
@@ -7519,53 +7519,134 @@ bool check_string_char_length(LEX_STRING
   Check if path does not contain mysql data home directory
-    test_if_data_home_dir()
-    dir                     directory
-    conv_home_dir           converted data home directory
-    home_dir_len            converted data home directory length
-    0	ok
-    1	error  
-int test_if_data_home_dir(const char *dir)
-  char path[FN_REFLEN];
-  int dir_len;
-  DBUG_ENTER("test_if_data_home_dir");
+  @param[in]            path            file or directory path name
-  if (!dir)
-    DBUG_RETURN(0);
+  @return               status
+    @retval             0	        ok, does no contain data home dir
+    @retval             1	        error, contains data home dir
+  @note This function may not return the correct answer if the path in
+  question refers to no existing file or directory. This can happen if
+  the path contains symbolic links. The low-level functions that try to
+  resolve symlinks return the original path if the file does not exist,
+  even though one or more symlinks may be involved. If the original path
+  is not within the data home dir, but contains symlinks that point into
+  the data home dir, this function will falsely claim the path be not in
+  the data home dir.
+  @note More technical details to the above: On some systems, we use
+  realpath(3) for the symlink resolution. This returns ENOENT if the
+  resolved path does not refer to an existing file. my_realpath() does
+  then copy the original path verbatim, without symlink resolution.
-  (void) fn_format(path, dir, "", "",
-  dir_len= strlen(path);
-  if (mysql_unpacked_real_data_home_len<= dir_len)
+  @note For a resolution of non-existent file problem, see the function
+  definition of test_if_data_home_dir() below.
+static int test_for_data_home_dir(const char *path)
+  char  real_path[FN_REFLEN];
+  int   real_len;
+  DBUG_ENTER("test_for_data_home_dir");
+  DBUG_ASSERT(path);
+  /* Convert path to absolute, clean path with symlinks resolved. */
+  (void) fn_format(real_path, path, "", "",
+  real_len= strlen(real_path);
+  /*
+    If real_path is shorter than data home dir, it cannot be within it.
+    Otherwise we have to look deeper.
+  */
+  if (real_len >= mysql_unpacked_real_data_home_len)
-    if (dir_len > mysql_unpacked_real_data_home_len &&
-        path[mysql_unpacked_real_data_home_len] != FN_LIBCHAR)
+    /*
+      If the path name is longer that data home dir, and the character
+      at the position where data home dir ends, is not a path delimiter
+      (slash '/' in UNIX), it cannot be within data home dir.
+    */
+    if (real_len > mysql_unpacked_real_data_home_len &&
+        real_path[mysql_unpacked_real_data_home_len] != FN_LIBCHAR)
+    /*
+      If the real path matches data home dir until the end of
+      data home dir, it is a path within data home dir.
+      Either real_len == mysql_unpacked_real_data_home_len,
+      or a path delimiter follows in real_path. See the check above.
+    */
     if (lower_case_file_system)
-      if (!my_strnncoll(default_charset_info, (const uchar*) path,
+      /* purecov: inspected */
+      if (!my_strnncoll(default_charset_info, (const uchar*) real_path,
                         (const uchar*) mysql_unpacked_real_data_home,
+      /* purecov: end */
-    else if (!memcmp(path, mysql_unpacked_real_data_home,
+    else if (!memcmp(real_path, mysql_unpacked_real_data_home,
+  Check if path does not contain mysql data home directory
+  @param[in]            path            file or directory path name
+  @return               status
+    @retval             0	        ok, does no contain data home dir
+    @retval             1	        error, contains data home dir
+  We need to do a trick here. test_for_data_home_dir() can fail if
+  'path' contains symlinks and refers to a non-existent file or
+  directory. To defeat this flaw, we try two paths. First the original
+  one. If it passes the check, we strip off the last path element and
+  try its directory. The latter can fail too. But then it means that the
+  directory doesn't exist either. In this case the calling function
+  won't succeed with its operation anyway.
+  @note For more information on the symlinks + non-existent file
+  problem, see the function definition of test_for_data_home_dir() above.
+int test_if_data_home_dir(const char *path)
+  size_t dirlen;
+  char   dirpath[FN_REFLEN];
+  DBUG_ENTER("test_if_data_home_dir");
+  /* Missing path cannot match data home dir. */
+  if (!path)
+    DBUG_RETURN(0);
+  /*
+    Try the path verbatim. If it turns out to be within data home dir,
+    we can accept this as a true result. mysql_unpacked_real_data_home
+    is a resolved path. Either 'path' is within it literally, or
+    symlinks could be resolved and then the compare matched.
+  */
+  if(test_for_data_home_dir(path))
+    DBUG_RETURN(1);
+  /*
+    'path' seems to point outside data home dir. This could be due to
+    symlinks + non-existent path. Now retry the check with the last
+    path element stripped off.
+  */
+  dirname_part(dirpath, path, &dirlen);
+  DBUG_RETURN(test_for_data_home_dir(dirpath));

bzr commit into mysql-5.1 branch (ingo.struewing:2712) Bug#39277Ingo Struewing19 Nov
Re: bzr commit into mysql-5.1 branch (ingo.struewing:2712) Bug#39277Ingo Strüwing19 Nov
Re: bzr commit into mysql-5.1 branch (ingo.struewing:2712) Bug#39277Ingo Strüwing24 Nov
Re: bzr commit into mysql-5.1 branch (ingo.struewing:2712) Bug#39277Ingo Strüwing24 Nov