3380 Georgi Kodinov 2011-08-17
Bug #11746029: 22615: MYSQL SERVER INCORRECTLY CATOGORIZES THE LOST+FOUND DIRECTORY AS A DATABA
Implemented an --ignore-db-dir option that takes a valid file name or an empty string.
You can specify multiple options.
When a non-empty string is specified it's added as a directory name to the list of directories to ignore
when executing SHOW commands and filling in INFORMATION_SCHEMA tables.
When an empty string is specified as an argument the list is reset to empty.
Note that the above list of ignored directories doesn't affect other SQL commands like e.g. USE or SELECT etc.
Added a read-only global @@ignore_db_dirs system variable that will contain the current list in effect.
In addition to the list above the server will always ignore the directories starting with a dot (.). This is safe
because since mysql-5.1 a name of a database directory can't start with a dot (.) thanks to the fact that we
encode database and table names.
Added a test case.
Fixed the affected test cases.
Fixed the memory handling when converting the directories array to a hash fails for some reason.
added:
mysql-test/suite/sys_vars/r/ignore_db_dirs_basic.result
mysql-test/suite/sys_vars/t/ignore_db_dirs_basic-master.opt
mysql-test/suite/sys_vars/t/ignore_db_dirs_basic.test
modified:
mysql-test/r/mysqld--help-notwin.result
mysql-test/r/mysqld--help-win.result
sql/mysqld.cc
sql/mysqld.h
sql/sql_show.cc
sql/sql_show.h
sql/sys_vars.cc
3379 Jimmy Yang 2011-08-17 [merge]
Merge from mysql-5.5 to mysql-trunk
modified:
mysql-test/suite/innodb/r/innodb_corrupt_bit.result
storage/innobase/handler/ha_innodb.cc
=== modified file 'mysql-test/r/mysqld--help-notwin.result'
--- a/mysql-test/r/mysqld--help-notwin.result 2011-08-15 08:58:05 +0000
+++ b/mysql-test/r/mysqld--help-notwin.result 2011-08-17 14:33:45 +0000
@@ -192,6 +192,10 @@ The following options may be given as th
-?, --help Display this help and exit.
--ignore-builtin-innodb
Disable initialization of builtin InnoDB plugin
+ --ignore-db-dir=name
+ Specifies a directory to add to the ignore list when
+ collecting database names from the datadir. Put a blank
+ argument to reset the list accumulated so far.
--init-connect=name Command(s) that are executed for each new connection
--init-file=name Read SQL commands from this file at startup
--init-slave=name Command(s) that are executed by a slave server each time
=== modified file 'mysql-test/r/mysqld--help-win.result'
--- a/mysql-test/r/mysqld--help-win.result 2011-08-15 08:58:05 +0000
+++ b/mysql-test/r/mysqld--help-win.result 2011-08-17 14:33:45 +0000
@@ -193,6 +193,10 @@ The following options may be given as th
--ignore-builtin-innodb
Disable initialization of builtin InnoDB plugin
--init-connect=name Command(s) that are executed for each new connection
+ --ignore-db-dir=name
+ Specifies a directory to add to the ignore list when
+ collecting database names from the datadir. Put a blank
+ argument to reset the list accumulated so far.
--init-file=name Read SQL commands from this file at startup
--init-slave=name Command(s) that are executed by a slave server each time
the SQL thread starts
=== added file 'mysql-test/suite/sys_vars/r/ignore_db_dirs_basic.result'
--- a/mysql-test/suite/sys_vars/r/ignore_db_dirs_basic.result 1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/sys_vars/r/ignore_db_dirs_basic.result 2011-08-17 14:33:45 +0000
@@ -0,0 +1,18 @@
+select @@ignore_db_dirs;
+@@ignore_db_dirs
+e,lost+found,.mysqlgui,test
+# Check that SHOW DATABASES ignores all directories from
+# @@ignore_db_dirs and all directories with names starting
+# with '.'
+SHOW DATABASES;
+Database
+information_schema
+mtr
+mysql
+performance_schema
+SET @@global.ignore_db_dirs = 'aha';
+ERROR HY000: Variable 'ignore_db_dirs' is a read only variable
+SET @@local.ignore_db_dirs = 'aha';
+ERROR HY000: Variable 'ignore_db_dirs' is a read only variable
+SET @@ignore_db_dirs = 'aha';
+ERROR HY000: Variable 'ignore_db_dirs' is a read only variable
=== added file 'mysql-test/suite/sys_vars/t/ignore_db_dirs_basic-master.opt'
--- a/mysql-test/suite/sys_vars/t/ignore_db_dirs_basic-master.opt 1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/sys_vars/t/ignore_db_dirs_basic-master.opt 2011-08-17 14:33:45 +0000
@@ -0,0 +1,11 @@
+--ignore-db-dir=a
+--ignore-db-dir=b
+--ignore-db-dir=c
+--ignore-db-dir=
+--ignore-db-dir=d
+--ignore-db-dir x
+--ignore-db-dir=
+--ignore-db-dir=e
+--ignore-db-dir=lost+found
+--ignore-db-dir=.mysqlgui
+--ignore-db-dir=test
=== added file 'mysql-test/suite/sys_vars/t/ignore_db_dirs_basic.test'
--- a/mysql-test/suite/sys_vars/t/ignore_db_dirs_basic.test 1970-01-01 00:00:00 +0000
+++ b/mysql-test/suite/sys_vars/t/ignore_db_dirs_basic.test 2011-08-17 14:33:45 +0000
@@ -0,0 +1,20 @@
+select @@ignore_db_dirs;
+let $MYSQLD_DATADIR= `select @@datadir`;
+
+mkdir $MYSQLD_DATADIR/.mysqlgui;
+mkdir $MYSQLD_DATADIR/.otherdir;
+mkdir $MYSQLD_DATADIR/lost+found;
+--echo # Check that SHOW DATABASES ignores all directories from
+--echo # @@ignore_db_dirs and all directories with names starting
+--echo # with '.'
+SHOW DATABASES;
+rmdir $MYSQLD_DATADIR/.mysqlgui;
+rmdir $MYSQLD_DATADIR/.otherdir;
+rmdir $MYSQLD_DATADIR/lost+found;
+
+--error ER_INCORRECT_GLOBAL_LOCAL_VAR
+SET @@global.ignore_db_dirs = 'aha';
+--error ER_INCORRECT_GLOBAL_LOCAL_VAR
+SET @@local.ignore_db_dirs = 'aha';
+--error ER_INCORRECT_GLOBAL_LOCAL_VAR
+SET @@ignore_db_dirs = 'aha';
=== modified file 'sql/mysqld.cc'
--- a/sql/mysqld.cc 2011-08-16 16:30:11 +0000
+++ b/sql/mysqld.cc 2011-08-17 14:33:45 +0000
@@ -1450,6 +1450,7 @@ void clean_up(bool print_message)
#endif
my_tz_free();
my_dboptions_cache_free();
+ ignore_db_dirs_free();
#ifndef NO_EMBEDDED_ACCESS_CHECKS
servers_free(1);
acl_free(1);
@@ -3267,6 +3268,9 @@ int init_common_variables()
mysql_init_variables())
return 1;
+ if (ignore_db_dirs_init())
+ return 1;
+
#ifdef HAVE_TZNAME
{
struct tm tm_tmp;
@@ -3703,6 +3707,12 @@ You should consider changing lower_case_
return 1;
}
+ if (ignore_db_dirs_process_additions())
+ {
+ sql_print_error("An error occurred while storing ignore_db_dirs to a hash.");
+ return 1;
+ }
+
return 0;
}
@@ -6206,6 +6216,11 @@ struct my_option my_long_options[]=
&opt_super_large_pages, &opt_super_large_pages, 0,
GET_BOOL, OPT_ARG, 0, 0, 1, 0, 1, 0},
#endif
+ {"ignore-db-dir", OPT_IGNORE_DB_DIRECTORY,
+ "Specifies a directory to add to the ignore list when collecting "
+ "database names from the datadir. Put a blank argument to reset "
+ "the list accumulated so far.", 0, 0, 0, GET_STR, REQUIRED_ARG,
+ 0, 0, 0, 0, 0, 0},
{"language", 'L',
"Client error messages in given language. May be given as a full path. "
"Deprecated. Use --lc-messages-dir instead.",
@@ -7670,6 +7685,22 @@ mysqld_get_one_option(int optid,
if (argument == NULL) /* no argument */
log_error_file_ptr= const_cast<char*>("");
break;
+
+ case OPT_IGNORE_DB_DIRECTORY:
+ if (*argument == 0)
+ ignore_db_dirs_reset();
+ else
+ {
+ if (push_ignored_db_dir(argument))
+ {
+ sql_print_error("Can't start server: "
+ "cannot process --ignore-db-dir=%.*s",
+ FN_REFLEN, argument);
+ return 1;
+ }
+ }
+ break;
+
}
return 0;
}
=== modified file 'sql/mysqld.h'
--- a/sql/mysqld.h 2011-07-25 15:13:06 +0000
+++ b/sql/mysqld.h 2011-08-17 14:33:45 +0000
@@ -507,6 +507,7 @@ enum options_mysqld
OPT_DEBUG_SYNC_TIMEOUT,
OPT_DELAY_KEY_WRITE_ALL,
OPT_ISAM_LOG,
+ OPT_IGNORE_DB_DIRECTORY,
OPT_KEY_BUFFER_SIZE,
OPT_KEY_CACHE_AGE_THRESHOLD,
OPT_KEY_CACHE_BLOCK_SIZE,
=== modified file 'sql/sql_show.cc'
--- a/sql/sql_show.cc 2011-08-15 09:44:00 +0000
+++ b/sql/sql_show.cc 2011-08-17 14:33:45 +0000
@@ -384,6 +384,225 @@ bool mysqld_show_privileges(THD *thd)
}
+/** Hash of LEX_STRINGs used to search for ignored db directories. */
+static HASH ignore_db_dirs_hash;
+
+/**
+ An array of LEX_STRING pointers to collect the options at
+ option parsing time.
+*/
+static DYNAMIC_ARRAY ignore_db_dirs_array;
+
+/**
+ A value for the read only system variable to show a list of
+ ignored directories.
+*/
+char *opt_ignore_db_dirs= NULL;
+
+
+/**
+ Sets up the data structures for collection of directories at option
+ processing time.
+ We need to collect the directories in an array first, because
+ we need the character sets initialized before setting up the hash.
+
+ @return state
+ @retval TRUE failed
+ @retval FALSE success
+*/
+
+bool
+ignore_db_dirs_init()
+{
+ return my_init_dynamic_array(&ignore_db_dirs_array, sizeof(LEX_STRING *),
+ 0, 0);
+}
+
+
+/**
+ Retrieves the key (the string itself) from the LEX_STRING hash members.
+
+ Needed by hash_init().
+
+ @param data the data element from the hash
+ @param out len_ret Placeholder to return the length of the key
+ @param unused
+ @return a pointer to the key
+*/
+
+static uchar *
+db_dirs_hash_get_key(const uchar *data, size_t *len_ret,
+ my_bool __attribute__((unused)))
+{
+ LEX_STRING *e= (LEX_STRING *) data;
+
+ *len_ret= e->length;
+ return (uchar *) e->str;
+}
+
+
+/**
+ Wrap a directory name into a LEX_STRING and push it to the array.
+
+ Called at option processing time for each --ignore-db-dir option.
+
+ @param path the name of the directory to push
+ @return state
+ @retval TRUE failed
+ @retval FALSE success
+*/
+
+bool
+push_ignored_db_dir(char *path)
+{
+ LEX_STRING *new_elt;
+ char *new_elt_buffer;
+ size_t path_len= strlen(path);
+
+ if (!path_len || path_len >= FN_REFLEN)
+ return true;
+
+ // No need to normalize, it's only a directory name, not a path.
+ if (!my_multi_malloc(0,
+ &new_elt, sizeof(LEX_STRING),
+ &new_elt_buffer, path_len + 1,
+ NullS))
+ return true;
+ new_elt->str= new_elt_buffer;
+ memcpy(new_elt_buffer, path, path_len);
+ new_elt_buffer[path_len]= 0;
+ new_elt->length= path_len;
+ return insert_dynamic(&ignore_db_dirs_array, &new_elt);
+}
+
+
+/**
+ Clean up the directory ignore options accumulated so far.
+
+ Called at option processing time for each --ignore-db-dir option
+ with an empty argument.
+*/
+
+void
+ignore_db_dirs_reset()
+{
+ LEX_STRING **elt;
+ while (NULL!= (elt= (LEX_STRING **) pop_dynamic(&ignore_db_dirs_array)))
+ if (elt && *elt)
+ my_free(*elt);
+}
+
+
+/**
+ Free the directory ignore option variables.
+
+ Called at server shutdown.
+*/
+
+void
+ignore_db_dirs_free()
+{
+ if (opt_ignore_db_dirs)
+ my_free(opt_ignore_db_dirs);
+ ignore_db_dirs_reset();
+ delete_dynamic(&ignore_db_dirs_array);
+ my_hash_free(&ignore_db_dirs_hash);
+}
+
+
+/**
+ Initialize the ignore db directories hash and status variable from
+ the options collected in the array.
+
+ Called when option processing is over and the server's in-memory
+ structures are fully initialized.
+
+ @return state
+ @retval TRUE failed
+ @retval FALSE success
+*/
+
+bool
+ignore_db_dirs_process_additions()
+{
+ ulong i;
+ size_t len;
+ char *ptr;
+ LEX_STRING *dir;
+
+ DBUG_ASSERT(opt_ignore_db_dirs == NULL);
+
+ if (my_hash_init(&ignore_db_dirs_hash,
+ lower_case_table_names ?
+ character_set_filesystem : &my_charset_bin,
+ 0, 0, 0, db_dirs_hash_get_key,
+ my_free,
+ HASH_UNIQUE))
+ return true;
+
+ /* len starts from 1 because of the terminating zero. */
+ len= 1;
+ for (i= 0; i < ignore_db_dirs_array.elements; i++)
+ {
+ get_dynamic(&ignore_db_dirs_array, (uchar *) &dir, i);
+ len+= dir->length + 1; // +1 for the comma
+ }
+
+ /* No delimiter for the last directory. */
+ if (len > 1)
+ len--;
+
+ ptr= opt_ignore_db_dirs= (char *) my_malloc(len, MYF(0));
+ if (!ptr)
+ return true;
+
+ /* Make sure we have an empty string to start with. */
+ *ptr= 0;
+
+ for (i= 0; i < ignore_db_dirs_array.elements; i++)
+ {
+ get_dynamic(&ignore_db_dirs_array, (uchar *) &dir, i);
+ if (my_hash_insert(&ignore_db_dirs_hash, (uchar *) dir))
+ return true;
+ ptr= strnmov(ptr, dir->str, dir->length);
+ if (i + 1 < ignore_db_dirs_array.elements)
+ ptr= strmov(ptr, ",");
+
+ /*
+ Set the transferred array element to NULL to avoid double free
+ in case of error.
+ */
+ dir= NULL;
+ set_dynamic(&ignore_db_dirs_array, (uchar *) &dir, i);
+ }
+
+ /*
+ It's OK to empty the array here as the allocated elements are
+ referenced through the hash now.
+ */
+ reset_dynamic(&ignore_db_dirs_array);
+
+ return false;
+}
+
+
+/**
+ Check if a directory name is in the hash of ignored directories.
+
+ @return search result
+ @retval TRUE found
+ @retval FALSE not found
+*/
+
+static inline bool
+is_in_ignore_db_dirs_list(const char *directory)
+{
+ return ignore_db_dirs_hash.records &&
+ NULL != my_hash_search(&ignore_db_dirs_hash, (const uchar *) directory,
+ strlen(directory));
+}
+
+
/*
find_files() - find files in a given directory.
@@ -409,11 +628,7 @@ find_files(THD *thd, List<LEX_STRING> *f
const char *path, const char *wild, bool dir)
{
uint i;
- char *ext;
MY_DIR *dirp;
- FILEINFO *file;
- LEX_STRING *file_name= 0;
- uint file_name_len;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
uint col_access=thd->col_access;
#endif
@@ -445,15 +660,23 @@ find_files(THD *thd, List<LEX_STRING> *f
for (i=0 ; i < (uint) dirp->number_off_files ; i++)
{
char uname[NAME_LEN + 1]; /* Unencoded name */
+ FILEINFO *file;
+ LEX_STRING *file_name= 0;
+ uint file_name_len;
+ char *ext;
+
file=dirp->dir_entry+i;
if (dir)
{ /* Return databases */
- if ((file->name[0] == '.' &&
- ((file->name[1] == '.' && file->name[2] == '\0') ||
- file->name[1] == '\0')))
- continue; /* . or .. */
+ /*
+ Ignore all the directories having names that start with a dot (.).
+ This covers '.' and '..' and other cases like e.g. '.mysqlgui'.
+ Note that since 5.1 database directory names can't start with a
+ dot (.) thanks to table name encoding.
+ */
+ if (file->name[0] == '.')
+ continue;
#ifdef USE_SYMDIR
- char *ext;
char buff[FN_REFLEN];
if (my_use_symdir && !strcmp(ext=fn_ext(file->name), ".sym"))
{
@@ -471,20 +694,9 @@ find_files(THD *thd, List<LEX_STRING> *f
if (!MY_S_ISDIR(file->mystat->st_mode))
continue;
- file_name_len= filename_to_tablename(file->name, uname, sizeof(uname));
- if (wild)
- {
- if (lower_case_table_names)
- {
- if (my_wildcmp(files_charset_info,
- uname, uname + file_name_len,
- wild, wild + wild_length,
- wild_prefix, wild_one,wild_many))
- continue;
- }
- else if (wild_compare(uname, wild, 0))
- continue;
- }
+ if (is_in_ignore_db_dirs_list(file->name))
+ continue;
+
}
else
{
@@ -493,21 +705,24 @@ find_files(THD *thd, List<LEX_STRING> *f
is_prefix(file->name, tmp_file_prefix))
continue;
*ext=0;
- file_name_len= filename_to_tablename(file->name, uname, sizeof(uname));
- if (wild)
+ }
+
+ file_name_len= filename_to_tablename(file->name, uname, sizeof(uname));
+
+ if (wild)
+ {
+ if (lower_case_table_names)
{
- if (lower_case_table_names)
- {
- if (my_wildcmp(files_charset_info,
- uname, uname + file_name_len,
- wild, wild + wild_length,
- wild_prefix, wild_one,wild_many))
- continue;
- }
- else if (wild_compare(uname, wild, 0))
- continue;
+ if (my_wildcmp(files_charset_info,
+ uname, uname + file_name_len,
+ wild, wild + wild_length,
+ wild_prefix, wild_one,wild_many))
+ continue;
}
+ else if (wild_compare(uname, wild, 0))
+ continue;
}
+
#ifndef NO_EMBEDDED_ACCESS_CHECKS
/* Don't show tables where we don't have any privileges */
if (db && !(col_access & TABLE_ACLS))
=== modified file 'sql/sql_show.h'
--- a/sql/sql_show.h 2011-06-30 15:50:45 +0000
+++ b/sql/sql_show.h 2011-08-17 14:33:45 +0000
@@ -131,4 +131,12 @@ enum enum_schema_tables get_schema_table
/* These functions were under INNODB_COMPATIBILITY_HOOKS */
int get_quote_char_for_identifier(THD *thd, const char *name, uint length);
+/* Handle the ignored database directories list for SHOW/I_S. */
+bool ignore_db_dirs_init();
+void ignore_db_dirs_free();
+void ignore_db_dirs_reset();
+bool ignore_db_dirs_process_additions();
+bool push_ignored_db_dir(char *path);
+extern char *opt_ignore_db_dirs;
+
#endif /* SQL_SHOW_H */
=== modified file 'sql/sys_vars.cc'
--- a/sql/sys_vars.cc 2011-07-29 09:55:15 +0000
+++ b/sql/sys_vars.cc 2011-08-17 14:33:45 +0000
@@ -51,6 +51,7 @@
// mysql_user_table_is_in_short_password_format
#include "derror.h" // read_texts
#include "sql_base.h" // close_cached_tables
+#include "sql_show.h" // opt_ignore_db_dirs
#ifdef WITH_PERFSCHEMA_STORAGE_ENGINE
#include "../storage/perfschema/pfs_server.h"
@@ -3486,3 +3487,9 @@ static Sys_var_tz Sys_time_zone(
SESSION_VAR(time_zone), NO_CMD_LINE,
DEFAULT(&default_tz), NO_MUTEX_GUARD, IN_BINLOG);
+static Sys_var_charptr Sys_ignore_db_dirs(
+ "ignore_db_dirs",
+ "The list of directories to ignore when collecting database lists",
+ READ_ONLY GLOBAL_VAR(opt_ignore_db_dirs),
+ NO_CMD_LINE,
+ IN_FS_CHARSET, DEFAULT(0));
No bundle (reason: useless for push emails).
| Thread |
|---|
| • bzr push into mysql-trunk branch (Georgi.Kodinov:3379 to 3380) Bug#11746029 | Georgi Kodinov | 22 Aug |