From: Date: July 2 2008 9:38pm Subject: bzr commit into mysql-6.0-backup branch (cbell:2648) Bug#35230 List-Archive: http://lists.mysql.com/commits/48907 X-Bug: 35230 Message-Id: <200807021938.m62JcFCT012619@mail.mysql.com> MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit #At file:///D:/source/bzr/mysql-6.0-bug-35230/ 2648 Chuck Bell 2008-07-02 BUG#35230 Backup: no backupdir This patch adds the dynamic variable --backupdir which specifies the path where the image files are placed (backup) or accessed (restore) if a path is not specified in the command. The default is the same as --datadir. modified: include/config-netware.h include/config-win.h mysql-test/mysql-test-run.pl mysql-test/r/backup_progress.result mysql-test/t/backup_progress.test scripts/mysqld_safe.sh sql/backup/kernel.cc sql/mysqld.cc sql/set_var.cc sql/set_var.h sql/share/errmsg.txt sql/unireg.h per-file messages: include/config-netware.h Added #define for backupdir. include/config-win.h Added #define for backupdir. mysql-test/mysql-test-run.pl Added backupdir to MTR to properly set the default path for test runs. mysql-test/r/backup_progress.result New result file. mysql-test/t/backup_progress.test Added a mask for backup location because this path will vary from machine to machine. scripts/mysqld_safe.sh Added setting of backupdir for launching on Linux systems. sql/backup/kernel.cc Added code to process the backupdir IFF there is no path specified in the command. sql/mysqld.cc Added code to support backupdir. sql/set_var.cc Added all of the methods needed to check, update, and set a default for the backupdir variable. sql/set_var.h Added extern for backupdir variable named sys_var_backupdir. sql/share/errmsg.txt Added new error message for invalid backupdir paths. sql/unireg.h Added #define for backupdir. === modified file 'include/config-netware.h' --- a/include/config-netware.h 2007-07-23 21:54:55 +0000 +++ b/include/config-netware.h 2008-07-02 19:37:44 +0000 @@ -132,6 +132,7 @@ extern "C" { #define SHAREDIR "share/" #define DEFAULT_CHARSET_HOME "sys:/mysql/" #define DATADIR "data/" +#define BACKUPDIR "data/" /* 64-bit file system calls */ #define SIZEOF_OFF_T 8 === modified file 'include/config-win.h' --- a/include/config-win.h 2008-07-02 11:27:17 +0000 +++ b/include/config-win.h 2008-07-02 19:37:44 +0000 @@ -341,6 +341,7 @@ inline double ulonglong2double(ulonglong #else #define DEFAULT_MYSQL_HOME "c:\\mysql" #define DATADIR "c:\\mysql\\data" +#define BACKUPDIR "c:\\mysql\\data" #define PACKAGE "mysql" #define DEFAULT_BASEDIR "C:\\" #define SHAREDIR "share" === modified file 'mysql-test/mysql-test-run.pl' --- a/mysql-test/mysql-test-run.pl 2008-07-02 07:53:34 +0000 +++ b/mysql-test/mysql-test-run.pl 2008-07-02 19:37:44 +0000 @@ -3663,6 +3663,17 @@ sub mysqld_arguments ($$$$) { mtr_add_arg($args, "%s--datadir=%s", $prefix, $mysqld->{'path_myddir'}); + # + # Add the --backupdir parameter for running backup tests from MTR. + # Note: In the future, this setting should be moved to an option + # file located in the backup suite directory. The file should contain + # the following and be named 'suite.opt': + # + # --backupdir=$MYSQLTEST_VARDIR/tmp + # + mtr_add_arg($args, "%s--backupdir=%s", $prefix, + $mysqld->{'path_myddir'}); + if ( $mysql_version_id >= 50106 ) { === modified file 'mysql-test/r/backup_progress.result' --- a/mysql-test/r/backup_progress.result 2008-07-01 20:32:27 +0000 +++ b/mysql-test/r/backup_progress.result 2008-07-02 19:37:44 +0000 @@ -78,7 +78,7 @@ start_time # stop_time # host_or_server_name localhost username root -backup_file backup_progress_orig.bak +backup_file # user_comment command BACKUP DATABASE backup_progress to 'backup_progress_orig.bak' engines MyISAM, Default, Snapshot @@ -135,7 +135,7 @@ start_time # stop_time # host_or_server_name localhost username root -backup_file backup_progress_orig.bak +backup_file # user_comment command RESTORE FROM 'backup_progress_orig.bak' engines MyISAM, Default, Snapshot === modified file 'mysql-test/t/backup_progress.test' --- a/mysql-test/t/backup_progress.test 2008-06-25 13:39:04 +0000 +++ b/mysql-test/t/backup_progress.test 2008-07-02 19:37:44 +0000 @@ -107,7 +107,7 @@ connection con2; reap; #Show results ---replace_column 1 # 2 # 3 # 4 # 10 # 11 # 12 # +--replace_column 1 # 2 # 3 # 4 # 10 # 11 # 12 # 15 # --query_vertical SELECT ob.* FROM mysql.online_backup AS ob JOIN backup_progress.t1_res AS t1 ON ob.backup_id = t1.id; --replace_column 1 # 3 # 4 # SELECT obp.* FROM mysql.online_backup_progress AS obp JOIN backup_progress.t1_res AS t1 ON obp.backup_id = t1.id; @@ -156,7 +156,7 @@ reap; DELETE FROM backup_progress.t1_res; SELECT MAX(backup_id) INTO @bup_id FROM mysql.online_backup WHERE command LIKE "RESTORE FROM%"; INSERT INTO backup_progress.t1_res (id) VALUES (@bup_id); ---replace_column 1 # 2 # 3 # 4 # 10 # 11 # 12 # +--replace_column 1 # 2 # 3 # 4 # 10 # 11 # 12 # 15 # --query_vertical SELECT ob.* FROM mysql.online_backup AS ob JOIN backup_progress.t1_res AS t1 ON ob.backup_id = t1.id; --replace_column 1 # 3 # 4 # SELECT obp.* FROM mysql.online_backup_progress AS obp JOIN backup_progress.t1_res AS t1 ON obp.backup_id = t1.id; === modified file 'scripts/mysqld_safe.sh' --- a/scripts/mysqld_safe.sh 2008-03-11 16:15:47 +0000 +++ b/scripts/mysqld_safe.sh 2008-07-02 19:37:44 +0000 @@ -156,6 +156,7 @@ parse_arguments() { # these get passed explicitly to mysqld --basedir=*) MY_BASEDIR_VERSION="$val" ;; --datadir=*) DATADIR="$val" ;; + --backupdir=*) DATADIR="$val" ;; --pid-file=*) pid_file="$val" ;; --user=*) user="$val"; SET_USER=1 ;; @@ -534,7 +535,7 @@ fi cmd="$NOHUP_NICENESS" for i in "$ledir/$MYSQLD" "$defaults" "--basedir=$MY_BASEDIR_VERSION" \ - "--datadir=$DATADIR" "$USER_OPTION" + "--datadir=$DATADIR" "--backupdir=$DATADIR" "$USER_OPTION" do cmd="$cmd "`shell_quote_string "$i"` done === modified file 'sql/backup/kernel.cc' --- a/sql/backup/kernel.cc 2008-07-01 13:34:36 +0000 +++ b/sql/backup/kernel.cc 2008-07-02 19:37:44 +0000 @@ -122,6 +122,8 @@ int execute_backup_command(THD *thd, LEX *lex) { int res= 0; + LEX_STRING path; + int path_len= 0; DBUG_ENTER("execute_backup_command"); DBUG_ASSERT(thd && lex); @@ -134,13 +136,31 @@ execute_backup_command(THD *thd, LEX *le if (!context.is_valid()) DBUG_RETURN(send_error(context, ER_BACKUP_CONTEXT_CREATE)); + /* + Prepare the path using the backupdir iff no path included + */ + if (!has_path(lex->backup_dir.str)) + { + path.length= sys_var_backupdir.value_length + lex->backup_dir.length + 1; + path.str= (char *)my_malloc(path.length + 1, MYF(0)); + strcpy(path.str, fn_format(path.str, lex->backup_dir.str, + sys_var_backupdir.value, "", + MY_UNPACK_FILENAME)); + } + else + { + path.length= lex->backup_dir.length + 1; + path.str= (char *)my_malloc(path.length + 1, MYF(0)); + strcpy(path.str, lex->backup_dir.str); + } + switch (lex->sql_command) { case SQLCOM_BACKUP: { // prepare for backup operation - Backup_info *info= context.prepare_for_backup(lex->backup_dir, thd->query, + Backup_info *info= context.prepare_for_backup(path, thd->query, lex->backup_compression); // reports errors @@ -185,7 +205,7 @@ execute_backup_command(THD *thd, LEX *le case SQLCOM_RESTORE: { - Restore_info *info= context.prepare_for_restore(lex->backup_dir, thd->query); + Restore_info *info= context.prepare_for_restore(path, thd->query); if (!info || !info->is_valid()) DBUG_RETURN(send_error(context, ER_BACKUP_RESTORE_PREPARE)); @@ -209,6 +229,7 @@ execute_backup_command(THD *thd, LEX *le } // switch(lex->sql_command) + my_free(path.str, MYF(0)); // free memory for path if (context.close()) DBUG_RETURN(send_error(context, ER_BACKUP_CONTEXT_REMOVE)); @@ -396,7 +417,12 @@ int Backup_restore_ctx::prepare(LEX_STRI // check if location is valid (we assume it is a file path) - bool bad_filename= (location.length == 0); + /* + For this error to work correctly, we need to check original + file specified by the user rather than the path formed + using the backupdir. + */ + bool bad_filename= (m_thd->lex->backup_dir.length == 0); /* On some systems certain file names are invalid. We use @@ -404,13 +430,13 @@ int Backup_restore_ctx::prepare(LEX_STRI */ #if defined(__WIN__) || defined(__EMX__) - bad_filename = bad_filename || check_if_legal_filename(location.str); - + bad_filename = bad_filename || check_if_legal_filename(m_thd->lex->backup_dir.str); + #endif if (bad_filename) { - fatal_error(ER_BAD_PATH, location.str); + fatal_error(ER_BAD_PATH, m_thd->lex->backup_dir.str); return m_error; } @@ -499,7 +525,12 @@ Backup_restore_ctx::prepare_for_backup(L if (!s->open()) { - fatal_error(ER_BACKUP_WRITE_LOC, path.ptr()); + /* + For this error, use the actual value returned instead of the + path complimented with backupdir. + */ + THD *thd= ::current_thd; + fatal_error(ER_BACKUP_WRITE_LOC, thd->lex->backup_dir.str); return NULL; } @@ -576,7 +607,12 @@ Backup_restore_ctx::prepare_for_restore( if (!s->open()) { - fatal_error(ER_BACKUP_READ_LOC, path.ptr()); + /* + For this error, use the actual value returned instead of the + path complimented with backupdir. + */ + THD *thd= ::current_thd; + fatal_error(ER_BACKUP_READ_LOC, thd->lex->backup_dir.str); return NULL; } === modified file 'sql/mysqld.cc' --- a/sql/mysqld.cc 2008-07-01 20:32:27 +0000 +++ b/sql/mysqld.cc 2008-07-02 19:37:44 +0000 @@ -1362,6 +1362,7 @@ void clean_up(bool print_message) my_free(sys_init_slave.value, MYF(MY_ALLOW_ZERO_PTR)); my_free(sys_var_general_log_path.value, MYF(MY_ALLOW_ZERO_PTR)); my_free(sys_var_slow_log_path.value, MYF(MY_ALLOW_ZERO_PTR)); + my_free(sys_var_backupdir.value, MYF(MY_ALLOW_ZERO_PTR)); free_tmpdir(&mysql_tmpdir_list); #ifdef HAVE_REPLICATION my_free(slave_load_tmpdir,MYF(MY_ALLOW_ZERO_PTR)); @@ -5745,6 +5746,8 @@ struct my_option my_long_options[] = "Creating and dropping stored procedures alters ACLs. Disable with --skip-automatic-sp-privileges.", (uchar**) &sp_automatic_privileges, (uchar**) &sp_automatic_privileges, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, + {"backupdir", 'B', "Path used to store backup data.", (uchar**) &sys_var_backupdir.value, + (uchar**) &sys_var_backupdir.value, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, {"basedir", 'b', "Path to installation directory. All paths are usually resolved relative to this.", (uchar**) &mysql_home_ptr, (uchar**) &mysql_home_ptr, 0, GET_STR, REQUIRED_ARG, @@ -7853,6 +7856,22 @@ mysqld_get_one_option(int optid, case 'l': opt_log=1; break; + case 'B': + { + /* + Check if directory exists and we have permission to create file and + write to file. + */ + if (my_access(argument, (F_OK|W_OK))) + { + fprintf(stderr, "The path specified for the variable backupdir cannot" + " be accessed or is invalid. ref:'%s'\n", argument); + exit(1); + } + strcpy(sys_var_backupdir.value, argument); + sys_var_backupdir.value_length= strlen(sys_var_backupdir.value); + break; + } case 'h': strmake(mysql_real_data_home,argument, sizeof(mysql_real_data_home)-1); /* Correct pointer set by my_getopt (for embedded library) */ === modified file 'sql/set_var.cc' --- a/sql/set_var.cc 2008-05-27 19:47:15 +0000 +++ b/sql/set_var.cc 2008-07-02 19:37:44 +0000 @@ -149,6 +149,9 @@ static bool sys_update_general_log_path( static void sys_default_general_log_path(THD *thd, enum_var_type type); static bool sys_update_slow_log_path(THD *thd, set_var * var); static void sys_default_slow_log_path(THD *thd, enum_var_type type); +static int sys_check_backupdir(THD *thd, set_var *var); +static bool sys_update_backupdir(THD *thd, set_var * var); +static void sys_default_backupdir(THD *thd, enum_var_type type); /* Variable definition list @@ -778,6 +781,15 @@ sys_var_str sys_var_slow_log_path(&vars, opt_slow_logname); static sys_var_log_output sys_var_log_output_state(&vars, "log_output", &log_output_options, &log_output_typelib, 0); + +/* + Create the backupdir dynamic variable. +*/ +sys_var_str sys_var_backupdir(&vars, "backupdir", + sys_check_backupdir, + sys_update_backupdir, + sys_default_backupdir, + 0); /* @@ -2614,6 +2626,142 @@ uchar *sys_var_log_output::value_ptr(THD return (uchar*) thd->strmake(tmp.ptr(), length); } +/* + Functions for backupdir variable. +*/ + +/** + Set the default for the backupdir + + @param[IN] buff Buffer to use for making string. + + @returns pointer to string (buff) +*/ +char *make_default_backupdir(char *buff) +{ + strmake(buff, mysql_data_home, FN_REFLEN-5); + return buff; +} + +/** + Check the backupdir value for validity. + + This method ensures the users is specifying a valid path + for the backupdir. Validity in this case means the path + is accessible to the user. + + @param[IN] thd Current thread context. + @param[IN] var The new value set in set_var structure. + + @returns 0 valid, 1 invalid. +*/ +static int sys_check_backupdir(THD *thd, set_var *var) +{ + char path[FN_REFLEN], buff[FN_REFLEN]; + MY_STAT f_stat; + String str(buff, sizeof(buff), system_charset_info), *res; + const char *log_file_str; + size_t path_length; + + if (!(res= var->value->val_str(&str))) + goto err; + + log_file_str= res->c_ptr(); + bzero(&f_stat, sizeof(MY_STAT)); + + /* Get dirname of the file path. */ + (void) dirname_part(path, log_file_str, &path_length); + + /* Dirname is empty if file path is relative. */ + if (!path_length) + return 0; + + /* + Check if directory exists and we have permission to create file and + write to file. + */ + if (my_access(path, (F_OK|W_OK))) + goto err; + + return 0; + +err: + my_error(ER_BACKUP_BACKUPDIR, MYF(0), var->value->str_value.c_ptr()); + return 1; +} + +/** + Update the backupdir variable + + This method is used to change the backupdir variable. + + @param[IN] thd Current thread context. + @param[IN] var The new value set in set_var structure. + + @returns 0 valid, 1 invalid. +*/ +static bool sys_update_backupdir(THD *thd, set_var * var) +{ + char buff[FN_REFLEN]; + char *res= 0, *old_value=(char *)(var ? var->value->str_value.ptr() : 0); + bool result= 0; + uint str_length= (var ? var->value->str_value.length() : 0); + + if (!old_value) + { + old_value= make_default_backupdir(buff); + str_length= strlen(old_value); + } + if (!(res= my_strndup(old_value, str_length, MYF(MY_FAE+MY_WME)))) + { + result= 1; + goto err; + } + + pthread_mutex_lock(&LOCK_global_system_variables); + logger.lock_exclusive(); + old_value= sys_var_backupdir.value; + sys_var_backupdir.value= res; + sys_var_backupdir.value_length= str_length; + logger.unlock(); + pthread_mutex_unlock(&LOCK_global_system_variables); + + if (my_access(sys_var_backupdir.value, (F_OK|W_OK))) + { + /* + There is a problem accessing backupdir specified. Revert to old + one and throw error. + */ + if (old_value) + { + sys_var_backupdir.value= old_value; + sys_var_backupdir.value_length= strlen(old_value); + } + result= 1; + goto err; + } + + return result; + +err: + my_error(ER_BACKUP_BACKUPDIR, MYF(0), var->value->str_value.c_ptr()); + return result; +} + +/** + Set the default value for the backupdir variable + + This method is used to reset the backupdir variable to the + default by calling the update method without a path. + + @param[IN] thd Current thread context. + @param[IN] type Ignored (needed for api). +*/ +static void sys_default_backupdir(THD *thd, enum_var_type type) +{ + sys_update_backupdir(thd, 0); +} + /***************************************************************************** Functions to handle SET NAMES and SET CHARACTER SET === modified file 'sql/set_var.h' --- a/sql/set_var.h 2008-05-27 19:47:15 +0000 +++ b/sql/set_var.h 2008-07-02 19:37:44 +0000 @@ -1345,7 +1345,8 @@ CHARSET_INFO *get_old_charset_by_name(co uchar* find_named(I_List *list, const char *name, uint length, NAMED_LIST **found); -extern sys_var_str sys_var_general_log_path, sys_var_slow_log_path; +extern sys_var_str sys_var_general_log_path, sys_var_slow_log_path, + sys_var_backupdir; /* key_cache functions */ KEY_CACHE *get_key_cache(LEX_STRING *cache_name); === modified file 'sql/share/errmsg.txt' --- a/sql/share/errmsg.txt 2008-07-01 20:32:27 +0000 +++ b/sql/share/errmsg.txt 2008-07-02 19:37:44 +0000 @@ -6366,3 +6366,5 @@ ER_BACKUP_OBTAIN_NAME_LOCK_FAILED ER_BACKUP_RELEASE_NAME_LOCK_FAILED eng "Restore failed to release the name locks on the tables." +ER_BACKUP_BACKUPDIR + eng "The path specified for the variable backupdir cannot be accessed or is invalid. ref: %-.64s" === modified file 'sql/unireg.h' --- a/sql/unireg.h 2008-04-30 16:16:45 +0000 +++ b/sql/unireg.h 2008-07-02 19:37:44 +0000 @@ -32,6 +32,9 @@ #ifndef DATADIR #define DATADIR "data/" #endif +#ifndef BACKUPDIR +#define BACKUPDIR "data/" +#endif #ifndef SHAREDIR #define SHAREDIR "share/" #endif