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.
modified:
  sql/sql_parse.cc

per-file messages:
  sql/sql_parse.cc
    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
    element.
=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc	2008-10-24 12:50:59 +0000
+++ b/sql/sql_parse.cc	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
-  SYNOPSIS
-    test_if_data_home_dir()
-    dir                     directory
-    conv_home_dir           converted data home directory
-    home_dir_len            converted data home directory length
-
-  RETURN VALUES
-    0	ok
-    1	error  
-*/
-C_MODE_START
 
-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, "", "",
-                   (MY_RETURN_REAL_PATH|MY_RESOLVE_SYMLINKS));
-  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, "", "",
+                   (MY_RETURN_REAL_PATH | MY_RESOLVE_SYMLINKS));
+  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)
       DBUG_RETURN(0);
 
+    /*
+      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,
                         mysql_unpacked_real_data_home_len,
                         (const uchar*) mysql_unpacked_real_data_home,
                         mysql_unpacked_real_data_home_len))
         DBUG_RETURN(1);
+      /* purecov: end */
     }
-    else if (!memcmp(path, mysql_unpacked_real_data_home,
+    else if (!memcmp(real_path, mysql_unpacked_real_data_home,
                      mysql_unpacked_real_data_home_len))
       DBUG_RETURN(1);
   }
   DBUG_RETURN(0);
 }
 
+
+/**
+  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.
+*/
+C_MODE_START
+
+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));
+}
+
 C_MODE_END
 
 

Thread
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