#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