summaryrefslogtreecommitdiff
path: root/extra/mariabackup/xtrabackup.cc
diff options
context:
space:
mode:
Diffstat (limited to 'extra/mariabackup/xtrabackup.cc')
-rw-r--r--extra/mariabackup/xtrabackup.cc7498
1 files changed, 7498 insertions, 0 deletions
diff --git a/extra/mariabackup/xtrabackup.cc b/extra/mariabackup/xtrabackup.cc
new file mode 100644
index 00000000000..c116d119cee
--- /dev/null
+++ b/extra/mariabackup/xtrabackup.cc
@@ -0,0 +1,7498 @@
+/******************************************************
+XtraBackup: hot backup tool for InnoDB
+(c) 2009-2017 Percona LLC and/or its affiliates
+Originally Created 3/3/2009 Yasufumi Kinoshita
+Written by Alexey Kopytov, Aleksandr Kuzminsky, Stewart Smith, Vadim Tkachenko,
+Yasufumi Kinoshita, Ignacio Nin and Baron Schwartz.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; version 2 of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+
+*******************************************************
+
+This file incorporates work covered by the following copyright and
+permission notice:
+
+Copyright (c) 2000, 2011, MySQL AB & Innobase Oy. All Rights Reserved.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; version 2 of the License.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place, Suite 330, Boston, MA 02111-1307 USA
+
+*******************************************************/
+
+//#define XTRABACKUP_TARGET_IS_PLUGIN
+
+#include <mysql_version.h>
+#include <my_base.h>
+#include <my_getopt.h>
+#include <mysql_com.h>
+#include <my_default.h>
+#include <mysqld.h>
+
+#include <fcntl.h>
+#include <string.h>
+
+#ifdef __linux__
+# include <sys/prctl.h>
+#include <sys/resource.h>
+#endif
+
+
+#include <btr0sea.h>
+#include <dict0priv.h>
+#include <dict0stats.h>
+#include <lock0lock.h>
+#include <log0recv.h>
+#include <row0mysql.h>
+#include <row0quiesce.h>
+#include <srv0start.h>
+#include <buf0dblwr.h>
+
+#include <list>
+#include <sstream>
+#include <set>
+#include <mysql.h>
+
+#define G_PTR uchar*
+
+#include "common.h"
+#include "datasink.h"
+
+#include "xb_regex.h"
+#include "fil_cur.h"
+#include "write_filt.h"
+#include "xtrabackup.h"
+#include "ds_buffer.h"
+#include "ds_tmpfile.h"
+#include "xbstream.h"
+#include "changed_page_bitmap.h"
+#include "read_filt.h"
+#include "wsrep.h"
+#include "innobackupex.h"
+#include "backup_mysql.h"
+#include "backup_copy.h"
+#include "backup_mysql.h"
+#include "xb0xb.h"
+#include "encryption_plugin.h"
+#include <sql_plugin.h>
+#include <srv0srv.h>
+#include <crc_glue.h>
+
+/* TODO: replace with appropriate macros used in InnoDB 5.6 */
+#define PAGE_ZIP_MIN_SIZE_SHIFT 10
+#define DICT_TF_ZSSIZE_SHIFT 1
+#define DICT_TF_FORMAT_ZIP 1
+#define DICT_TF_FORMAT_SHIFT 5
+
+int sys_var_init();
+
+my_bool innodb_inited= 0;
+
+/* === xtrabackup specific options === */
+char xtrabackup_real_target_dir[FN_REFLEN] = "./xtrabackup_backupfiles/";
+char *xtrabackup_target_dir= xtrabackup_real_target_dir;
+my_bool xtrabackup_version = FALSE;
+my_bool xtrabackup_backup = FALSE;
+my_bool xtrabackup_stats = FALSE;
+my_bool xtrabackup_prepare = FALSE;
+my_bool xtrabackup_copy_back = FALSE;
+my_bool xtrabackup_move_back = FALSE;
+my_bool xtrabackup_decrypt_decompress = FALSE;
+my_bool xtrabackup_print_param = FALSE;
+
+my_bool xtrabackup_export = FALSE;
+my_bool xtrabackup_apply_log_only = FALSE;
+
+longlong xtrabackup_use_memory = 100*1024*1024L;
+my_bool xtrabackup_create_ib_logfile = FALSE;
+
+long xtrabackup_throttle = 0; /* 0:unlimited */
+lint io_ticket;
+os_event_t wait_throttle = NULL;
+os_event_t log_copying_stop = NULL;
+
+char *xtrabackup_incremental = NULL;
+lsn_t incremental_lsn;
+lsn_t incremental_to_lsn;
+lsn_t incremental_last_lsn;
+xb_page_bitmap *changed_page_bitmap = NULL;
+
+char *xtrabackup_incremental_basedir = NULL; /* for --backup */
+char *xtrabackup_extra_lsndir = NULL; /* for --backup with --extra-lsndir */
+char *xtrabackup_incremental_dir = NULL; /* for --prepare */
+
+char xtrabackup_real_incremental_basedir[FN_REFLEN];
+char xtrabackup_real_extra_lsndir[FN_REFLEN];
+char xtrabackup_real_incremental_dir[FN_REFLEN];
+
+lsn_t xtrabackup_archived_to_lsn = 0; /* for --archived-to-lsn */
+
+char *xtrabackup_tmpdir;
+
+char *xtrabackup_tables = NULL;
+char *xtrabackup_tables_file = NULL;
+char *xtrabackup_tables_exclude = NULL;
+
+typedef std::list<regex_t> regex_list_t;
+static regex_list_t regex_include_list;
+static regex_list_t regex_exclude_list;
+
+static hash_table_t* tables_include_hash = NULL;
+static hash_table_t* tables_exclude_hash = NULL;
+
+char *xtrabackup_databases = NULL;
+char *xtrabackup_databases_file = NULL;
+char *xtrabackup_databases_exclude = NULL;
+static hash_table_t* databases_include_hash = NULL;
+static hash_table_t* databases_exclude_hash = NULL;
+
+static hash_table_t* inc_dir_tables_hash;
+
+struct xb_filter_entry_struct{
+ char* name;
+ ibool has_tables;
+ hash_node_t name_hash;
+};
+typedef struct xb_filter_entry_struct xb_filter_entry_t;
+
+static ulint thread_nr[SRV_MAX_N_IO_THREADS + 6];
+static os_thread_id_t thread_ids[SRV_MAX_N_IO_THREADS + 6];
+
+lsn_t checkpoint_lsn_start;
+lsn_t checkpoint_no_start;
+lsn_t log_copy_scanned_lsn;
+ibool log_copying = TRUE;
+ibool log_copying_running = FALSE;
+ibool io_watching_thread_running = FALSE;
+
+ibool xtrabackup_logfile_is_renamed = FALSE;
+
+int xtrabackup_parallel;
+
+char *xtrabackup_stream_str = NULL;
+xb_stream_fmt_t xtrabackup_stream_fmt = XB_STREAM_FMT_NONE;
+ibool xtrabackup_stream = FALSE;
+
+const char *xtrabackup_compress_alg = NULL;
+ibool xtrabackup_compress = FALSE;
+uint xtrabackup_compress_threads;
+ulonglong xtrabackup_compress_chunk_size = 0;
+
+const char *xtrabackup_encrypt_algo_names[] =
+{ "NONE", "AES128", "AES192", "AES256", NullS};
+TYPELIB xtrabackup_encrypt_algo_typelib=
+{array_elements(xtrabackup_encrypt_algo_names)-1,"",
+ xtrabackup_encrypt_algo_names, NULL};
+
+ibool xtrabackup_encrypt = FALSE;
+ulong xtrabackup_encrypt_algo;
+char *xtrabackup_encrypt_key = NULL;
+char *xtrabackup_encrypt_key_file = NULL;
+uint xtrabackup_encrypt_threads;
+ulonglong xtrabackup_encrypt_chunk_size = 0;
+
+ulint xtrabackup_rebuild_threads = 1;
+
+/* sleep interval beetween log copy iterations in log copying thread
+in milliseconds (default is 1 second) */
+ulint xtrabackup_log_copy_interval = 1000;
+static ulong max_buf_pool_modified_pct;
+
+/* Ignored option (--log) for MySQL option compatibility */
+char* log_ignored_opt = NULL;
+
+/* === metadata of backup === */
+#define XTRABACKUP_METADATA_FILENAME "xtrabackup_checkpoints"
+char metadata_type[30] = ""; /*[full-backuped|log-applied|
+ full-prepared|incremental]*/
+lsn_t metadata_from_lsn = 0;
+lsn_t metadata_to_lsn = 0;
+lsn_t metadata_last_lsn = 0;
+
+#define XB_LOG_FILENAME "xtrabackup_logfile"
+
+ds_file_t *dst_log_file = NULL;
+
+static char mysql_data_home_buff[2];
+
+const char *defaults_group = "mysqld";
+
+/* === static parameters in ha_innodb.cc */
+
+#define HA_INNOBASE_ROWS_IN_TABLE 10000 /* to get optimization right */
+#define HA_INNOBASE_RANGE_COUNT 100
+
+ulong innobase_large_page_size = 0;
+
+/* The default values for the following, type long or longlong, start-up
+parameters are declared in mysqld.cc: */
+
+long innobase_additional_mem_pool_size = 1*1024*1024L;
+long innobase_buffer_pool_awe_mem_mb = 0;
+long innobase_file_io_threads = 4;
+long innobase_read_io_threads = 4;
+long innobase_write_io_threads = 4;
+long innobase_force_recovery = 0;
+long innobase_log_buffer_size = 1024*1024L;
+long innobase_log_files_in_group = 2;
+long innobase_open_files = 300L;
+
+longlong innobase_page_size = (1LL << 14); /* 16KB */
+static ulong innobase_log_block_size = 512;
+my_bool innobase_fast_checksum = FALSE;
+char* innobase_doublewrite_file = NULL;
+char* innobase_buffer_pool_filename = NULL;
+
+longlong innobase_buffer_pool_size = 8*1024*1024L;
+longlong innobase_log_file_size = 48*1024*1024L;
+
+/* The default values for the following char* start-up parameters
+are determined in innobase_init below: */
+
+char* innobase_ignored_opt = NULL;
+char* innobase_data_home_dir = NULL;
+char* innobase_data_file_path = NULL;
+char* innobase_log_arch_dir = NULL;/* unused */
+/* The following has a misleading name: starting from 4.0.5, this also
+affects Windows: */
+char* innobase_unix_file_flush_method = NULL;
+
+/* Below we have boolean-valued start-up parameters, and their default
+values */
+
+ulong innobase_fast_shutdown = 1;
+my_bool innobase_log_archive = FALSE;/* unused */
+my_bool innobase_use_doublewrite = TRUE;
+my_bool innobase_use_checksums = TRUE;
+my_bool innobase_use_large_pages = FALSE;
+my_bool innobase_file_per_table = FALSE;
+my_bool innobase_locks_unsafe_for_binlog = FALSE;
+my_bool innobase_rollback_on_timeout = FALSE;
+my_bool innobase_create_status_file = FALSE;
+my_bool innobase_adaptive_hash_index = TRUE;
+
+static char *internal_innobase_data_file_path = NULL;
+
+/* The following counter is used to convey information to InnoDB
+about server activity: in selects it is not sensible to call
+srv_active_wake_master_thread after each fetch or search, we only do
+it every INNOBASE_WAKE_INTERVAL'th step. */
+
+#define INNOBASE_WAKE_INTERVAL 32
+ulong innobase_active_counter = 0;
+
+
+static char *xtrabackup_debug_sync = NULL;
+
+my_bool xtrabackup_compact = FALSE;
+my_bool xtrabackup_rebuild_indexes = FALSE;
+
+my_bool xtrabackup_incremental_force_scan = FALSE;
+
+/* The flushed lsn which is read from data files */
+lsn_t min_flushed_lsn= 0;
+lsn_t max_flushed_lsn= 0;
+
+/* The size of archived log file */
+ib_int64_t xtrabackup_arch_file_size = 0ULL;
+/* The minimal LSN of found archived log files */
+lsn_t xtrabackup_arch_first_file_lsn = 0ULL;
+/* The maximum LSN of found archived log files */
+lsn_t xtrabackup_arch_last_file_lsn = 0ULL;
+
+ulong xb_open_files_limit= 0;
+char *xb_plugin_dir;
+char *xb_plugin_load;
+my_bool xb_close_files= FALSE;
+
+/* Datasinks */
+ds_ctxt_t *ds_data = NULL;
+ds_ctxt_t *ds_meta = NULL;
+ds_ctxt_t *ds_redo = NULL;
+
+static bool innobackupex_mode = false;
+
+static long innobase_log_files_in_group_save;
+static char *srv_log_group_home_dir_save;
+static longlong innobase_log_file_size_save;
+
+/* String buffer used by --print-param to accumulate server options as they are
+parsed from the defaults file */
+static std::ostringstream print_param_str;
+
+/* Set of specified parameters */
+std::set<std::string> param_set;
+
+static ulonglong global_max_value;
+
+extern "C" sig_handler handle_fatal_signal(int sig);
+
+my_bool opt_galera_info = FALSE;
+my_bool opt_slave_info = FALSE;
+my_bool opt_no_lock = FALSE;
+my_bool opt_safe_slave_backup = FALSE;
+my_bool opt_rsync = FALSE;
+my_bool opt_force_non_empty_dirs = FALSE;
+my_bool opt_noversioncheck = FALSE;
+my_bool opt_no_backup_locks = FALSE;
+my_bool opt_decompress = FALSE;
+my_bool opt_remove_original = FALSE;
+
+static const char *binlog_info_values[] = {"off", "lockless", "on", "auto",
+ NullS};
+static TYPELIB binlog_info_typelib = {array_elements(binlog_info_values)-1, "",
+ binlog_info_values, NULL};
+ulong opt_binlog_info;
+
+char *opt_incremental_history_name = NULL;
+char *opt_incremental_history_uuid = NULL;
+
+char *opt_user = NULL;
+char *opt_password = NULL;
+char *opt_host = NULL;
+char *opt_defaults_group = NULL;
+char *opt_socket = NULL;
+uint opt_port = 0;
+char *opt_login_path = NULL;
+char *opt_log_bin = NULL;
+
+const char *query_type_names[] = { "ALL", "UPDATE", "SELECT", NullS};
+
+TYPELIB query_type_typelib= {array_elements(query_type_names) - 1, "",
+ query_type_names, NULL};
+
+ulong opt_lock_wait_query_type;
+ulong opt_kill_long_query_type;
+
+ulong opt_decrypt_algo = 0;
+
+uint opt_kill_long_queries_timeout = 0;
+uint opt_lock_wait_timeout = 0;
+uint opt_lock_wait_threshold = 0;
+uint opt_debug_sleep_before_unlock = 0;
+uint opt_safe_slave_backup_timeout = 0;
+
+const char *opt_history = NULL;
+my_bool opt_decrypt = FALSE;
+
+#if defined(HAVE_OPENSSL)
+my_bool opt_ssl_verify_server_cert = FALSE;
+#if !defined(HAVE_YASSL)
+char *opt_server_public_key = NULL;
+#endif
+#endif
+
+/* Whether xtrabackup_binlog_info should be created on recovery */
+static bool recover_binlog_info;
+
+/* Simple datasink creation tracking...add datasinks in the reverse order you
+want them destroyed. */
+#define XTRABACKUP_MAX_DATASINKS 10
+static ds_ctxt_t *datasinks[XTRABACKUP_MAX_DATASINKS];
+static uint actual_datasinks = 0;
+static inline
+void
+xtrabackup_add_datasink(ds_ctxt_t *ds)
+{
+ xb_ad(actual_datasinks < XTRABACKUP_MAX_DATASINKS);
+ datasinks[actual_datasinks] = ds; actual_datasinks++;
+}
+
+/* ======== Datafiles iterator ======== */
+datafiles_iter_t *
+datafiles_iter_new(fil_system_t *f_system)
+{
+ datafiles_iter_t *it;
+
+ it = static_cast<datafiles_iter_t *>
+ (ut_malloc(sizeof(datafiles_iter_t)));
+ it->mutex = os_mutex_create();
+
+ it->system = f_system;
+ it->space = NULL;
+ it->node = NULL;
+ it->started = FALSE;
+
+ return it;
+}
+
+fil_node_t *
+datafiles_iter_next(datafiles_iter_t *it)
+{
+ fil_node_t *new_node;
+
+ os_mutex_enter(it->mutex);
+
+ if (it->node == NULL) {
+ if (it->started)
+ goto end;
+ it->started = TRUE;
+ } else {
+ it->node = UT_LIST_GET_NEXT(chain, it->node);
+ if (it->node != NULL)
+ goto end;
+ }
+
+ it->space = (it->space == NULL) ?
+ UT_LIST_GET_FIRST(it->system->space_list) :
+ UT_LIST_GET_NEXT(space_list, it->space);
+
+ while (it->space != NULL &&
+ (it->space->purpose != FIL_TABLESPACE ||
+ UT_LIST_GET_LEN(it->space->chain) == 0))
+ it->space = UT_LIST_GET_NEXT(space_list, it->space);
+ if (it->space == NULL)
+ goto end;
+
+ it->node = UT_LIST_GET_FIRST(it->space->chain);
+
+end:
+ new_node = it->node;
+ os_mutex_exit(it->mutex);
+
+ return new_node;
+}
+
+void
+datafiles_iter_free(datafiles_iter_t *it)
+{
+ os_mutex_free(it->mutex);
+ ut_free(it);
+}
+
+/* ======== Date copying thread context ======== */
+
+typedef struct {
+ datafiles_iter_t *it;
+ uint num;
+ uint *count;
+ os_ib_mutex_t count_mutex;
+ os_thread_id_t id;
+} data_thread_ctxt_t;
+
+/* ======== for option and variables ======== */
+
+enum options_xtrabackup
+{
+ OPT_XTRA_TARGET_DIR = 1000, /* make sure it is larger
+ than OPT_MAX_CLIENT_OPTION */
+ OPT_XTRA_BACKUP,
+ OPT_XTRA_STATS,
+ OPT_XTRA_PREPARE,
+ OPT_XTRA_EXPORT,
+ OPT_XTRA_APPLY_LOG_ONLY,
+ OPT_XTRA_PRINT_PARAM,
+ OPT_XTRA_USE_MEMORY,
+ OPT_XTRA_THROTTLE,
+ OPT_XTRA_LOG_COPY_INTERVAL,
+ OPT_XTRA_INCREMENTAL,
+ OPT_XTRA_INCREMENTAL_BASEDIR,
+ OPT_XTRA_EXTRA_LSNDIR,
+ OPT_XTRA_INCREMENTAL_DIR,
+ OPT_XTRA_ARCHIVED_TO_LSN,
+ OPT_XTRA_TABLES,
+ OPT_XTRA_TABLES_FILE,
+ OPT_XTRA_DATABASES,
+ OPT_XTRA_DATABASES_FILE,
+ OPT_XTRA_CREATE_IB_LOGFILE,
+ OPT_XTRA_PARALLEL,
+ OPT_XTRA_STREAM,
+ OPT_XTRA_COMPRESS,
+ OPT_XTRA_COMPRESS_THREADS,
+ OPT_XTRA_COMPRESS_CHUNK_SIZE,
+ OPT_XTRA_ENCRYPT,
+ OPT_XTRA_ENCRYPT_KEY,
+ OPT_XTRA_ENCRYPT_KEY_FILE,
+ OPT_XTRA_ENCRYPT_THREADS,
+ OPT_XTRA_ENCRYPT_CHUNK_SIZE,
+ OPT_LOG,
+ OPT_INNODB,
+ OPT_INNODB_CHECKSUMS,
+ OPT_INNODB_DATA_FILE_PATH,
+ OPT_INNODB_DATA_HOME_DIR,
+ OPT_INNODB_ADAPTIVE_HASH_INDEX,
+ OPT_INNODB_DOUBLEWRITE,
+ OPT_INNODB_FAST_SHUTDOWN,
+ OPT_INNODB_FILE_PER_TABLE,
+ OPT_INNODB_FLUSH_LOG_AT_TRX_COMMIT,
+ OPT_INNODB_FLUSH_METHOD,
+ OPT_INNODB_LOCKS_UNSAFE_FOR_BINLOG,
+ OPT_INNODB_LOG_ARCH_DIR,
+ OPT_INNODB_LOG_ARCHIVE,
+ OPT_INNODB_LOG_GROUP_HOME_DIR,
+ OPT_INNODB_MAX_DIRTY_PAGES_PCT,
+ OPT_INNODB_MAX_PURGE_LAG,
+ OPT_INNODB_ROLLBACK_ON_TIMEOUT,
+ OPT_INNODB_STATUS_FILE,
+ OPT_INNODB_ADDITIONAL_MEM_POOL_SIZE,
+ OPT_INNODB_AUTOEXTEND_INCREMENT,
+ OPT_INNODB_BUFFER_POOL_SIZE,
+ OPT_INNODB_COMMIT_CONCURRENCY,
+ OPT_INNODB_CONCURRENCY_TICKETS,
+ OPT_INNODB_FILE_IO_THREADS,
+ OPT_INNODB_IO_CAPACITY,
+ OPT_INNODB_READ_IO_THREADS,
+ OPT_INNODB_WRITE_IO_THREADS,
+ OPT_INNODB_USE_NATIVE_AIO,
+ OPT_INNODB_PAGE_SIZE,
+ OPT_INNODB_LOG_BLOCK_SIZE,
+ OPT_INNODB_FAST_CHECKSUM,
+ OPT_INNODB_EXTRA_UNDOSLOTS,
+ OPT_INNODB_DOUBLEWRITE_FILE,
+ OPT_INNODB_BUFFER_POOL_FILENAME,
+ OPT_INNODB_FORCE_RECOVERY,
+ OPT_INNODB_LOCK_WAIT_TIMEOUT,
+ OPT_INNODB_LOG_BUFFER_SIZE,
+ OPT_INNODB_LOG_FILE_SIZE,
+ OPT_INNODB_LOG_FILES_IN_GROUP,
+ OPT_INNODB_MIRRORED_LOG_GROUPS,
+ OPT_INNODB_OPEN_FILES,
+ OPT_INNODB_SYNC_SPIN_LOOPS,
+ OPT_INNODB_THREAD_CONCURRENCY,
+ OPT_INNODB_THREAD_SLEEP_DELAY,
+ OPT_XTRA_DEBUG_SYNC,
+ OPT_XTRA_COMPACT,
+ OPT_XTRA_REBUILD_INDEXES,
+ OPT_XTRA_REBUILD_THREADS,
+ OPT_INNODB_CHECKSUM_ALGORITHM,
+ OPT_INNODB_UNDO_DIRECTORY,
+ OPT_INNODB_UNDO_TABLESPACES,
+ OPT_INNODB_LOG_CHECKSUM_ALGORITHM,
+ OPT_XTRA_INCREMENTAL_FORCE_SCAN,
+ OPT_DEFAULTS_GROUP,
+ OPT_OPEN_FILES_LIMIT,
+ OPT_PLUGIN_DIR,
+ OPT_PLUGIN_LOAD,
+ OPT_INNODB_ENCRYPT_LOG,
+ OPT_CLOSE_FILES,
+ OPT_CORE_FILE,
+
+ OPT_COPY_BACK,
+ OPT_MOVE_BACK,
+ OPT_GALERA_INFO,
+ OPT_SLAVE_INFO,
+ OPT_NO_LOCK,
+ OPT_SAFE_SLAVE_BACKUP,
+ OPT_RSYNC,
+ OPT_FORCE_NON_EMPTY_DIRS,
+ OPT_NO_VERSION_CHECK,
+ OPT_NO_BACKUP_LOCKS,
+ OPT_DECOMPRESS,
+ OPT_INCREMENTAL_HISTORY_NAME,
+ OPT_INCREMENTAL_HISTORY_UUID,
+ OPT_DECRYPT,
+ OPT_REMOVE_ORIGINAL,
+ OPT_LOCK_WAIT_QUERY_TYPE,
+ OPT_KILL_LONG_QUERY_TYPE,
+ OPT_HISTORY,
+ OPT_KILL_LONG_QUERIES_TIMEOUT,
+ OPT_LOCK_WAIT_TIMEOUT,
+ OPT_LOCK_WAIT_THRESHOLD,
+ OPT_DEBUG_SLEEP_BEFORE_UNLOCK,
+ OPT_SAFE_SLAVE_BACKUP_TIMEOUT,
+ OPT_BINLOG_INFO,
+ OPT_XB_SECURE_AUTH,
+
+ OPT_SSL_SSL,
+ OPT_SSL_VERIFY_SERVER_CERT,
+ OPT_SERVER_PUBLIC_KEY,
+
+ OPT_XTRA_TABLES_EXCLUDE,
+ OPT_XTRA_DATABASES_EXCLUDE,
+};
+
+struct my_option xb_client_options[] =
+{
+ {"version", 'v', "print xtrabackup version information",
+ (G_PTR *) &xtrabackup_version, (G_PTR *) &xtrabackup_version, 0, GET_BOOL,
+ NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"target-dir", OPT_XTRA_TARGET_DIR, "destination directory", (G_PTR*) &xtrabackup_target_dir,
+ (G_PTR*) &xtrabackup_target_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"backup", OPT_XTRA_BACKUP, "take backup to target-dir",
+ (G_PTR*) &xtrabackup_backup, (G_PTR*) &xtrabackup_backup,
+ 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"stats", OPT_XTRA_STATS, "calc statistic of datadir (offline mysqld is recommended)",
+ (G_PTR*) &xtrabackup_stats, (G_PTR*) &xtrabackup_stats,
+ 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"prepare", OPT_XTRA_PREPARE, "prepare a backup for starting mysql server on the backup.",
+ (G_PTR*) &xtrabackup_prepare, (G_PTR*) &xtrabackup_prepare,
+ 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"export", OPT_XTRA_EXPORT, "create files to import to another database when prepare.",
+ (G_PTR*) &xtrabackup_export, (G_PTR*) &xtrabackup_export,
+ 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"apply-log-only", OPT_XTRA_APPLY_LOG_ONLY,
+ "stop recovery process not to progress LSN after applying log when prepare.",
+ (G_PTR*) &xtrabackup_apply_log_only, (G_PTR*) &xtrabackup_apply_log_only,
+ 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"print-param", OPT_XTRA_PRINT_PARAM, "print parameter of mysqld needed for copyback.",
+ (G_PTR*) &xtrabackup_print_param, (G_PTR*) &xtrabackup_print_param,
+ 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"use-memory", OPT_XTRA_USE_MEMORY, "The value is used instead of buffer_pool_size",
+ (G_PTR*) &xtrabackup_use_memory, (G_PTR*) &xtrabackup_use_memory,
+ 0, GET_LL, REQUIRED_ARG, 100*1024*1024L, 1024*1024L, LONGLONG_MAX, 0,
+ 1024*1024L, 0},
+ {"throttle", OPT_XTRA_THROTTLE, "limit count of IO operations (pairs of read&write) per second to IOS values (for '--backup')",
+ (G_PTR*) &xtrabackup_throttle, (G_PTR*) &xtrabackup_throttle,
+ 0, GET_LONG, REQUIRED_ARG, 0, 0, LONG_MAX, 0, 1, 0},
+ {"log", OPT_LOG, "Ignored option for MySQL option compatibility",
+ (G_PTR*) &log_ignored_opt, (G_PTR*) &log_ignored_opt, 0,
+ GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
+ {"log-copy-interval", OPT_XTRA_LOG_COPY_INTERVAL, "time interval between checks done by log copying thread in milliseconds (default is 1 second).",
+ (G_PTR*) &xtrabackup_log_copy_interval, (G_PTR*) &xtrabackup_log_copy_interval,
+ 0, GET_LONG, REQUIRED_ARG, 1000, 0, LONG_MAX, 0, 1, 0},
+ {"extra-lsndir", OPT_XTRA_EXTRA_LSNDIR, "(for --backup): save an extra copy of the xtrabackup_checkpoints file in this directory.",
+ (G_PTR*) &xtrabackup_extra_lsndir, (G_PTR*) &xtrabackup_extra_lsndir,
+ 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"incremental-lsn", OPT_XTRA_INCREMENTAL, "(for --backup): copy only .ibd pages newer than specified LSN 'high:low'. ##ATTENTION##: If a wrong LSN value is specified, it is impossible to diagnose this, causing the backup to be unusable. Be careful!",
+ (G_PTR*) &xtrabackup_incremental, (G_PTR*) &xtrabackup_incremental,
+ 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"incremental-basedir", OPT_XTRA_INCREMENTAL_BASEDIR, "(for --backup): copy only .ibd pages newer than backup at specified directory.",
+ (G_PTR*) &xtrabackup_incremental_basedir, (G_PTR*) &xtrabackup_incremental_basedir,
+ 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"incremental-dir", OPT_XTRA_INCREMENTAL_DIR, "(for --prepare): apply .delta files and logfile in the specified directory.",
+ (G_PTR*) &xtrabackup_incremental_dir, (G_PTR*) &xtrabackup_incremental_dir,
+ 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"to-archived-lsn", OPT_XTRA_ARCHIVED_TO_LSN,
+ "Don't apply archived logs with bigger log sequence number.",
+ (G_PTR*) &xtrabackup_archived_to_lsn, (G_PTR*) &xtrabackup_archived_to_lsn, 0,
+ GET_LL, REQUIRED_ARG, 0, 0, LONGLONG_MAX, 0, 0, 0},
+ {"tables", OPT_XTRA_TABLES, "filtering by regexp for table names.",
+ (G_PTR*) &xtrabackup_tables, (G_PTR*) &xtrabackup_tables,
+ 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"tables_file", OPT_XTRA_TABLES_FILE, "filtering by list of the exact database.table name in the file.",
+ (G_PTR*) &xtrabackup_tables_file, (G_PTR*) &xtrabackup_tables_file,
+ 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"databases", OPT_XTRA_DATABASES, "filtering by list of databases.",
+ (G_PTR*) &xtrabackup_databases, (G_PTR*) &xtrabackup_databases,
+ 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"databases_file", OPT_XTRA_TABLES_FILE,
+ "filtering by list of databases in the file.",
+ (G_PTR*) &xtrabackup_databases_file, (G_PTR*) &xtrabackup_databases_file,
+ 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"tables-exclude", OPT_XTRA_TABLES_EXCLUDE, "filtering by regexp for table names. "
+ "Operates the same way as --tables, but matched names are excluded from backup. "
+ "Note that this option has a higher priority than --tables.",
+ (G_PTR*) &xtrabackup_tables_exclude, (G_PTR*) &xtrabackup_tables_exclude,
+ 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"databases-exclude", OPT_XTRA_DATABASES_EXCLUDE, "Excluding databases based on name, "
+ "Operates the same way as --databases, but matched names are excluded from backup. "
+ "Note that this option has a higher priority than --databases.",
+ (G_PTR*) &xtrabackup_databases_exclude, (G_PTR*) &xtrabackup_databases_exclude,
+ 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"create-ib-logfile", OPT_XTRA_CREATE_IB_LOGFILE, "** not work for now** creates ib_logfile* also after '--prepare'. ### If you want create ib_logfile*, only re-execute this command in same options. ###",
+ (G_PTR*) &xtrabackup_create_ib_logfile, (G_PTR*) &xtrabackup_create_ib_logfile,
+ 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+
+ {"stream", OPT_XTRA_STREAM, "Stream all backup files to the standard output "
+ "in the specified format."
+#ifdef HAVE_LIBARCHIVE
+ "Supported formats are 'tar' and 'xbstream'."
+#else
+ "Supported format is 'xbstream'."
+#endif
+ ,
+ (G_PTR*) &xtrabackup_stream_str, (G_PTR*) &xtrabackup_stream_str, 0, GET_STR,
+ REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+
+ {"compress", OPT_XTRA_COMPRESS, "Compress individual backup files using the "
+ "specified compression algorithm. Currently the only supported algorithm "
+ "is 'quicklz'. It is also the default algorithm, i.e. the one used when "
+ "--compress is used without an argument.",
+ (G_PTR*) &xtrabackup_compress_alg, (G_PTR*) &xtrabackup_compress_alg, 0,
+ GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
+
+ {"compress-threads", OPT_XTRA_COMPRESS_THREADS,
+ "Number of threads for parallel data compression. The default value is 1.",
+ (G_PTR*) &xtrabackup_compress_threads, (G_PTR*) &xtrabackup_compress_threads,
+ 0, GET_UINT, REQUIRED_ARG, 1, 1, UINT_MAX, 0, 0, 0},
+
+ {"compress-chunk-size", OPT_XTRA_COMPRESS_CHUNK_SIZE,
+ "Size of working buffer(s) for compression threads in bytes. The default value is 64K.",
+ (G_PTR*) &xtrabackup_compress_chunk_size, (G_PTR*) &xtrabackup_compress_chunk_size,
+ 0, GET_ULL, REQUIRED_ARG, (1 << 16), 1024, ULONGLONG_MAX, 0, 0, 0},
+
+ {"encrypt", OPT_XTRA_ENCRYPT, "Encrypt individual backup files using the "
+ "specified encryption algorithm.",
+ &xtrabackup_encrypt_algo, &xtrabackup_encrypt_algo,
+ &xtrabackup_encrypt_algo_typelib, GET_ENUM, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+
+ {"encrypt-key", OPT_XTRA_ENCRYPT_KEY, "Encryption key to use.",
+ (G_PTR*) &xtrabackup_encrypt_key, (G_PTR*) &xtrabackup_encrypt_key, 0,
+ GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+
+ {"encrypt-key-file", OPT_XTRA_ENCRYPT_KEY_FILE, "File which contains encryption key to use.",
+ (G_PTR*) &xtrabackup_encrypt_key_file, (G_PTR*) &xtrabackup_encrypt_key_file, 0,
+ GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+
+ {"encrypt-threads", OPT_XTRA_ENCRYPT_THREADS,
+ "Number of threads for parallel data encryption. The default value is 1.",
+ (G_PTR*) &xtrabackup_encrypt_threads, (G_PTR*) &xtrabackup_encrypt_threads,
+ 0, GET_UINT, REQUIRED_ARG, 1, 1, UINT_MAX, 0, 0, 0},
+
+ {"encrypt-chunk-size", OPT_XTRA_ENCRYPT_CHUNK_SIZE,
+ "Size of working buffer(S) for encryption threads in bytes. The default value is 64K.",
+ (G_PTR*) &xtrabackup_encrypt_chunk_size, (G_PTR*) &xtrabackup_encrypt_chunk_size,
+ 0, GET_ULL, REQUIRED_ARG, (1 << 16), 1024, ULONGLONG_MAX, 0, 0, 0},
+
+ {"compact", OPT_XTRA_COMPACT,
+ "Create a compact backup by skipping secondary index pages.",
+ (G_PTR*) &xtrabackup_compact, (G_PTR*) &xtrabackup_compact,
+ 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+
+ {"rebuild_indexes", OPT_XTRA_REBUILD_INDEXES,
+ "Rebuild secondary indexes in InnoDB tables after applying the log. "
+ "Only has effect with --prepare.",
+ (G_PTR*) &xtrabackup_rebuild_indexes, (G_PTR*) &xtrabackup_rebuild_indexes,
+ 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+
+ {"rebuild_threads", OPT_XTRA_REBUILD_THREADS,
+ "Use this number of threads to rebuild indexes in a compact backup. "
+ "Only has effect with --prepare and --rebuild-indexes.",
+ (G_PTR*) &xtrabackup_rebuild_threads, (G_PTR*) &xtrabackup_rebuild_threads,
+ 0, GET_UINT, REQUIRED_ARG, 1, 1, UINT_MAX, 0, 0, 0},
+
+ {"incremental-force-scan", OPT_XTRA_INCREMENTAL_FORCE_SCAN,
+ "Perform a full-scan incremental backup even in the presence of changed "
+ "page bitmap data",
+ (G_PTR*)&xtrabackup_incremental_force_scan,
+ (G_PTR*)&xtrabackup_incremental_force_scan, 0, GET_BOOL, NO_ARG,
+ 0, 0, 0, 0, 0, 0},
+
+
+ {"close_files", OPT_CLOSE_FILES, "do not keep files opened. Use at your own "
+ "risk.", (G_PTR*) &xb_close_files, (G_PTR*) &xb_close_files, 0, GET_BOOL,
+ NO_ARG, 0, 0, 0, 0, 0, 0},
+
+ {"core-file", OPT_CORE_FILE, "Write core on fatal signals", 0, 0, 0,
+ GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0},
+
+
+ {"copy-back", OPT_COPY_BACK, "Copy all the files in a previously made "
+ "backup from the backup directory to their original locations.",
+ (uchar *) &xtrabackup_copy_back, (uchar *) &xtrabackup_copy_back, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+
+ {"move-back", OPT_MOVE_BACK, "Move all the files in a previously made "
+ "backup from the backup directory to the actual datadir location. "
+ "Use with caution, as it removes backup files.",
+ (uchar *) &xtrabackup_move_back, (uchar *) &xtrabackup_move_back, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+
+ {"galera-info", OPT_GALERA_INFO, "This options creates the "
+ "xtrabackup_galera_info file which contains the local node state at "
+ "the time of the backup. Option should be used when performing the "
+ "backup of Percona-XtraDB-Cluster. Has no effect when backup locks "
+ "are used to create the backup.",
+ (uchar *) &opt_galera_info, (uchar *) &opt_galera_info, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+
+ {"slave-info", OPT_SLAVE_INFO, "This option is useful when backing "
+ "up a replication slave server. It prints the binary log position "
+ "and name of the master server. It also writes this information to "
+ "the \"xtrabackup_slave_info\" file as a \"CHANGE MASTER\" command. "
+ "A new slave for this master can be set up by starting a slave server "
+ "on this backup and issuing a \"CHANGE MASTER\" command with the "
+ "binary log position saved in the \"xtrabackup_slave_info\" file.",
+ (uchar *) &opt_slave_info, (uchar *) &opt_slave_info, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+
+ {"no-lock", OPT_NO_LOCK, "Use this option to disable table lock "
+ "with \"FLUSH TABLES WITH READ LOCK\". Use it only if ALL your "
+ "tables are InnoDB and you DO NOT CARE about the binary log "
+ "position of the backup. This option shouldn't be used if there "
+ "are any DDL statements being executed or if any updates are "
+ "happening on non-InnoDB tables (this includes the system MyISAM "
+ "tables in the mysql database), otherwise it could lead to an "
+ "inconsistent backup. If you are considering to use --no-lock "
+ "because your backups are failing to acquire the lock, this could "
+ "be because of incoming replication events preventing the lock "
+ "from succeeding. Please try using --safe-slave-backup to "
+ "momentarily stop the replication slave thread, this may help "
+ "the backup to succeed and you then don't need to resort to "
+ "using this option.",
+ (uchar *) &opt_no_lock, (uchar *) &opt_no_lock, 0,
+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+
+ {"safe-slave-backup", OPT_SAFE_SLAVE_BACKUP, "Stop slave SQL thread "
+ "and wait to start backup until Slave_open_temp_tables in "
+ "\"SHOW STATUS\" is zero. If there are no open temporary tables, "
+ "the backup will take place, otherwise the SQL thread will be "
+ "started and stopped until there are no open temporary tables. "
+ "The backup will fail if Slave_open_temp_tables does not become "
+ "zero after --safe-slave-backup-timeout seconds. The slave SQL "
+ "thread will be restarted when the backup finishes.",
+ (uchar *) &opt_safe_slave_backup,
+ (uchar *) &opt_safe_slave_backup,
+ 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+
+ {"rsync", OPT_RSYNC, "Uses the rsync utility to optimize local file "
+ "transfers. When this option is specified, innobackupex uses rsync "
+ "to copy all non-InnoDB files instead of spawning a separate cp for "
+ "each file, which can be much faster for servers with a large number "
+ "of databases or tables. This option cannot be used together with "
+ "--stream.",
+ (uchar *) &opt_rsync, (uchar *) &opt_rsync,
+ 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+
+ {"force-non-empty-directories", OPT_FORCE_NON_EMPTY_DIRS, "This "
+ "option, when specified, makes --copy-back or --move-back transfer "
+ "files to non-empty directories. Note that no existing files will be "
+ "overwritten. If --copy-back or --nove-back has to copy a file from "
+ "the backup directory which already exists in the destination "
+ "directory, it will still fail with an error.",
+ (uchar *) &opt_force_non_empty_dirs,
+ (uchar *) &opt_force_non_empty_dirs,
+ 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+
+ {"no-version-check", OPT_NO_VERSION_CHECK, "This option disables the "
+ "version check which is enabled by the --version-check option.",
+ (uchar *) &opt_noversioncheck,
+ (uchar *) &opt_noversioncheck,
+ 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+
+ {"no-backup-locks", OPT_NO_BACKUP_LOCKS, "This option controls if "
+ "backup locks should be used instead of FLUSH TABLES WITH READ LOCK "
+ "on the backup stage. The option has no effect when backup locks are "
+ "not supported by the server. This option is enabled by default, "
+ "disable with --no-backup-locks.",
+ (uchar *) &opt_no_backup_locks,
+ (uchar *) &opt_no_backup_locks,
+ 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+
+ {"decompress", OPT_DECOMPRESS, "Decompresses all files with the .qp "
+ "extension in a backup previously made with the --compress option.",
+ (uchar *) &opt_decompress,
+ (uchar *) &opt_decompress,
+ 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+
+ {"user", 'u', "This option specifies the MySQL username used "
+ "when connecting to the server, if that's not the current user. "
+ "The option accepts a string argument. See mysql --help for details.",
+ (uchar*) &opt_user, (uchar*) &opt_user, 0, GET_STR,
+ REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+
+ {"host", 'H', "This option specifies the host to use when "
+ "connecting to the database server with TCP/IP. The option accepts "
+ "a string argument. See mysql --help for details.",
+ (uchar*) &opt_host, (uchar*) &opt_host, 0, GET_STR,
+ REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+
+ {"port", 'P', "This option specifies the port to use when "
+ "connecting to the database server with TCP/IP. The option accepts "
+ "a string argument. See mysql --help for details.",
+ &opt_port, &opt_port, 0, GET_UINT, REQUIRED_ARG,
+ 0, 0, 0, 0, 0, 0},
+
+ {"password", 'p', "This option specifies the password to use "
+ "when connecting to the database. It accepts a string argument. "
+ "See mysql --help for details.",
+ 0, 0, 0, GET_STR,
+ REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+
+ {"socket", 'S', "This option specifies the socket to use when "
+ "connecting to the local database server with a UNIX domain socket. "
+ "The option accepts a string argument. See mysql --help for details.",
+ (uchar*) &opt_socket, (uchar*) &opt_socket, 0, GET_STR,
+ REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+
+ {"incremental-history-name", OPT_INCREMENTAL_HISTORY_NAME,
+ "This option specifies the name of the backup series stored in the "
+ "PERCONA_SCHEMA.xtrabackup_history history record to base an "
+ "incremental backup on. Xtrabackup will search the history table "
+ "looking for the most recent (highest innodb_to_lsn), successful "
+ "backup in the series and take the to_lsn value to use as the "
+ "starting lsn for the incremental backup. This will be mutually "
+ "exclusive with --incremental-history-uuid, --incremental-basedir "
+ "and --incremental-lsn. If no valid lsn can be found (no series by "
+ "that name, no successful backups by that name) xtrabackup will "
+ "return with an error. It is used with the --incremental option.",
+ (uchar*) &opt_incremental_history_name,
+ (uchar*) &opt_incremental_history_name, 0, GET_STR,
+ REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+
+ {"incremental-history-uuid", OPT_INCREMENTAL_HISTORY_UUID,
+ "This option specifies the UUID of the specific history record "
+ "stored in the PERCONA_SCHEMA.xtrabackup_history to base an "
+ "incremental backup on. --incremental-history-name, "
+ "--incremental-basedir and --incremental-lsn. If no valid lsn can be "
+ "found (no success record with that uuid) xtrabackup will return "
+ "with an error. It is used with the --incremental option.",
+ (uchar*) &opt_incremental_history_uuid,
+ (uchar*) &opt_incremental_history_uuid, 0, GET_STR,
+ REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+
+ {"decrypt", OPT_DECRYPT, "Decrypts all files with the .xbcrypt "
+ "extension in a backup previously made with --encrypt option.",
+ &opt_decrypt_algo, &opt_decrypt_algo,
+ &xtrabackup_encrypt_algo_typelib, GET_ENUM, REQUIRED_ARG,
+ 0, 0, 0, 0, 0, 0},
+
+ {"remove-original", OPT_REMOVE_ORIGINAL, "Remove .qp and .xbcrypt files "
+ "after decryption and decompression.",
+ (uchar *) &opt_remove_original,
+ (uchar *) &opt_remove_original,
+ 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+
+ {"ftwrl-wait-query-type", OPT_LOCK_WAIT_QUERY_TYPE,
+ "This option specifies which types of queries are allowed to complete "
+ "before innobackupex will issue the global lock. Default is all.",
+ (uchar*) &opt_lock_wait_query_type,
+ (uchar*) &opt_lock_wait_query_type, &query_type_typelib,
+ GET_ENUM, REQUIRED_ARG, QUERY_TYPE_ALL, 0, 0, 0, 0, 0},
+
+ {"kill-long-query-type", OPT_KILL_LONG_QUERY_TYPE,
+ "This option specifies which types of queries should be killed to "
+ "unblock the global lock. Default is \"all\".",
+ (uchar*) &opt_kill_long_query_type,
+ (uchar*) &opt_kill_long_query_type, &query_type_typelib,
+ GET_ENUM, REQUIRED_ARG, QUERY_TYPE_SELECT, 0, 0, 0, 0, 0},
+
+ {"history", OPT_HISTORY,
+ "This option enables the tracking of backup history in the "
+ "PERCONA_SCHEMA.xtrabackup_history table. An optional history "
+ "series name may be specified that will be placed with the history "
+ "record for the current backup being taken.",
+ NULL, NULL, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
+
+ {"kill-long-queries-timeout", OPT_KILL_LONG_QUERIES_TIMEOUT,
+ "This option specifies the number of seconds innobackupex waits "
+ "between starting FLUSH TABLES WITH READ LOCK and killing those "
+ "queries that block it. Default is 0 seconds, which means "
+ "innobackupex will not attempt to kill any queries.",
+ (uchar*) &opt_kill_long_queries_timeout,
+ (uchar*) &opt_kill_long_queries_timeout, 0, GET_UINT,
+ REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+
+ {"ftwrl-wait-timeout", OPT_LOCK_WAIT_TIMEOUT,
+ "This option specifies time in seconds that innobackupex should wait "
+ "for queries that would block FTWRL before running it. If there are "
+ "still such queries when the timeout expires, innobackupex terminates "
+ "with an error. Default is 0, in which case innobackupex does not "
+ "wait for queries to complete and starts FTWRL immediately.",
+ (uchar*) &opt_lock_wait_timeout,
+ (uchar*) &opt_lock_wait_timeout, 0, GET_UINT,
+ REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+
+ {"ftwrl-wait-threshold", OPT_LOCK_WAIT_THRESHOLD,
+ "This option specifies the query run time threshold which is used by "
+ "innobackupex to detect long-running queries with a non-zero value "
+ "of --ftwrl-wait-timeout. FTWRL is not started until such "
+ "long-running queries exist. This option has no effect if "
+ "--ftwrl-wait-timeout is 0. Default value is 60 seconds.",
+ (uchar*) &opt_lock_wait_threshold,
+ (uchar*) &opt_lock_wait_threshold, 0, GET_UINT,
+ REQUIRED_ARG, 60, 0, 0, 0, 0, 0},
+
+ {"debug-sleep-before-unlock", OPT_DEBUG_SLEEP_BEFORE_UNLOCK,
+ "This is a debug-only option used by the XtraBackup test suite.",
+ (uchar*) &opt_debug_sleep_before_unlock,
+ (uchar*) &opt_debug_sleep_before_unlock, 0, GET_UINT,
+ REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+
+ {"safe-slave-backup-timeout", OPT_SAFE_SLAVE_BACKUP_TIMEOUT,
+ "How many seconds --safe-slave-backup should wait for "
+ "Slave_open_temp_tables to become zero. (default 300)",
+ (uchar*) &opt_safe_slave_backup_timeout,
+ (uchar*) &opt_safe_slave_backup_timeout, 0, GET_UINT,
+ REQUIRED_ARG, 300, 0, 0, 0, 0, 0},
+
+ {"binlog-info", OPT_BINLOG_INFO,
+ "This option controls how XtraBackup should retrieve server's binary log "
+ "coordinates corresponding to the backup. Possible values are OFF, ON, "
+ "LOCKLESS and AUTO. See the XtraBackup manual for more information",
+ &opt_binlog_info, &opt_binlog_info,
+ &binlog_info_typelib, GET_ENUM, OPT_ARG, BINLOG_INFO_AUTO, 0, 0, 0, 0, 0},
+
+ {"secure-auth", OPT_XB_SECURE_AUTH, "Refuse client connecting to server if it"
+ " uses old (pre-4.1.1) protocol.", &opt_secure_auth,
+ &opt_secure_auth, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
+
+#include "sslopt-longopts.h"
+
+
+ { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
+};
+
+uint xb_client_options_count = array_elements(xb_client_options);
+
+struct my_option xb_server_options[] =
+{
+ {"datadir", 'h', "Path to the database root.", (G_PTR*) &mysql_data_home,
+ (G_PTR*) &mysql_data_home, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"tmpdir", 't',
+ "Path for temporary files. Several paths may be specified, separated by a "
+#if defined(__WIN__) || defined(OS2) || defined(__NETWARE__)
+ "semicolon (;)"
+#else
+ "colon (:)"
+#endif
+ ", in this case they are used in a round-robin fashion.",
+ (G_PTR*) &opt_mysql_tmpdir,
+ (G_PTR*) &opt_mysql_tmpdir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"parallel", OPT_XTRA_PARALLEL,
+ "Number of threads to use for parallel datafiles transfer. "
+ "The default value is 1.",
+ (G_PTR*) &xtrabackup_parallel, (G_PTR*) &xtrabackup_parallel, 0, GET_INT,
+ REQUIRED_ARG, 1, 1, INT_MAX, 0, 0, 0},
+
+ {"log", OPT_LOG, "Ignored option for MySQL option compatibility",
+ (G_PTR*) &log_ignored_opt, (G_PTR*) &log_ignored_opt, 0,
+ GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
+
+ {"log_bin", OPT_LOG, "Base name for the log sequence",
+ &opt_log_bin, &opt_log_bin, 0, GET_STR_ALLOC, OPT_ARG, 0, 0, 0, 0, 0, 0},
+
+ {"innodb", OPT_INNODB, "Ignored option for MySQL option compatibility",
+ (G_PTR*) &innobase_ignored_opt, (G_PTR*) &innobase_ignored_opt, 0,
+ GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
+
+ {"innodb_adaptive_hash_index", OPT_INNODB_ADAPTIVE_HASH_INDEX,
+ "Enable InnoDB adaptive hash index (enabled by default). "
+ "Disable with --skip-innodb-adaptive-hash-index.",
+ (G_PTR*) &innobase_adaptive_hash_index,
+ (G_PTR*) &innobase_adaptive_hash_index,
+ 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
+ {"innodb_additional_mem_pool_size", OPT_INNODB_ADDITIONAL_MEM_POOL_SIZE,
+ "Size of a memory pool InnoDB uses to store data dictionary information and other internal data structures.",
+ (G_PTR*) &innobase_additional_mem_pool_size,
+ (G_PTR*) &innobase_additional_mem_pool_size, 0, GET_LONG, REQUIRED_ARG,
+ 1*1024*1024L, 512*1024L, LONG_MAX, 0, 1024, 0},
+ {"innodb_autoextend_increment", OPT_INNODB_AUTOEXTEND_INCREMENT,
+ "Data file autoextend increment in megabytes",
+ (G_PTR*) &srv_auto_extend_increment,
+ (G_PTR*) &srv_auto_extend_increment,
+ 0, GET_ULONG, REQUIRED_ARG, 8L, 1L, 1000L, 0, 1L, 0},
+ {"innodb_buffer_pool_size", OPT_INNODB_BUFFER_POOL_SIZE,
+ "The size of the memory buffer InnoDB uses to cache data and indexes of its tables.",
+ (G_PTR*) &innobase_buffer_pool_size, (G_PTR*) &innobase_buffer_pool_size, 0,
+ GET_LL, REQUIRED_ARG, 8*1024*1024L, 1024*1024L, LONGLONG_MAX, 0,
+ 1024*1024L, 0},
+ {"innodb_checksums", OPT_INNODB_CHECKSUMS, "Enable InnoDB checksums validation (enabled by default). \
+Disable with --skip-innodb-checksums.", (G_PTR*) &innobase_use_checksums,
+ (G_PTR*) &innobase_use_checksums, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
+ {"innodb_data_file_path", OPT_INNODB_DATA_FILE_PATH,
+ "Path to individual files and their sizes.", &innobase_data_file_path,
+ &innobase_data_file_path, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"innodb_data_home_dir", OPT_INNODB_DATA_HOME_DIR,
+ "The common part for InnoDB table spaces.", &innobase_data_home_dir,
+ &innobase_data_home_dir, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"innodb_doublewrite", OPT_INNODB_DOUBLEWRITE, "Enable InnoDB doublewrite buffer (enabled by default). \
+Disable with --skip-innodb-doublewrite.", (G_PTR*) &innobase_use_doublewrite,
+ (G_PTR*) &innobase_use_doublewrite, 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0},
+ {"innodb_io_capacity", OPT_INNODB_IO_CAPACITY,
+ "Number of IOPs the server can do. Tunes the background IO rate",
+ (G_PTR*) &srv_io_capacity, (G_PTR*) &srv_io_capacity,
+ 0, GET_ULONG, OPT_ARG, 200, 100, ~0UL, 0, 0, 0},
+ {"innodb_file_io_threads", OPT_INNODB_FILE_IO_THREADS,
+ "Number of file I/O threads in InnoDB.", (G_PTR*) &innobase_file_io_threads,
+ (G_PTR*) &innobase_file_io_threads, 0, GET_LONG, REQUIRED_ARG, 4, 4, 64, 0,
+ 1, 0},
+ {"innodb_read_io_threads", OPT_INNODB_READ_IO_THREADS,
+ "Number of background read I/O threads in InnoDB.", (G_PTR*) &innobase_read_io_threads,
+ (G_PTR*) &innobase_read_io_threads, 0, GET_LONG, REQUIRED_ARG, 4, 1, 64, 0,
+ 1, 0},
+ {"innodb_write_io_threads", OPT_INNODB_WRITE_IO_THREADS,
+ "Number of background write I/O threads in InnoDB.", (G_PTR*) &innobase_write_io_threads,
+ (G_PTR*) &innobase_write_io_threads, 0, GET_LONG, REQUIRED_ARG, 4, 1, 64, 0,
+ 1, 0},
+ {"innodb_file_per_table", OPT_INNODB_FILE_PER_TABLE,
+ "Stores each InnoDB table to an .ibd file in the database dir.",
+ (G_PTR*) &innobase_file_per_table,
+ (G_PTR*) &innobase_file_per_table, 0, GET_BOOL, NO_ARG,
+ FALSE, 0, 0, 0, 0, 0},
+
+ {"innodb_flush_method", OPT_INNODB_FLUSH_METHOD,
+ "With which method to flush data.", (G_PTR*) &innobase_unix_file_flush_method,
+ (G_PTR*) &innobase_unix_file_flush_method, 0, GET_STR, REQUIRED_ARG, 0, 0, 0,
+ 0, 0, 0},
+
+/* ####### Should we use this option? ####### */
+ {"innodb_force_recovery", OPT_INNODB_FORCE_RECOVERY,
+ "Helps to save your data in case the disk image of the database becomes corrupt.",
+ (G_PTR*) &innobase_force_recovery, (G_PTR*) &innobase_force_recovery, 0,
+ GET_LONG, REQUIRED_ARG, 0, 0, 6, 0, 1, 0},
+
+ {"innodb_log_arch_dir", OPT_INNODB_LOG_ARCH_DIR,
+ "Where full logs should be archived.", (G_PTR*) &innobase_log_arch_dir,
+ (G_PTR*) &innobase_log_arch_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"innodb_log_buffer_size", OPT_INNODB_LOG_BUFFER_SIZE,
+ "The size of the buffer which InnoDB uses to write log to the log files on disk.",
+ (G_PTR*) &innobase_log_buffer_size, (G_PTR*) &innobase_log_buffer_size, 0,
+ GET_LONG, REQUIRED_ARG, 1024*1024L, 256*1024L, LONG_MAX, 0, 1024, 0},
+ {"innodb_log_file_size", OPT_INNODB_LOG_FILE_SIZE,
+ "Size of each log file in a log group.",
+ (G_PTR*) &innobase_log_file_size, (G_PTR*) &innobase_log_file_size, 0,
+ GET_LL, REQUIRED_ARG, 48*1024*1024L, 1*1024*1024L, LONGLONG_MAX, 0,
+ 1024*1024L, 0},
+ {"innodb_log_files_in_group", OPT_INNODB_LOG_FILES_IN_GROUP,
+ "Number of log files in the log group. InnoDB writes to the files in a "
+ "circular fashion. Value 3 is recommended here.",
+ &innobase_log_files_in_group, &innobase_log_files_in_group,
+ 0, GET_LONG, REQUIRED_ARG, 2, 2, 100, 0, 1, 0},
+ {"innodb_log_group_home_dir", OPT_INNODB_LOG_GROUP_HOME_DIR,
+ "Path to InnoDB log files.", &srv_log_group_home_dir,
+ &srv_log_group_home_dir, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"innodb_max_dirty_pages_pct", OPT_INNODB_MAX_DIRTY_PAGES_PCT,
+ "Percentage of dirty pages allowed in bufferpool.", (G_PTR*) &srv_max_buf_pool_modified_pct,
+ (G_PTR*) &srv_max_buf_pool_modified_pct, 0, GET_ULONG, REQUIRED_ARG, 90, 0, 100, 0, 0, 0},
+ {"innodb_open_files", OPT_INNODB_OPEN_FILES,
+ "How many files at the maximum InnoDB keeps open at the same time.",
+ (G_PTR*) &innobase_open_files, (G_PTR*) &innobase_open_files, 0,
+ GET_LONG, REQUIRED_ARG, 300L, 10L, LONG_MAX, 0, 1L, 0},
+ {"innodb_use_native_aio", OPT_INNODB_USE_NATIVE_AIO,
+ "Use native AIO if supported on this platform.",
+ (G_PTR*) &srv_use_native_aio,
+ (G_PTR*) &srv_use_native_aio, 0, GET_BOOL, NO_ARG,
+ FALSE, 0, 0, 0, 0, 0},
+ {"innodb_page_size", OPT_INNODB_PAGE_SIZE,
+ "The universal page size of the database.",
+ (G_PTR*) &innobase_page_size, (G_PTR*) &innobase_page_size, 0,
+ /* Use GET_LL to support numeric suffixes in 5.6 */
+ GET_LL, REQUIRED_ARG,
+ (1LL << 14), (1LL << 12), (1LL << UNIV_PAGE_SIZE_SHIFT_MAX), 0, 1L, 0},
+ {"innodb_log_block_size", OPT_INNODB_LOG_BLOCK_SIZE,
+ "The log block size of the transaction log file. "
+ "Changing for created log file is not supported. Use on your own risk!",
+ (G_PTR*) &innobase_log_block_size, (G_PTR*) &innobase_log_block_size, 0,
+ GET_ULONG, REQUIRED_ARG, 512, 512, 1 << UNIV_PAGE_SIZE_SHIFT_MAX, 0, 1L, 0},
+ {"innodb_fast_checksum", OPT_INNODB_FAST_CHECKSUM,
+ "Change the algorithm of checksum for the whole of datapage to 4-bytes word based.",
+ (G_PTR*) &innobase_fast_checksum,
+ (G_PTR*) &innobase_fast_checksum, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
+ {"innodb_doublewrite_file", OPT_INNODB_DOUBLEWRITE_FILE,
+ "Path to special datafile for doublewrite buffer. (default is "": not used)",
+ (G_PTR*) &innobase_doublewrite_file, (G_PTR*) &innobase_doublewrite_file,
+ 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"innodb_buffer_pool_filename", OPT_INNODB_BUFFER_POOL_FILENAME,
+ "Filename to/from which to dump/load the InnoDB buffer pool",
+ (G_PTR*) &innobase_buffer_pool_filename,
+ (G_PTR*) &innobase_buffer_pool_filename,
+ 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+
+#ifndef __WIN__
+ {"debug-sync", OPT_XTRA_DEBUG_SYNC,
+ "Debug sync point. This is only used by the xtrabackup test suite",
+ (G_PTR*) &xtrabackup_debug_sync,
+ (G_PTR*) &xtrabackup_debug_sync,
+ 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+#endif
+
+ {"innodb_checksum_algorithm", OPT_INNODB_CHECKSUM_ALGORITHM,
+ "The algorithm InnoDB uses for page checksumming. [CRC32, STRICT_CRC32, "
+ "INNODB, STRICT_INNODB, NONE, STRICT_NONE]", &srv_checksum_algorithm,
+ &srv_checksum_algorithm, &innodb_checksum_algorithm_typelib, GET_ENUM,
+ REQUIRED_ARG, SRV_CHECKSUM_ALGORITHM_INNODB, 0, 0, 0, 0, 0},
+ {"innodb_log_checksum_algorithm", OPT_INNODB_LOG_CHECKSUM_ALGORITHM,
+ "The algorithm InnoDB uses for log checksumming. [CRC32, STRICT_CRC32, "
+ "INNODB, STRICT_INNODB, NONE, STRICT_NONE]", &srv_log_checksum_algorithm,
+ &srv_log_checksum_algorithm, &innodb_checksum_algorithm_typelib, GET_ENUM,
+ REQUIRED_ARG, SRV_CHECKSUM_ALGORITHM_INNODB, 0, 0, 0, 0, 0},
+ {"innodb_undo_directory", OPT_INNODB_UNDO_DIRECTORY,
+ "Directory where undo tablespace files live, this path can be absolute.",
+ &srv_undo_dir, &srv_undo_dir, 0, GET_STR_ALLOC, REQUIRED_ARG, 0, 0, 0, 0, 0,
+ 0},
+
+ {"innodb_undo_tablespaces", OPT_INNODB_UNDO_TABLESPACES,
+ "Number of undo tablespaces to use.",
+ (G_PTR*)&srv_undo_tablespaces, (G_PTR*)&srv_undo_tablespaces,
+ 0, GET_ULONG, REQUIRED_ARG, 0, 0, 126, 0, 1, 0},
+
+ {"defaults_group", OPT_DEFAULTS_GROUP, "defaults group in config file (default \"mysqld\").",
+ (G_PTR*) &defaults_group, (G_PTR*) &defaults_group,
+ 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+
+ {"plugin-dir", OPT_PLUGIN_DIR, "Server plugin directory",
+ &xb_plugin_dir, &xb_plugin_dir,
+ 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
+
+ { "plugin-load", OPT_PLUGIN_LOAD, "encrypton plugin to load",
+ &xb_plugin_load, &xb_plugin_load,
+ 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
+
+ { "innodb-encrypt-log", OPT_INNODB_ENCRYPT_LOG, "encrypton plugin to load",
+ &srv_encrypt_log, &srv_encrypt_log,
+ 0, GET_BOOL, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
+
+ {"open_files_limit", OPT_OPEN_FILES_LIMIT, "the maximum number of file "
+ "descriptors to reserve with setrlimit().",
+ (G_PTR*) &xb_open_files_limit, (G_PTR*) &xb_open_files_limit, 0, GET_ULONG,
+ REQUIRED_ARG, 0, 0, UINT_MAX, 0, 1, 0},
+
+ { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
+};
+
+uint xb_server_options_count = array_elements(xb_server_options);
+
+#ifndef __WIN__
+static int debug_sync_resumed;
+
+static void sigcont_handler(int sig);
+
+static void sigcont_handler(int sig __attribute__((unused)))
+{
+ debug_sync_resumed= 1;
+}
+#endif
+
+static inline
+void
+debug_sync_point(const char *name)
+{
+#ifndef __WIN__
+ FILE *fp;
+ pid_t pid;
+ char pid_path[FN_REFLEN];
+
+ if (xtrabackup_debug_sync == NULL) {
+ return;
+ }
+
+ if (strcmp(xtrabackup_debug_sync, name)) {
+ return;
+ }
+
+ pid = getpid();
+
+ snprintf(pid_path, sizeof(pid_path), "%s/xtrabackup_debug_sync",
+ xtrabackup_target_dir);
+ fp = fopen(pid_path, "w");
+ if (fp == NULL) {
+ msg("xtrabackup: Error: cannot open %s\n", pid_path);
+ exit(EXIT_FAILURE);
+ }
+ fprintf(fp, "%u\n", (uint) pid);
+ fclose(fp);
+
+ msg("xtrabackup: DEBUG: Suspending at debug sync point '%s'. "
+ "Resume with 'kill -SIGCONT %u'.\n", name, (uint) pid);
+
+ debug_sync_resumed= 0;
+ kill(pid, SIGSTOP);
+ while (!debug_sync_resumed) {
+ sleep(1);
+ }
+
+ /* On resume */
+ msg("xtrabackup: DEBUG: removing the pid file.\n");
+ my_delete(pid_path, MYF(MY_WME));
+#endif
+}
+
+static const char *xb_client_default_groups[]=
+ { "xtrabackup", "client", 0, 0, 0 };
+
+static const char *xb_server_default_groups[]=
+ { "xtrabackup", "mysqld", 0, 0, 0 };
+
+static void print_version(void)
+{
+ msg("%s based on MariaDB server %s %s (%s) \n",
+ my_progname, MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE);
+}
+
+static void usage(void)
+{
+ puts("Open source backup tool for InnoDB and XtraDB\n\
+\n\
+Copyright (C) 2009-2015 Percona LLC and/or its affiliates.\n\
+Portions Copyright (C) 2000, 2011, MySQL AB & Innobase Oy. All Rights Reserved.\n\
+\n\
+This program is free software; you can redistribute it and/or\n\
+modify it under the terms of the GNU General Public License\n\
+as published by the Free Software Foundation version 2\n\
+of the License.\n\
+\n\
+This program is distributed in the hope that it will be useful,\n\
+but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\
+GNU General Public License for more details.\n\
+\n\
+You can download full text of the license on http://www.gnu.org/licenses/gpl-2.0.txt\n");
+
+ printf("Usage: [%s [--defaults-file=#] --backup | %s [--defaults-file=#] --prepare] [OPTIONS]\n",my_progname,my_progname);
+ print_defaults("my", xb_server_default_groups);
+ my_print_help(xb_client_options);
+ my_print_help(xb_server_options);
+ my_print_variables(xb_server_options);
+ my_print_variables(xb_client_options);
+}
+
+#define ADD_PRINT_PARAM_OPT(value) \
+ { \
+ print_param_str << opt->name << "=" << value << "\n"; \
+ param_set.insert(opt->name); \
+ }
+
+/************************************************************************
+Check if parameter is set in defaults file or via command line argument
+@return true if parameter is set. */
+bool
+check_if_param_set(const char *param)
+{
+ return param_set.find(param) != param_set.end();
+}
+
+my_bool
+xb_get_one_option(int optid,
+ const struct my_option *opt __attribute__((unused)),
+ char *argument)
+{
+ switch(optid) {
+ case 'h':
+ strmake(mysql_real_data_home,argument, FN_REFLEN - 1);
+ mysql_data_home= mysql_real_data_home;
+
+ ADD_PRINT_PARAM_OPT(mysql_real_data_home);
+ break;
+
+ case 't':
+
+ ADD_PRINT_PARAM_OPT(opt_mysql_tmpdir);
+ break;
+
+ case OPT_INNODB_DATA_HOME_DIR:
+
+ ADD_PRINT_PARAM_OPT(innobase_data_home_dir);
+ break;
+
+ case OPT_INNODB_DATA_FILE_PATH:
+
+ ADD_PRINT_PARAM_OPT(innobase_data_file_path);
+ break;
+
+ case OPT_INNODB_LOG_GROUP_HOME_DIR:
+
+ ADD_PRINT_PARAM_OPT(srv_log_group_home_dir);
+ break;
+
+ case OPT_INNODB_LOG_FILES_IN_GROUP:
+
+ ADD_PRINT_PARAM_OPT(innobase_log_files_in_group);
+ break;
+
+ case OPT_INNODB_LOG_FILE_SIZE:
+
+ ADD_PRINT_PARAM_OPT(innobase_log_file_size);
+ break;
+
+ case OPT_INNODB_FLUSH_METHOD:
+
+ ADD_PRINT_PARAM_OPT(innobase_unix_file_flush_method);
+ break;
+
+ case OPT_INNODB_PAGE_SIZE:
+
+ ADD_PRINT_PARAM_OPT(innobase_page_size);
+ break;
+
+ case OPT_INNODB_FAST_CHECKSUM:
+
+ ADD_PRINT_PARAM_OPT(!!innobase_fast_checksum);
+ break;
+
+ case OPT_INNODB_LOG_BLOCK_SIZE:
+
+ ADD_PRINT_PARAM_OPT(innobase_log_block_size);
+ break;
+
+ case OPT_INNODB_DOUBLEWRITE_FILE:
+
+ ADD_PRINT_PARAM_OPT(innobase_doublewrite_file);
+ break;
+
+ case OPT_INNODB_UNDO_DIRECTORY:
+
+ ADD_PRINT_PARAM_OPT(srv_undo_dir);
+ break;
+
+ case OPT_INNODB_UNDO_TABLESPACES:
+
+ ADD_PRINT_PARAM_OPT(srv_undo_tablespaces);
+ break;
+
+ case OPT_INNODB_CHECKSUM_ALGORITHM:
+
+ ut_a(srv_checksum_algorithm <= SRV_CHECKSUM_ALGORITHM_STRICT_NONE);
+
+ ADD_PRINT_PARAM_OPT(innodb_checksum_algorithm_names[srv_checksum_algorithm]);
+ break;
+
+ case OPT_INNODB_LOG_CHECKSUM_ALGORITHM:
+
+ ut_a(srv_log_checksum_algorithm <= SRV_CHECKSUM_ALGORITHM_STRICT_NONE);
+
+ ADD_PRINT_PARAM_OPT(innodb_checksum_algorithm_names[srv_log_checksum_algorithm]);
+ break;
+
+ case OPT_INNODB_BUFFER_POOL_FILENAME:
+
+ ADD_PRINT_PARAM_OPT(innobase_buffer_pool_filename);
+ break;
+
+ case OPT_XTRA_TARGET_DIR:
+ strmake(xtrabackup_real_target_dir,argument, sizeof(xtrabackup_real_target_dir)-1);
+ xtrabackup_target_dir= xtrabackup_real_target_dir;
+ break;
+ case OPT_XTRA_STREAM:
+ if (!strcasecmp(argument, "tar"))
+ xtrabackup_stream_fmt = XB_STREAM_FMT_TAR;
+ else if (!strcasecmp(argument, "xbstream"))
+ xtrabackup_stream_fmt = XB_STREAM_FMT_XBSTREAM;
+ else
+ {
+ msg("Invalid --stream argument: %s\n", argument);
+ return 1;
+ }
+ xtrabackup_stream = TRUE;
+ break;
+ case OPT_XTRA_COMPRESS:
+ if (argument == NULL)
+ xtrabackup_compress_alg = "quicklz";
+ else if (strcasecmp(argument, "quicklz"))
+ {
+ msg("Invalid --compress argument: %s\n", argument);
+ return 1;
+ }
+ xtrabackup_compress = TRUE;
+ break;
+ case OPT_XTRA_ENCRYPT:
+ if (argument == NULL)
+ {
+ msg("Missing --encrypt argument, must specify a valid encryption "
+ " algorithm.\n");
+ return 1;
+ }
+ xtrabackup_encrypt = TRUE;
+ break;
+ case OPT_DECRYPT:
+ if (argument == NULL) {
+ msg("Missing --decrypt argument, must specify a "
+ "valid encryption algorithm.\n");
+ return(1);
+ }
+ opt_decrypt = TRUE;
+ xtrabackup_decrypt_decompress = true;
+ break;
+ case OPT_DECOMPRESS:
+ opt_decompress = TRUE;
+ xtrabackup_decrypt_decompress = true;
+ break;
+ case (int) OPT_CORE_FILE:
+ test_flags |= TEST_CORE_ON_SIGNAL;
+ break;
+ case OPT_HISTORY:
+ if (argument) {
+ opt_history = argument;
+ } else {
+ opt_history = "";
+ }
+ break;
+ case 'p':
+ if (argument)
+ {
+ char *start= argument;
+ my_free(opt_password);
+ opt_password= my_strdup(argument, MYF(MY_FAE));
+ while (*argument) *argument++= 'x'; // Destroy argument
+ if (*start)
+ start[1]=0 ;
+ }
+ break;
+
+
+#include "sslopt-case.h"
+
+ case '?':
+ usage();
+ exit(EXIT_SUCCESS);
+ break;
+ case 'v':
+ print_version();
+ exit(EXIT_SUCCESS);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+/***********************************************************************
+Initializes log_block_size */
+static
+ibool
+xb_init_log_block_size(void)
+{
+ srv_log_block_size = 0;
+ if (innobase_log_block_size != 512) {
+ uint n_shift = (uint)get_bit_shift(innobase_log_block_size);;
+
+ if (n_shift > 0) {
+ srv_log_block_size = (ulint)(1LL << n_shift);
+ msg("InnoDB: The log block size is set to %lu.\n",
+ srv_log_block_size);
+ }
+ } else {
+ srv_log_block_size = 512;
+ }
+ if (!srv_log_block_size) {
+ msg("InnoDB: Error: %lu is not valid value for "
+ "innodb_log_block_size.\n", innobase_log_block_size);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static my_bool
+innodb_init_param(void)
+{
+ /* innobase_init */
+ static char current_dir[3]; /* Set if using current lib */
+ my_bool ret;
+ char *default_path;
+ srv_is_being_started = TRUE;
+ /* === some variables from mysqld === */
+ memset((G_PTR) &mysql_tmpdir_list, 0, sizeof(mysql_tmpdir_list));
+
+ if (init_tmpdir(&mysql_tmpdir_list, opt_mysql_tmpdir))
+ exit(EXIT_FAILURE);
+ xtrabackup_tmpdir = my_tmpdir(&mysql_tmpdir_list);
+ /* dummy for initialize all_charsets[] */
+ get_charset_name(0);
+
+ srv_page_size = 0;
+ srv_page_size_shift = 0;
+
+ if (innobase_page_size != (1LL << 14)) {
+ int n_shift = (int)get_bit_shift((ulint) innobase_page_size);
+
+ if (n_shift >= 12 && n_shift <= UNIV_PAGE_SIZE_SHIFT_MAX) {
+ srv_page_size_shift = n_shift;
+ srv_page_size = 1 << n_shift;
+ msg("InnoDB: The universal page size of the "
+ "database is set to %lu.\n", srv_page_size);
+ } else {
+ msg("InnoDB: Error: invalid value of "
+ "innobase_page_size: %lld", innobase_page_size);
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ srv_page_size_shift = 14;
+ srv_page_size = (1 << srv_page_size_shift);
+ }
+
+ if (!xb_init_log_block_size()) {
+ goto error;
+ }
+
+ /* Check that values don't overflow on 32-bit systems. */
+ if (sizeof(ulint) == 4) {
+ if (xtrabackup_use_memory > UINT_MAX32) {
+ msg("xtrabackup: use-memory can't be over 4GB"
+ " on 32-bit systems\n");
+ }
+
+ if (innobase_buffer_pool_size > UINT_MAX32) {
+ msg("xtrabackup: innobase_buffer_pool_size can't be "
+ "over 4GB on 32-bit systems\n");
+
+ goto error;
+ }
+
+ if (innobase_log_file_size > UINT_MAX32) {
+ msg("xtrabackup: innobase_log_file_size can't be "
+ "over 4GB on 32-bit systemsi\n");
+
+ goto error;
+ }
+ }
+
+ os_innodb_umask = (ulint)0664;
+
+ /* First calculate the default path for innodb_data_home_dir etc.,
+ in case the user has not given any value.
+
+ Note that when using the embedded server, the datadirectory is not
+ necessarily the current directory of this program. */
+
+ /* It's better to use current lib, to keep paths short */
+ current_dir[0] = FN_CURLIB;
+ current_dir[1] = FN_LIBCHAR;
+ current_dir[2] = 0;
+ default_path = current_dir;
+
+ ut_a(default_path);
+
+ /* Set InnoDB initialization parameters according to the values
+ read from MySQL .cnf file */
+
+ if (xtrabackup_backup || xtrabackup_stats) {
+ msg("xtrabackup: using the following InnoDB configuration:\n");
+ } else {
+ msg("xtrabackup: using the following InnoDB configuration "
+ "for recovery:\n");
+ }
+
+ /*--------------- Data files -------------------------*/
+
+ /* The default dir for data files is the datadir of MySQL */
+
+ srv_data_home = ((xtrabackup_backup || xtrabackup_stats) && innobase_data_home_dir
+ ? innobase_data_home_dir : default_path);
+ msg("xtrabackup: innodb_data_home_dir = %s\n", srv_data_home);
+
+ /* Set default InnoDB data file size to 10 MB and let it be
+ auto-extending. Thus users can use InnoDB in >= 4.0 without having
+ to specify any startup options. */
+
+ if (!innobase_data_file_path) {
+ innobase_data_file_path = (char*) "ibdata1:10M:autoextend";
+ }
+ msg("xtrabackup: innodb_data_file_path = %s\n",
+ innobase_data_file_path);
+
+ /* Since InnoDB edits the argument in the next call, we make another
+ copy of it: */
+
+ internal_innobase_data_file_path = strdup(innobase_data_file_path);
+
+ ret = (my_bool) srv_parse_data_file_paths_and_sizes(
+ internal_innobase_data_file_path);
+ if (ret == FALSE) {
+ msg("xtrabackup: syntax error in innodb_data_file_path\n");
+mem_free_and_error:
+ free(internal_innobase_data_file_path);
+ internal_innobase_data_file_path = NULL;
+ goto error;
+ }
+
+ if (xtrabackup_prepare) {
+ /* "--prepare" needs filenames only */
+ ulint i;
+
+ for (i=0; i < srv_n_data_files; i++) {
+ char *p;
+
+ p = srv_data_file_names[i];
+ while ((p = strchr(p, SRV_PATH_SEPARATOR)) != NULL)
+ {
+ p++;
+ srv_data_file_names[i] = p;
+ }
+ }
+ }
+
+ /* -------------- Log files ---------------------------*/
+
+ /* The default dir for log files is the datadir of MySQL */
+
+ if (!((xtrabackup_backup || xtrabackup_stats) &&
+ srv_log_group_home_dir)) {
+ srv_log_group_home_dir = default_path;
+ }
+ if (xtrabackup_prepare && xtrabackup_incremental_dir) {
+ srv_log_group_home_dir = xtrabackup_incremental_dir;
+ }
+ msg("xtrabackup: innodb_log_group_home_dir = %s\n",
+ srv_log_group_home_dir);
+
+ srv_normalize_path_for_win(srv_log_group_home_dir);
+
+ if (strchr(srv_log_group_home_dir, ';')) {
+
+ msg("syntax error in innodb_log_group_home_dir, ");
+
+ goto mem_free_and_error;
+ }
+
+ srv_adaptive_flushing = FALSE;
+ srv_use_sys_malloc = TRUE;
+ srv_file_format = 1; /* Barracuda */
+ srv_max_file_format_at_startup = UNIV_FORMAT_MIN; /* on */
+ /* --------------------------------------------------*/
+
+ srv_file_flush_method_str = innobase_unix_file_flush_method;
+
+ srv_n_log_files = (ulint) innobase_log_files_in_group;
+ srv_log_file_size = (ulint) innobase_log_file_size;
+ msg("xtrabackup: innodb_log_files_in_group = %ld\n",
+ srv_n_log_files);
+ msg("xtrabackup: innodb_log_file_size = %lld\n",
+ (long long int) srv_log_file_size);
+
+ srv_log_archive_on = (ulint) innobase_log_archive;
+ srv_log_buffer_size = (ulint) innobase_log_buffer_size;
+
+ /* We set srv_pool_size here in units of 1 kB. InnoDB internally
+ changes the value so that it becomes the number of database pages. */
+
+ //srv_buf_pool_size = (ulint) innobase_buffer_pool_size;
+ srv_buf_pool_size = (ulint) xtrabackup_use_memory;
+
+ srv_mem_pool_size = (ulint) innobase_additional_mem_pool_size;
+
+ srv_n_file_io_threads = (ulint) innobase_file_io_threads;
+ srv_n_read_io_threads = (ulint) innobase_read_io_threads;
+ srv_n_write_io_threads = (ulint) innobase_write_io_threads;
+
+ srv_force_recovery = (ulint) innobase_force_recovery;
+
+ srv_use_doublewrite_buf = (ibool) innobase_use_doublewrite;
+
+ if (!innobase_use_checksums) {
+
+ srv_checksum_algorithm = SRV_CHECKSUM_ALGORITHM_NONE;
+ }
+
+ btr_search_enabled = (char) innobase_adaptive_hash_index;
+ btr_search_index_num = 1;
+
+ os_use_large_pages = (ibool) innobase_use_large_pages;
+ os_large_page_size = (ulint) innobase_large_page_size;
+
+ if (!innobase_log_arch_dir) {
+ static char default_dir[3] = "./";
+ srv_arch_dir = default_dir;
+ }
+ row_rollback_on_timeout = (ibool) innobase_rollback_on_timeout;
+
+ srv_file_per_table = (my_bool) innobase_file_per_table;
+
+ srv_locks_unsafe_for_binlog = (ibool) innobase_locks_unsafe_for_binlog;
+
+ srv_max_n_open_files = (ulint) innobase_open_files;
+ srv_innodb_status = (ibool) innobase_create_status_file;
+
+ srv_print_verbose_log = 1;
+
+ /* Store the default charset-collation number of this MySQL
+ installation */
+
+ /* We cannot treat characterset here for now!! */
+ data_mysql_default_charset_coll = (ulint)default_charset_info->number;
+
+ ut_a(DATA_MYSQL_LATIN1_SWEDISH_CHARSET_COLL ==
+ my_charset_latin1.number);
+ ut_a(DATA_MYSQL_BINARY_CHARSET_COLL == my_charset_bin.number);
+
+ /* Store the latin1_swedish_ci character ordering table to InnoDB. For
+ non-latin1_swedish_ci charsets we use the MySQL comparison functions,
+ and consequently we do not need to know the ordering internally in
+ InnoDB. */
+
+ ut_a(0 == strcmp(my_charset_latin1.name, "latin1_swedish_ci"));
+ srv_latin1_ordering = my_charset_latin1.sort_order;
+
+ //innobase_commit_concurrency_init_default();
+
+ /* Since we in this module access directly the fields of a trx
+ struct, and due to different headers and flags it might happen that
+ mutex_t has a different size in this module and in InnoDB
+ modules, we check at run time that the size is the same in
+ these compilation modules. */
+
+ /* On 5.5+ srv_use_native_aio is TRUE by default. It is later reset
+ if it is not supported by the platform in
+ innobase_start_or_create_for_mysql(). As we don't call it in xtrabackup,
+ we have to duplicate checks from that function here. */
+
+#ifdef __WIN__
+ switch (os_get_os_version()) {
+ case OS_WIN95:
+ case OS_WIN31:
+ case OS_WINNT:
+ /* On Win 95, 98, ME, Win32 subsystem for Windows 3.1,
+ and NT use simulated aio. In NT Windows provides async i/o,
+ but when run in conjunction with InnoDB Hot Backup, it seemed
+ to corrupt the data files. */
+
+ srv_use_native_aio = FALSE;
+ break;
+
+ case OS_WIN2000:
+ case OS_WINXP:
+ /* On 2000 and XP, async IO is available. */
+ srv_use_native_aio = TRUE;
+ break;
+
+ default:
+ /* Vista and later have both async IO and condition variables */
+ srv_use_native_aio = TRUE;
+ srv_use_native_conditions = TRUE;
+ break;
+ }
+
+#elif defined(LINUX_NATIVE_AIO)
+
+ if (srv_use_native_aio) {
+ ut_print_timestamp(stderr);
+ msg(" InnoDB: Using Linux native AIO\n");
+ }
+#else
+ /* Currently native AIO is supported only on windows and linux
+ and that also when the support is compiled in. In all other
+ cases, we ignore the setting of innodb_use_native_aio. */
+ srv_use_native_aio = FALSE;
+
+#endif
+
+ /* Assign the default value to srv_undo_dir if it's not specified, as
+ my_getopt does not support default values for string options. We also
+ ignore the option and override innodb_undo_directory on --prepare,
+ because separate undo tablespaces are copied to the root backup
+ directory. */
+
+ if (!srv_undo_dir || !xtrabackup_backup) {
+ my_free(srv_undo_dir);
+ srv_undo_dir = my_strdup(".", MYF(MY_FAE));
+ }
+
+ innodb_log_checksum_func_update(srv_log_checksum_algorithm);
+
+ return(FALSE);
+
+error:
+ msg("xtrabackup: innodb_init_param(): Error occured.\n");
+ return(TRUE);
+}
+
+static my_bool
+innodb_init(void)
+{
+ int err;
+ srv_is_being_started = TRUE;
+ err = innobase_start_or_create_for_mysql();
+
+ if (err != DB_SUCCESS) {
+ free(internal_innobase_data_file_path);
+ internal_innobase_data_file_path = NULL;
+ goto error;
+ }
+
+ /* They may not be needed for now */
+// (void) hash_init(&innobase_open_tables,system_charset_info, 32, 0, 0,
+// (hash_get_key) innobase_get_key, 0, 0);
+// pthread_mutex_init(&innobase_share_mutex, MY_MUTEX_INIT_FAST);
+// pthread_mutex_init(&prepare_commit_mutex, MY_MUTEX_INIT_FAST);
+// pthread_mutex_init(&commit_threads_m, MY_MUTEX_INIT_FAST);
+// pthread_mutex_init(&commit_cond_m, MY_MUTEX_INIT_FAST);
+// pthread_cond_init(&commit_cond, NULL);
+
+ innodb_inited= 1;
+
+ return(FALSE);
+
+error:
+ msg("xtrabackup: innodb_init(): Error occured.\n");
+ return(TRUE);
+}
+
+static my_bool
+innodb_end(void)
+{
+ srv_fast_shutdown = (ulint) innobase_fast_shutdown;
+ innodb_inited = 0;
+
+ msg("xtrabackup: starting shutdown with innodb_fast_shutdown = %lu\n",
+ srv_fast_shutdown);
+
+ if (innobase_shutdown_for_mysql() != DB_SUCCESS) {
+ goto error;
+ }
+ free(internal_innobase_data_file_path);
+ internal_innobase_data_file_path = NULL;
+
+ /* They may not be needed for now */
+// hash_free(&innobase_open_tables);
+// pthread_mutex_destroy(&innobase_share_mutex);
+// pthread_mutex_destroy(&prepare_commit_mutex);
+// pthread_mutex_destroy(&commit_threads_m);
+// pthread_mutex_destroy(&commit_cond_m);
+// pthread_cond_destroy(&commit_cond);
+
+ return(FALSE);
+
+error:
+ msg("xtrabackup: innodb_end(): Error occured.\n");
+ return(TRUE);
+}
+
+/* ================= common ================= */
+
+/***********************************************************************
+Read backup meta info.
+@return TRUE on success, FALSE on failure. */
+static
+my_bool
+xtrabackup_read_metadata(char *filename)
+{
+ FILE *fp;
+ my_bool r = TRUE;
+ int t;
+
+ fp = fopen(filename,"r");
+ if(!fp) {
+ msg("xtrabackup: Error: cannot open %s\n", filename);
+ return(FALSE);
+ }
+
+ if (fscanf(fp, "backup_type = %29s\n", metadata_type)
+ != 1) {
+ r = FALSE;
+ goto end;
+ }
+ /* Use UINT64PF instead of LSN_PF here, as we have to maintain the file
+ format. */
+ if (fscanf(fp, "from_lsn = " UINT64PF "\n", &metadata_from_lsn)
+ != 1) {
+ r = FALSE;
+ goto end;
+ }
+ if (fscanf(fp, "to_lsn = " UINT64PF "\n", &metadata_to_lsn)
+ != 1) {
+ r = FALSE;
+ goto end;
+ }
+ if (fscanf(fp, "last_lsn = " UINT64PF "\n", &metadata_last_lsn)
+ != 1) {
+ metadata_last_lsn = 0;
+ }
+ /* Optional fields */
+
+ if (fscanf(fp, "recover_binlog_info = %d\n", &t) == 1) {
+ recover_binlog_info = (t == 1);
+ }
+end:
+ fclose(fp);
+
+ return(r);
+}
+
+/***********************************************************************
+Print backup meta info to a specified buffer. */
+static
+void
+xtrabackup_print_metadata(char *buf, size_t buf_len)
+{
+ /* Use UINT64PF instead of LSN_PF here, as we have to maintain the file
+ format. */
+ snprintf(buf, buf_len,
+ "backup_type = %s\n"
+ "from_lsn = " UINT64PF "\n"
+ "to_lsn = " UINT64PF "\n"
+ "last_lsn = " UINT64PF "\n"
+ "compact = %d\n"
+ "recover_binlog_info = %d\n",
+ metadata_type,
+ metadata_from_lsn,
+ metadata_to_lsn,
+ metadata_last_lsn,
+ MY_TEST(false),
+ MY_TEST(opt_binlog_info == BINLOG_INFO_LOCKLESS));
+}
+
+/***********************************************************************
+Stream backup meta info to a specified datasink.
+@return TRUE on success, FALSE on failure. */
+static
+my_bool
+xtrabackup_stream_metadata(ds_ctxt_t *ds_ctxt)
+{
+ char buf[1024];
+ size_t len;
+ ds_file_t *stream;
+ MY_STAT mystat;
+ my_bool rc = TRUE;
+
+ xtrabackup_print_metadata(buf, sizeof(buf));
+
+ len = strlen(buf);
+
+ mystat.st_size = len;
+ mystat.st_mtime = my_time(0);
+
+ stream = ds_open(ds_ctxt, XTRABACKUP_METADATA_FILENAME, &mystat);
+ if (stream == NULL) {
+ msg("xtrabackup: Error: cannot open output stream "
+ "for %s\n", XTRABACKUP_METADATA_FILENAME);
+ return(FALSE);
+ }
+
+ if (ds_write(stream, buf, len)) {
+ rc = FALSE;
+ }
+
+ if (ds_close(stream)) {
+ rc = FALSE;
+ }
+
+ return(rc);
+}
+
+/***********************************************************************
+Write backup meta info to a specified file.
+@return TRUE on success, FALSE on failure. */
+static
+my_bool
+xtrabackup_write_metadata(const char *filepath)
+{
+ char buf[1024];
+ size_t len;
+ FILE *fp;
+
+ xtrabackup_print_metadata(buf, sizeof(buf));
+
+ len = strlen(buf);
+
+ fp = fopen(filepath, "w");
+ if(!fp) {
+ msg("xtrabackup: Error: cannot open %s\n", filepath);
+ return(FALSE);
+ }
+ if (fwrite(buf, len, 1, fp) < 1) {
+ fclose(fp);
+ return(FALSE);
+ }
+
+ fclose(fp);
+
+ return(TRUE);
+}
+
+/***********************************************************************
+Read meta info for an incremental delta.
+@return TRUE on success, FALSE on failure. */
+static my_bool
+xb_read_delta_metadata(const char *filepath, xb_delta_info_t *info)
+{
+ FILE* fp;
+ char key[51];
+ char value[51];
+ my_bool r = TRUE;
+
+ /* set defaults */
+ info->page_size = ULINT_UNDEFINED;
+ info->zip_size = ULINT_UNDEFINED;
+ info->space_id = ULINT_UNDEFINED;
+
+ fp = fopen(filepath, "r");
+ if (!fp) {
+ /* Meta files for incremental deltas are optional */
+ return(TRUE);
+ }
+
+ while (!feof(fp)) {
+ if (fscanf(fp, "%50s = %50s\n", key, value) == 2) {
+ if (strcmp(key, "page_size") == 0) {
+ info->page_size = strtoul(value, NULL, 10);
+ } else if (strcmp(key, "zip_size") == 0) {
+ info->zip_size = strtoul(value, NULL, 10);
+ } else if (strcmp(key, "space_id") == 0) {
+ info->space_id = strtoul(value, NULL, 10);
+ }
+ }
+ }
+
+ fclose(fp);
+
+ if (info->page_size == ULINT_UNDEFINED) {
+ msg("xtrabackup: page_size is required in %s\n", filepath);
+ r = FALSE;
+ }
+ if (info->space_id == ULINT_UNDEFINED) {
+ msg("xtrabackup: Warning: This backup was taken with XtraBackup 2.0.1 "
+ "or earlier, some DDL operations between full and incremental "
+ "backups may be handled incorrectly\n");
+ }
+
+ return(r);
+}
+
+/***********************************************************************
+Write meta info for an incremental delta.
+@return TRUE on success, FALSE on failure. */
+my_bool
+xb_write_delta_metadata(const char *filename, const xb_delta_info_t *info)
+{
+ ds_file_t *f;
+ char buf[64];
+ my_bool ret;
+ size_t len;
+ MY_STAT mystat;
+
+ snprintf(buf, sizeof(buf),
+ "page_size = %lu\n"
+ "zip_size = %lu\n"
+ "space_id = %lu\n",
+ info->page_size, info->zip_size, info->space_id);
+ len = strlen(buf);
+
+ mystat.st_size = len;
+ mystat.st_mtime = my_time(0);
+
+ f = ds_open(ds_meta, filename, &mystat);
+ if (f == NULL) {
+ msg("xtrabackup: Error: cannot open output stream for %s\n",
+ filename);
+ return(FALSE);
+ }
+
+ ret = (ds_write(f, buf, len) == 0);
+
+ if (ds_close(f)) {
+ ret = FALSE;
+ }
+
+ return(ret);
+}
+
+/* ================= backup ================= */
+void
+xtrabackup_io_throttling(void)
+{
+ if (xtrabackup_throttle && (io_ticket--) < 0) {
+ os_event_reset(wait_throttle);
+ os_event_wait(wait_throttle);
+ }
+}
+
+static
+my_bool regex_list_check_match(
+ const regex_list_t& list,
+ const char* name)
+{
+ regmatch_t tables_regmatch[1];
+ for (regex_list_t::const_iterator i = list.begin(), end = list.end();
+ i != end; ++i) {
+ const regex_t& regex = *i;
+ int regres = regexec(&regex, name, 1, tables_regmatch, 0);
+
+ if (regres != REG_NOMATCH) {
+ return(TRUE);
+ }
+ }
+ return(FALSE);
+}
+
+static
+my_bool
+find_filter_in_hashtable(
+ const char* name,
+ hash_table_t* table,
+ xb_filter_entry_t** result
+)
+{
+ xb_filter_entry_t* found = NULL;
+ HASH_SEARCH(name_hash, table, ut_fold_string(name),
+ xb_filter_entry_t*,
+ found, (void) 0,
+ !strcmp(found->name, name));
+
+ if (found && result) {
+ *result = found;
+ }
+ return (found != NULL);
+}
+
+/************************************************************************
+Checks if a given table name matches any of specifications given in
+regex_list or tables_hash.
+
+@return TRUE on match or both regex_list and tables_hash are empty.*/
+static my_bool
+check_if_table_matches_filters(const char *name,
+ const regex_list_t& regex_list,
+ hash_table_t* tables_hash)
+{
+ if (regex_list.empty() && !tables_hash) {
+ return(FALSE);
+ }
+
+ if (regex_list_check_match(regex_list, name)) {
+ return(TRUE);
+ }
+
+ if (tables_hash && find_filter_in_hashtable(name, tables_hash, NULL)) {
+ return(TRUE);
+ }
+
+ return FALSE;
+}
+
+enum skip_database_check_result {
+ DATABASE_SKIP,
+ DATABASE_SKIP_SOME_TABLES,
+ DATABASE_DONT_SKIP,
+ DATABASE_DONT_SKIP_UNLESS_EXPLICITLY_EXCLUDED,
+};
+
+/************************************************************************
+Checks if a database specified by name should be skipped from backup based on
+the --databases, --databases_file or --databases_exclude options.
+
+@return TRUE if entire database should be skipped,
+ FALSE otherwise.
+*/
+static
+skip_database_check_result
+check_if_skip_database(
+ const char* name /*!< in: path to the database */
+)
+{
+ /* There are some filters for databases, check them */
+ xb_filter_entry_t* database = NULL;
+
+ if (databases_exclude_hash &&
+ find_filter_in_hashtable(name, databases_exclude_hash,
+ &database) &&
+ !database->has_tables) {
+ /* Database is found and there are no tables specified,
+ skip entire db. */
+ return DATABASE_SKIP;
+ }
+
+ if (databases_include_hash) {
+ if (!find_filter_in_hashtable(name, databases_include_hash,
+ &database)) {
+ /* Database isn't found, skip the database */
+ return DATABASE_SKIP;
+ } else if (database->has_tables) {
+ return DATABASE_SKIP_SOME_TABLES;
+ } else {
+ return DATABASE_DONT_SKIP_UNLESS_EXPLICITLY_EXCLUDED;
+ }
+ }
+
+ return DATABASE_DONT_SKIP;
+}
+
+/************************************************************************
+Checks if a database specified by path should be skipped from backup based on
+the --databases, --databases_file or --databases_exclude options.
+
+@return TRUE if the table should be skipped. */
+my_bool
+check_if_skip_database_by_path(
+ const char* path /*!< in: path to the db directory. */
+)
+{
+ if (databases_include_hash == NULL &&
+ databases_exclude_hash == NULL) {
+ return(FALSE);
+ }
+
+ const char* db_name = strrchr(path, SRV_PATH_SEPARATOR);
+ if (db_name == NULL) {
+ db_name = path;
+ } else {
+ ++db_name;
+ }
+
+ return check_if_skip_database(db_name) == DATABASE_SKIP;
+}
+
+/************************************************************************
+Checks if a table specified as a name in the form "database/name" (InnoDB 5.6)
+or "./database/name.ibd" (InnoDB 5.5-) should be skipped from backup based on
+the --tables or --tables-file options.
+
+@return TRUE if the table should be skipped. */
+my_bool
+check_if_skip_table(
+/******************/
+ const char* name) /*!< in: path to the table */
+{
+ char buf[FN_REFLEN];
+ const char *dbname, *tbname;
+ const char *ptr;
+ char *eptr;
+
+ if (regex_exclude_list.empty() &&
+ regex_include_list.empty() &&
+ tables_include_hash == NULL &&
+ tables_exclude_hash == NULL &&
+ databases_include_hash == NULL &&
+ databases_exclude_hash == NULL) {
+ return(FALSE);
+ }
+
+ dbname = NULL;
+ tbname = name;
+ while ((ptr = strchr(tbname, '/')) != NULL) {
+ dbname = tbname;
+ tbname = ptr + 1;
+ }
+
+ if (dbname == NULL) {
+ return(FALSE);
+ }
+
+ strncpy(buf, dbname, FN_REFLEN);
+ buf[tbname - 1 - dbname] = 0;
+
+ const skip_database_check_result skip_database =
+ check_if_skip_database(buf);
+ if (skip_database == DATABASE_SKIP) {
+ return (TRUE);
+ }
+
+ buf[FN_REFLEN - 1] = '\0';
+ buf[tbname - 1 - dbname] = '.';
+
+ /* Check if there's a suffix in the table name. If so, truncate it. We
+ rely on the fact that a dot cannot be a part of a table name (it is
+ encoded by the server with the @NNNN syntax). */
+ if ((eptr = strchr(&buf[tbname - dbname], '.')) != NULL) {
+
+ *eptr = '\0';
+ }
+
+ /* For partitioned tables first try to match against the regexp
+ without truncating the #P#... suffix so we can backup individual
+ partitions with regexps like '^test[.]t#P#p5' */
+ if (check_if_table_matches_filters(buf, regex_exclude_list,
+ tables_exclude_hash)) {
+ return(TRUE);
+ }
+ if (check_if_table_matches_filters(buf, regex_include_list,
+ tables_include_hash)) {
+ return(FALSE);
+ }
+ if ((eptr = strstr(buf, "#P#")) != NULL) {
+ *eptr = 0;
+
+ if (check_if_table_matches_filters(buf, regex_exclude_list,
+ tables_exclude_hash)) {
+ return (TRUE);
+ }
+ if (check_if_table_matches_filters(buf, regex_include_list,
+ tables_include_hash)) {
+ return(FALSE);
+ }
+ }
+
+ if (skip_database == DATABASE_DONT_SKIP_UNLESS_EXPLICITLY_EXCLUDED) {
+ /* Database is in include-list, and qualified name wasn't
+ found in any of exclusion filters.*/
+ return (FALSE);
+ }
+
+ if (skip_database == DATABASE_SKIP_SOME_TABLES ||
+ !regex_include_list.empty() ||
+ tables_include_hash) {
+
+ /* Include lists are present, but qualified name
+ failed to match any.*/
+ return(TRUE);
+ }
+
+ return(FALSE);
+}
+
+/***********************************************************************
+Reads the space flags from a given data file and returns the compressed
+page size, or 0 if the space is not compressed. */
+ulint
+xb_get_zip_size(os_file_t file)
+{
+ byte *buf;
+ byte *page;
+ ulint zip_size = ULINT_UNDEFINED;
+ ibool success;
+ ulint space;
+
+ buf = static_cast<byte *>(ut_malloc(2 * UNIV_PAGE_SIZE));
+ page = static_cast<byte *>(ut_align(buf, UNIV_PAGE_SIZE));
+
+ success = os_file_read(file, page, 0, UNIV_PAGE_SIZE);
+ if (!success) {
+ goto end;
+ }
+
+ space = mach_read_from_4(page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID);
+ zip_size = (space == 0 ) ? 0 :
+ dict_tf_get_zip_size(fsp_header_get_flags(page));
+end:
+ ut_free(buf);
+
+ return(zip_size);
+}
+
+const char*
+xb_get_copy_action(const char *dflt)
+{
+ const char *action;
+
+ if (xtrabackup_stream) {
+ if (xtrabackup_compress) {
+ if (xtrabackup_encrypt) {
+ action = "Compressing, encrypting and streaming";
+ } else {
+ action = "Compressing and streaming";
+ }
+ } else if (xtrabackup_encrypt) {
+ action = "Encrypting and streaming";
+ } else {
+ action = "Streaming";
+ }
+ } else {
+ if (xtrabackup_compress) {
+ if (xtrabackup_encrypt) {
+ action = "Compressing and encrypting";
+ } else {
+ action = "Compressing";
+ }
+ } else if (xtrabackup_encrypt) {
+ action = "Encrypting";
+ } else {
+ action = dflt;
+ }
+ }
+
+ return(action);
+}
+
+/* TODO: We may tune the behavior (e.g. by fil_aio)*/
+
+static
+my_bool
+xtrabackup_copy_datafile(fil_node_t* node, uint thread_n)
+{
+ char dst_name[FN_REFLEN];
+ ds_file_t *dstfile = NULL;
+ xb_fil_cur_t cursor;
+ xb_fil_cur_result_t res;
+ xb_write_filt_t *write_filter = NULL;
+ xb_write_filt_ctxt_t write_filt_ctxt;
+ const char *action;
+ xb_read_filt_t *read_filter;
+ ibool is_system;
+ my_bool rc = FALSE;
+
+ /* Get the name and the path for the tablespace. node->name always
+ contains the path (which may be absolute for remote tablespaces in
+ 5.6+). space->name contains the tablespace name in the form
+ "./database/table.ibd" (in 5.5-) or "database/table" (in 5.6+). For a
+ multi-node shared tablespace, space->name contains the name of the first
+ node, but that's irrelevant, since we only need node_name to match them
+ against filters, and the shared tablespace is always copied regardless
+ of the filters value. */
+
+ const char* const node_name = node->space->name;
+ const char* const node_path = node->name;
+
+ is_system = !fil_is_user_tablespace_id(node->space->id);
+
+ if (!is_system && check_if_skip_table(node_name)) {
+ msg("[%02u] Skipping %s.\n", thread_n, node_name);
+ return(FALSE);
+ }
+
+ if (!changed_page_bitmap) {
+ read_filter = &rf_pass_through;
+ }
+ else {
+ read_filter = &rf_bitmap;
+ }
+ res = xb_fil_cur_open(&cursor, read_filter, node, thread_n);
+ if (res == XB_FIL_CUR_SKIP) {
+ goto skip;
+ } else if (res == XB_FIL_CUR_ERROR) {
+ goto error;
+ }
+
+ strncpy(dst_name, cursor.rel_path, sizeof(dst_name));
+
+ /* Setup the page write filter */
+ if (xtrabackup_incremental) {
+ write_filter = &wf_incremental;
+ } else {
+ write_filter = &wf_write_through;
+ }
+
+ memset(&write_filt_ctxt, 0, sizeof(xb_write_filt_ctxt_t));
+ ut_a(write_filter->process != NULL);
+
+ if (write_filter->init != NULL &&
+ !write_filter->init(&write_filt_ctxt, dst_name, &cursor)) {
+ msg("[%02u] xtrabackup: error: "
+ "failed to initialize page write filter.\n", thread_n);
+ goto error;
+ }
+
+ dstfile = ds_open(ds_data, dst_name, &cursor.statinfo);
+ if (dstfile == NULL) {
+ msg("[%02u] xtrabackup: error: "
+ "cannot open the destination stream for %s\n",
+ thread_n, dst_name);
+ goto error;
+ }
+
+ action = xb_get_copy_action();
+
+ if (xtrabackup_stream) {
+ msg_ts("[%02u] %s %s\n", thread_n, action, node_path);
+ } else {
+ msg_ts("[%02u] %s %s to %s\n", thread_n, action,
+ node_path, dstfile->path);
+ }
+
+ /* The main copy loop */
+ while ((res = xb_fil_cur_read(&cursor)) == XB_FIL_CUR_SUCCESS) {
+ if (!write_filter->process(&write_filt_ctxt, dstfile)) {
+ goto error;
+ }
+ }
+
+ if (res == XB_FIL_CUR_ERROR) {
+ goto error;
+ }
+
+ if (write_filter->finalize
+ && !write_filter->finalize(&write_filt_ctxt, dstfile)) {
+ goto error;
+ }
+
+ /* close */
+ msg_ts("[%02u] ...done\n", thread_n);
+ xb_fil_cur_close(&cursor);
+ if (ds_close(dstfile)) {
+ rc = TRUE;
+ }
+ if (write_filter && write_filter->deinit) {
+ write_filter->deinit(&write_filt_ctxt);
+ }
+ return(rc);
+
+error:
+ xb_fil_cur_close(&cursor);
+ if (dstfile != NULL) {
+ ds_close(dstfile);
+ }
+ if (write_filter && write_filter->deinit) {
+ write_filter->deinit(&write_filt_ctxt);;
+ }
+ msg("[%02u] xtrabackup: Error: "
+ "xtrabackup_copy_datafile() failed.\n", thread_n);
+ return(TRUE); /*ERROR*/
+
+skip:
+
+ if (dstfile != NULL) {
+ ds_close(dstfile);
+ }
+ if (write_filter && write_filter->deinit) {
+ write_filter->deinit(&write_filt_ctxt);
+ }
+ msg("[%02u] xtrabackup: Warning: We assume the "
+ "table was dropped during xtrabackup execution "
+ "and ignore the file.\n", thread_n);
+ msg("[%02u] xtrabackup: Warning: skipping tablespace %s.\n",
+ thread_n, node_name);
+ return(FALSE);
+}
+
+static
+void
+xtrabackup_choose_lsn_offset(lsn_t start_lsn)
+{
+#if SUPPORT_PERCONA_5_5
+ ulint no, alt_no, expected_no;
+ ulint blocks_in_group;
+ lsn_t tmp_offset, end_lsn;
+ int lsn_chosen = 0;
+ log_group_t *group;
+
+ start_lsn = ut_uint64_align_down(start_lsn, OS_FILE_LOG_BLOCK_SIZE);
+ end_lsn = start_lsn + RECV_SCAN_SIZE;
+
+ group = UT_LIST_GET_FIRST(log_sys->log_groups);
+
+ if (mysql_server_version < 50500 || mysql_server_version > 50600) {
+ /* only make sense for Percona Server 5.5 */
+ return;
+ }
+
+ if (server_flavor == FLAVOR_PERCONA_SERVER) {
+ /* it is Percona Server 5.5 */
+ group->alt_offset_chosen = true;
+ group->lsn_offset = group->lsn_offset_alt;
+ return;
+ }
+
+ if (group->lsn_offset_alt == group->lsn_offset ||
+ group->lsn_offset_alt == (lsn_t) -1) {
+ /* we have only one option */
+ return;
+ }
+
+ no = alt_no = (ulint) -1;
+ lsn_chosen = 0;
+
+ blocks_in_group = log_block_convert_lsn_to_no(
+ log_group_get_capacity(group)) - 1;
+
+ /* read log block number from usual offset */
+ if (group->lsn_offset < group->file_size * group->n_files &&
+ (log_group_calc_lsn_offset(start_lsn, group) %
+ UNIV_PAGE_SIZE) % OS_MIN_LOG_BLOCK_SIZE == 0) {
+ log_group_read_log_seg(LOG_RECOVER, log_sys->buf,
+ group, start_lsn, end_lsn);
+ no = log_block_get_hdr_no(log_sys->buf);
+ }
+
+ /* read log block number from Percona Server 5.5 offset */
+ tmp_offset = group->lsn_offset;
+ group->lsn_offset = group->lsn_offset_alt;
+
+ if (group->lsn_offset < group->file_size * group->n_files &&
+ (log_group_calc_lsn_offset(start_lsn, group) %
+ UNIV_PAGE_SIZE) % OS_MIN_LOG_BLOCK_SIZE == 0) {
+ log_group_read_log_seg(LOG_RECOVER, log_sys->buf,
+ group, start_lsn, end_lsn);
+ alt_no = log_block_get_hdr_no(log_sys->buf);
+ }
+
+ expected_no = log_block_convert_lsn_to_no(start_lsn);
+
+ ut_a(!(no == expected_no && alt_no == expected_no));
+
+ group->lsn_offset = tmp_offset;
+
+ if ((no <= expected_no &&
+ ((expected_no - no) % blocks_in_group) == 0) ||
+ ((expected_no | 0x40000000UL) - no) % blocks_in_group == 0) {
+ /* default offset looks ok */
+ ++lsn_chosen;
+ }
+
+ if ((alt_no <= expected_no &&
+ ((expected_no - alt_no) % blocks_in_group) == 0) ||
+ ((expected_no | 0x40000000UL) - alt_no) % blocks_in_group == 0) {
+ /* PS 5.5 style offset looks ok */
+ ++lsn_chosen;
+ group->alt_offset_chosen = true;
+ group->lsn_offset = group->lsn_offset_alt;
+ }
+
+ /* We are in trouble, because we can not make a
+ decision to choose one over the other. Die just
+ like a Buridan's ass */
+ ut_a(lsn_chosen == 1);
+#endif
+}
+
+extern ibool log_block_checksum_is_ok_or_old_format(const byte* block);
+
+/*******************************************************//**
+Scans log from a buffer and writes new log data to the outpud datasinc.
+@return true if success */
+static
+bool
+xtrabackup_scan_log_recs(
+/*===============*/
+ log_group_t* group, /*!< in: log group */
+ bool is_last, /*!< in: whether it is last segment
+ to copy */
+ lsn_t start_lsn, /*!< in: buffer start lsn */
+ lsn_t* contiguous_lsn, /*!< in/out: it is known that all log
+ groups contain contiguous log data up
+ to this lsn */
+ lsn_t* group_scanned_lsn,/*!< out: scanning succeeded up to
+ this lsn */
+ bool* finished) /*!< out: false if is not able to scan
+ any more in this log group */
+{
+ lsn_t scanned_lsn;
+ ulint data_len;
+ ulint write_size;
+ const byte* log_block;
+
+ ulint scanned_checkpoint_no = 0;
+
+ *finished = false;
+ scanned_lsn = start_lsn;
+ log_block = log_sys->buf;
+
+ while (log_block < log_sys->buf + RECV_SCAN_SIZE && !*finished) {
+ ulint no = log_block_get_hdr_no(log_block);
+ ulint scanned_no = log_block_convert_lsn_to_no(scanned_lsn);
+ ibool checksum_is_ok =
+ log_block_checksum_is_ok_or_old_format(log_block);
+
+ if (no != scanned_no && checksum_is_ok) {
+ ulint blocks_in_group;
+
+ blocks_in_group = log_block_convert_lsn_to_no(
+ log_group_get_capacity(group)) - 1;
+
+ if ((no < scanned_no &&
+ ((scanned_no - no) % blocks_in_group) == 0) ||
+ no == 0 ||
+ /* Log block numbers wrap around at 0x3FFFFFFF */
+ ((scanned_no | 0x40000000UL) - no) %
+ blocks_in_group == 0) {
+
+ /* old log block, do nothing */
+ *finished = true;
+ break;
+ }
+
+ msg("xtrabackup: error:"
+ " log block numbers mismatch:\n"
+ "xtrabackup: error: expected log block no. %lu,"
+ " but got no. %lu from the log file.\n",
+ (ulong) scanned_no, (ulong) no);
+
+ if ((no - scanned_no) % blocks_in_group == 0) {
+ msg("xtrabackup: error:"
+ " it looks like InnoDB log has wrapped"
+ " around before xtrabackup could"
+ " process all records due to either"
+ " log copying being too slow, or "
+ " log files being too small.\n");
+ }
+
+ return(false);
+ } else if (!checksum_is_ok) {
+ /* Garbage or an incompletely written log block */
+
+ msg("xtrabackup: warning: Log block checksum mismatch"
+ " (block no %lu at lsn " LSN_PF "): \n"
+ "expected %lu, calculated checksum %lu\n",
+ (ulong) no,
+ scanned_lsn,
+ (ulong) log_block_get_checksum(log_block),
+ (ulong) log_block_calc_checksum(log_block));
+ msg("xtrabackup: warning: this is possible when the "
+ "log block has not been fully written by the "
+ "server, will retry later.\n");
+ *finished = true;
+ break;
+ }
+
+ if (log_block_get_flush_bit(log_block)) {
+ /* This block was a start of a log flush operation:
+ we know that the previous flush operation must have
+ been completed for all log groups before this block
+ can have been flushed to any of the groups. Therefore,
+ we know that log data is contiguous up to scanned_lsn
+ in all non-corrupt log groups. */
+
+ if (scanned_lsn > *contiguous_lsn) {
+
+ *contiguous_lsn = scanned_lsn;
+ }
+ }
+
+ data_len = log_block_get_data_len(log_block);
+
+ if (
+ (scanned_checkpoint_no > 0)
+ && (log_block_get_checkpoint_no(log_block)
+ < scanned_checkpoint_no)
+ && (scanned_checkpoint_no
+ - log_block_get_checkpoint_no(log_block)
+ > 0x80000000UL)) {
+
+ /* Garbage from a log buffer flush which was made
+ before the most recent database recovery */
+
+ *finished = true;
+ break;
+ }
+
+ scanned_lsn = scanned_lsn + data_len;
+ scanned_checkpoint_no = log_block_get_checkpoint_no(log_block);
+
+ if (data_len < OS_FILE_LOG_BLOCK_SIZE) {
+ /* Log data for this group ends here */
+
+ *finished = true;
+ } else {
+ log_block += OS_FILE_LOG_BLOCK_SIZE;
+ }
+ }
+
+ *group_scanned_lsn = scanned_lsn;
+
+ /* ===== write log to 'xtrabackup_logfile' ====== */
+ if (!*finished) {
+ write_size = RECV_SCAN_SIZE;
+ } else {
+ write_size = (ulint)(ut_uint64_align_up(scanned_lsn,
+ OS_FILE_LOG_BLOCK_SIZE) - start_lsn);
+ if (!is_last && scanned_lsn % OS_FILE_LOG_BLOCK_SIZE) {
+ write_size -= OS_FILE_LOG_BLOCK_SIZE;
+ }
+ }
+
+ if (write_size == 0) {
+ return(true);
+ }
+
+ if (srv_encrypt_log) {
+ log_encrypt_before_write(scanned_checkpoint_no,
+ log_sys->buf, write_size);
+ }
+
+ if (ds_write(dst_log_file, log_sys->buf, write_size)) {
+ msg("xtrabackup: Error: "
+ "write to logfile failed\n");
+ return(false);
+ }
+
+ return(true);
+}
+
+static my_bool
+xtrabackup_copy_logfile(lsn_t from_lsn, my_bool is_last)
+{
+ /* definition from recv_recovery_from_checkpoint_start() */
+ log_group_t* group;
+ lsn_t group_scanned_lsn;
+ lsn_t contiguous_lsn;
+
+ ut_a(dst_log_file != NULL);
+
+ /* read from checkpoint_lsn_start to current */
+ contiguous_lsn = ut_uint64_align_down(from_lsn, OS_FILE_LOG_BLOCK_SIZE);
+
+ /* TODO: We must check the contiguous_lsn still exists in log file.. */
+
+ group = UT_LIST_GET_FIRST(log_sys->log_groups);
+
+ while (group) {
+ bool finished;
+ lsn_t start_lsn;
+ lsn_t end_lsn;
+
+ /* reference recv_group_scan_log_recs() */
+ finished = false;
+
+ start_lsn = contiguous_lsn;
+
+ while (!finished) {
+
+ end_lsn = start_lsn + RECV_SCAN_SIZE;
+
+ xtrabackup_io_throttling();
+
+ mutex_enter(&log_sys->mutex);
+
+ log_group_read_log_seg(LOG_RECOVER, log_sys->buf,
+ group, start_lsn, end_lsn, false);
+
+ if (!xtrabackup_scan_log_recs(group, is_last,
+ start_lsn, &contiguous_lsn, &group_scanned_lsn,
+ &finished)) {
+ goto error;
+ }
+
+ mutex_exit(&log_sys->mutex);
+
+ start_lsn = end_lsn;
+
+ }
+
+ group->scanned_lsn = group_scanned_lsn;
+
+ msg_ts(">> log scanned up to (" LSN_PF ")\n",
+ group->scanned_lsn);
+
+ group = UT_LIST_GET_NEXT(log_groups, group);
+
+ /* update global variable*/
+ log_copy_scanned_lsn = group_scanned_lsn;
+
+ /* innodb_mirrored_log_groups must be 1, no other groups */
+ ut_a(group == NULL);
+
+ debug_sync_point("xtrabackup_copy_logfile_pause");
+
+ }
+
+
+ return(FALSE);
+
+error:
+ mutex_exit(&log_sys->mutex);
+ ds_close(dst_log_file);
+ msg("xtrabackup: Error: xtrabackup_copy_logfile() failed.\n");
+ return(TRUE);
+}
+
+static
+#ifndef __WIN__
+void*
+#else
+ulint
+#endif
+log_copying_thread(
+ void* arg __attribute__((unused)))
+{
+ /*
+ Initialize mysys thread-specific memory so we can
+ use mysys functions in this thread.
+ */
+ my_thread_init();
+
+ ut_a(dst_log_file != NULL);
+
+ log_copying_running = TRUE;
+
+ while(log_copying) {
+ os_event_reset(log_copying_stop);
+ os_event_wait_time_low(log_copying_stop,
+ xtrabackup_log_copy_interval * 1000ULL,
+ 0);
+ if (log_copying) {
+ if(xtrabackup_copy_logfile(log_copy_scanned_lsn,
+ FALSE)) {
+
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+
+ /* last copying */
+ if(xtrabackup_copy_logfile(log_copy_scanned_lsn, TRUE)) {
+
+ exit(EXIT_FAILURE);
+ }
+
+ log_copying_running = FALSE;
+ my_thread_end();
+ os_thread_exit(NULL);
+
+ return(0);
+}
+
+/* io throttle watching (rough) */
+static
+#ifndef __WIN__
+void*
+#else
+ulint
+#endif
+io_watching_thread(
+ void* arg)
+{
+ (void)arg;
+ /* currently, for --backup only */
+ ut_a(xtrabackup_backup);
+
+ io_watching_thread_running = TRUE;
+
+ while (log_copying) {
+ os_thread_sleep(1000000); /*1 sec*/
+ io_ticket = xtrabackup_throttle;
+ os_event_set(wait_throttle);
+ }
+
+ /* stop io throttle */
+ xtrabackup_throttle = 0;
+ os_event_set(wait_throttle);
+
+ io_watching_thread_running = FALSE;
+
+ os_thread_exit(NULL);
+
+ return(0);
+}
+
+/************************************************************************
+I/o-handler thread function. */
+static
+
+#ifndef __WIN__
+void*
+#else
+ulint
+#endif
+io_handler_thread(
+/*==============*/
+ void* arg)
+{
+ ulint segment;
+
+
+ segment = *((ulint*)arg);
+
+ while (srv_shutdown_state != SRV_SHUTDOWN_EXIT_THREADS) {
+ fil_aio_wait(segment);
+ }
+
+ /* We count the number of threads in os_thread_exit(). A created
+ thread should always use that to exit and not use return() to exit.
+ The thread actually never comes here because it is exited in an
+ os_event_wait(). */
+
+ os_thread_exit(NULL);
+
+#ifndef __WIN__
+ return(NULL); /* Not reached */
+#else
+ return(0);
+#endif
+}
+
+/**************************************************************************
+Datafiles copying thread.*/
+static
+os_thread_ret_t
+data_copy_thread_func(
+/*==================*/
+ void *arg) /* thread context */
+{
+ data_thread_ctxt_t *ctxt = (data_thread_ctxt_t *) arg;
+ uint num = ctxt->num;
+ fil_node_t* node;
+
+ /*
+ Initialize mysys thread-specific memory so we can
+ use mysys functions in this thread.
+ */
+ my_thread_init();
+
+ debug_sync_point("data_copy_thread_func");
+
+ while ((node = datafiles_iter_next(ctxt->it)) != NULL) {
+
+ /* copy the datafile */
+ if(xtrabackup_copy_datafile(node, num)) {
+ msg("[%02u] xtrabackup: Error: "
+ "failed to copy datafile.\n", num);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ os_mutex_enter(ctxt->count_mutex);
+ (*ctxt->count)--;
+ os_mutex_exit(ctxt->count_mutex);
+
+ my_thread_end();
+ os_thread_exit(NULL);
+ OS_THREAD_DUMMY_RETURN;
+}
+
+/************************************************************************
+Initialize the appropriate datasink(s). Both local backups and streaming in the
+'xbstream' format allow parallel writes so we can write directly.
+
+Otherwise (i.e. when streaming in the 'tar' format) we need 2 separate datasinks
+for the data stream (and don't allow parallel data copying) and for metainfo
+files (including xtrabackup_logfile). The second datasink writes to temporary
+files first, and then streams them in a serialized way when closed. */
+static void
+xtrabackup_init_datasinks(void)
+{
+ if (xtrabackup_parallel > 1 && xtrabackup_stream &&
+ xtrabackup_stream_fmt == XB_STREAM_FMT_TAR) {
+ msg("xtrabackup: warning: the --parallel option does not have "
+ "any effect when streaming in the 'tar' format. "
+ "You can use the 'xbstream' format instead.\n");
+ xtrabackup_parallel = 1;
+ }
+
+ /* Start building out the pipelines from the terminus back */
+ if (xtrabackup_stream) {
+ /* All streaming goes to stdout */
+ ds_data = ds_meta = ds_redo = ds_create(xtrabackup_target_dir,
+ DS_TYPE_STDOUT);
+ } else {
+ /* Local filesystem */
+ ds_data = ds_meta = ds_redo = ds_create(xtrabackup_target_dir,
+ DS_TYPE_LOCAL);
+ }
+
+ /* Track it for destruction */
+ xtrabackup_add_datasink(ds_data);
+
+ /* Stream formatting */
+ if (xtrabackup_stream) {
+ ds_ctxt_t *ds;
+ if (xtrabackup_stream_fmt == XB_STREAM_FMT_TAR) {
+ ds = ds_create(xtrabackup_target_dir, DS_TYPE_ARCHIVE);
+ } else if (xtrabackup_stream_fmt == XB_STREAM_FMT_XBSTREAM) {
+ ds = ds_create(xtrabackup_target_dir, DS_TYPE_XBSTREAM);
+ } else {
+ /* bad juju... */
+ ds = NULL;
+ }
+
+ xtrabackup_add_datasink(ds);
+
+ ds_set_pipe(ds, ds_data);
+ ds_data = ds;
+
+ if (xtrabackup_stream_fmt != XB_STREAM_FMT_XBSTREAM) {
+
+ /* 'tar' does not allow parallel streams */
+ ds_redo = ds_meta = ds_create(xtrabackup_target_dir,
+ DS_TYPE_TMPFILE);
+ xtrabackup_add_datasink(ds_meta);
+ ds_set_pipe(ds_meta, ds);
+ } else {
+ ds_redo = ds_meta = ds_data;
+ }
+ }
+
+ /* Encryption */
+ if (xtrabackup_encrypt) {
+ ds_ctxt_t *ds;
+
+
+
+ ds = ds_create(xtrabackup_target_dir, DS_TYPE_ENCRYPT);
+ xtrabackup_add_datasink(ds);
+
+ ds_set_pipe(ds, ds_data);
+ if (ds_data != ds_meta) {
+ ds_data = ds;
+ ds = ds_create(xtrabackup_target_dir, DS_TYPE_ENCRYPT);
+ xtrabackup_add_datasink(ds);
+
+ ds_set_pipe(ds, ds_meta);
+ ds_redo = ds_meta = ds;
+ } else {
+ ds_redo = ds_data = ds_meta = ds;
+ }
+ }
+
+ /* Compression for ds_data and ds_redo */
+ if (xtrabackup_compress) {
+ ds_ctxt_t *ds;
+
+ /* Use a 1 MB buffer for compressed output stream */
+ ds = ds_create(xtrabackup_target_dir, DS_TYPE_BUFFER);
+ ds_buffer_set_size(ds, 1024 * 1024);
+ xtrabackup_add_datasink(ds);
+ ds_set_pipe(ds, ds_data);
+ if (ds_data != ds_redo) {
+ ds_data = ds;
+ ds = ds_create(xtrabackup_target_dir, DS_TYPE_BUFFER);
+ ds_buffer_set_size(ds, 1024 * 1024);
+ xtrabackup_add_datasink(ds);
+ ds_set_pipe(ds, ds_redo);
+ ds_redo = ds;
+ } else {
+ ds_redo = ds_data = ds;
+ }
+
+ ds = ds_create(xtrabackup_target_dir, DS_TYPE_COMPRESS);
+ xtrabackup_add_datasink(ds);
+ ds_set_pipe(ds, ds_data);
+ if (ds_data != ds_redo) {
+ ds_data = ds;
+ ds = ds_create(xtrabackup_target_dir, DS_TYPE_COMPRESS);
+ xtrabackup_add_datasink(ds);
+ ds_set_pipe(ds, ds_redo);
+ ds_redo = ds;
+ } else {
+ ds_redo = ds_data = ds;
+ }
+ }
+}
+
+/************************************************************************
+Destroy datasinks.
+
+Destruction is done in the specific order to not violate their order in the
+pipeline so that each datasink is able to flush data down the pipeline. */
+static void xtrabackup_destroy_datasinks(void)
+{
+ for (uint i = actual_datasinks; i > 0; i--) {
+ ds_destroy(datasinks[i-1]);
+ datasinks[i-1] = NULL;
+ }
+ ds_data = NULL;
+ ds_meta = NULL;
+ ds_redo = NULL;
+}
+
+#define SRV_N_PENDING_IOS_PER_THREAD OS_AIO_N_PENDING_IOS_PER_THREAD
+#define SRV_MAX_N_PENDING_SYNC_IOS 100
+
+/************************************************************************
+@return TRUE if table should be opened. */
+static
+ibool
+xb_check_if_open_tablespace(
+ const char* db,
+ const char* table)
+{
+ char buf[FN_REFLEN];
+
+ snprintf(buf, sizeof(buf), "%s/%s", db, table);
+
+ return !check_if_skip_table(buf);
+}
+
+/************************************************************************
+Initializes the I/O and tablespace cache subsystems. */
+static
+void
+xb_fil_io_init(void)
+/*================*/
+{
+ srv_n_file_io_threads = srv_n_read_io_threads;
+
+ os_aio_init(8 * SRV_N_PENDING_IOS_PER_THREAD,
+ srv_n_read_io_threads,
+ srv_n_write_io_threads,
+ SRV_MAX_N_PENDING_SYNC_IOS);
+
+ fil_init(srv_file_per_table ? 50000 : 5000, LONG_MAX);
+
+ fsp_init();
+}
+
+/****************************************************************************
+Populates the tablespace memory cache by scanning for and opening data files.
+@returns DB_SUCCESS or error code.*/
+static
+ulint
+xb_load_tablespaces(void)
+/*=====================*/
+{
+ ulint i;
+ ibool create_new_db;
+ ulint err;
+ ulint sum_of_new_sizes;
+ lsn_t min_arch_logno, max_arch_logno;
+
+ for (i = 0; i < srv_n_file_io_threads; i++) {
+ thread_nr[i] = i;
+
+ os_thread_create(io_handler_thread, thread_nr + i,
+ thread_ids + i);
+ }
+
+ os_thread_sleep(200000); /*0.2 sec*/
+
+ err = open_or_create_data_files(&create_new_db,
+ &min_arch_logno, &max_arch_logno,
+ &min_flushed_lsn, &max_flushed_lsn,
+ &sum_of_new_sizes);
+ if (err != DB_SUCCESS) {
+ msg("xtrabackup: Could not open or create data files.\n"
+ "xtrabackup: If you tried to add new data files, and it "
+ "failed here,\n"
+ "xtrabackup: you should now edit innodb_data_file_path in "
+ "my.cnf back\n"
+ "xtrabackup: to what it was, and remove the new ibdata "
+ "files InnoDB created\n"
+ "xtrabackup: in this failed attempt. InnoDB only wrote "
+ "those files full of\n"
+ "xtrabackup: zeros, but did not yet use them in any way. "
+ "But be careful: do not\n"
+ "xtrabackup: remove old data files which contain your "
+ "precious data!\n");
+ return(err);
+ }
+
+ /* create_new_db must not be TRUE.. */
+ if (create_new_db) {
+ msg("xtrabackup: could not find data files at the "
+ "specified datadir\n");
+ return(DB_ERROR);
+ }
+
+ /* Add separate undo tablespaces to fil_system */
+
+ err = srv_undo_tablespaces_init(FALSE,
+ TRUE,
+ srv_undo_tablespaces,
+ &srv_undo_tablespaces_open);
+ if (err != DB_SUCCESS) {
+ return(err);
+ }
+
+ /* It is important to call fil_load_single_table_tablespace() after
+ srv_undo_tablespaces_init(), because fil_is_user_tablespace_id() *
+ relies on srv_undo_tablespaces_open to be properly initialized */
+
+ msg("xtrabackup: Generating a list of tablespaces\n");
+
+ err = fil_load_single_table_tablespaces(xb_check_if_open_tablespace);
+ if (err != DB_SUCCESS) {
+ return(err);
+ }
+
+ debug_sync_point("xtrabackup_load_tablespaces_pause");
+
+ return(DB_SUCCESS);
+}
+
+/************************************************************************
+Initialize the tablespace memory cache and populate it by scanning for and
+opening data files.
+@returns DB_SUCCESS or error code.*/
+ulint
+xb_data_files_init(void)
+/*====================*/
+{
+ xb_fil_io_init();
+
+ return(xb_load_tablespaces());
+}
+
+/************************************************************************
+Destroy the tablespace memory cache. */
+void
+xb_data_files_close(void)
+/*====================*/
+{
+ ulint i;
+
+ /* Shutdown the aio threads. This has been copied from
+ innobase_shutdown_for_mysql(). */
+
+ srv_shutdown_state = SRV_SHUTDOWN_EXIT_THREADS;
+
+ for (i = 0; i < 1000; i++) {
+ os_aio_wake_all_threads_at_shutdown();
+
+ if (os_thread_count == 0) {
+ break;
+ }
+ os_thread_sleep(10000);
+ }
+
+ if (i == 1000) {
+ msg("xtrabackup: Warning: %lu threads created by InnoDB"
+ " had not exited at shutdown!\n",
+ (ulong) os_thread_count);
+ }
+
+ os_aio_free();
+
+ fil_close_all_files();
+
+ /* Free the double write data structures. */
+ if (buf_dblwr) {
+ buf_dblwr_free();
+ }
+
+ /* Reset srv_file_io_threads to its default value to avoid confusing
+ warning on --prepare in innobase_start_or_create_for_mysql()*/
+ srv_n_file_io_threads = 4;
+
+ srv_shutdown_state = SRV_SHUTDOWN_NONE;
+}
+
+/***********************************************************************
+Allocate and initialize the entry for databases and tables filtering
+hash tables. If memory allocation is not successful, terminate program.
+@return pointer to the created entry. */
+static
+xb_filter_entry_t *
+xb_new_filter_entry(
+/*================*/
+ const char* name) /*!< in: name of table/database */
+{
+ xb_filter_entry_t *entry;
+ ulint namelen = strlen(name);
+
+ ut_a(namelen <= NAME_LEN * 2 + 1);
+
+ entry = static_cast<xb_filter_entry_t *>
+ (ut_malloc(sizeof(xb_filter_entry_t) + namelen + 1));
+ memset(entry, '\0', sizeof(xb_filter_entry_t) + namelen + 1);
+ entry->name = ((char*)entry) + sizeof(xb_filter_entry_t);
+ strcpy(entry->name, name);
+ entry->has_tables = FALSE;
+
+ return entry;
+}
+
+/***********************************************************************
+Add entry to hash table. If hash table is NULL, allocate and initialize
+new hash table */
+static
+xb_filter_entry_t*
+xb_add_filter(
+/*========================*/
+ const char* name, /*!< in: name of table/database */
+ hash_table_t** hash) /*!< in/out: hash to insert into */
+{
+ xb_filter_entry_t* entry;
+
+ entry = xb_new_filter_entry(name);
+
+ if (UNIV_UNLIKELY(*hash == NULL)) {
+ *hash = hash_create(1000);
+ }
+ HASH_INSERT(xb_filter_entry_t,
+ name_hash, *hash,
+ ut_fold_string(entry->name),
+ entry);
+
+ return entry;
+}
+
+/***********************************************************************
+Validate name of table or database. If name is invalid, program will
+be finished with error code */
+static
+void
+xb_validate_name(
+/*=============*/
+ const char* name, /*!< in: name */
+ size_t len) /*!< in: length of name */
+{
+ const char* p;
+
+ /* perform only basic validation. validate length and
+ path symbols */
+ if (len > NAME_LEN) {
+ msg("xtrabackup: name `%s` is too long.\n", name);
+ exit(EXIT_FAILURE);
+ }
+ p = strpbrk(name, "/\\~");
+ if (p && p - name < NAME_LEN) {
+ msg("xtrabackup: name `%s` is not valid.\n", name);
+ exit(EXIT_FAILURE);
+ }
+}
+
+/***********************************************************************
+Register new filter entry which can be either database
+or table name. */
+static
+void
+xb_register_filter_entry(
+/*=====================*/
+ const char* name, /*!< in: name */
+ hash_table_t** databases_hash,
+ hash_table_t** tables_hash
+ )
+{
+ const char* p;
+ size_t namelen;
+ xb_filter_entry_t* db_entry = NULL;
+
+ namelen = strlen(name);
+ if ((p = strchr(name, '.')) != NULL) {
+ char dbname[NAME_LEN + 1];
+
+ xb_validate_name(name, p - name);
+ xb_validate_name(p + 1, namelen - (p - name));
+
+ strncpy(dbname, name, p - name);
+ dbname[p - name] = 0;
+
+ if (*databases_hash) {
+ HASH_SEARCH(name_hash, (*databases_hash),
+ ut_fold_string(dbname),
+ xb_filter_entry_t*,
+ db_entry, (void) 0,
+ !strcmp(db_entry->name, dbname));
+ }
+ if (!db_entry) {
+ db_entry = xb_add_filter(dbname, databases_hash);
+ }
+ db_entry->has_tables = TRUE;
+ xb_add_filter(name, tables_hash);
+ } else {
+ xb_validate_name(name, namelen);
+
+ xb_add_filter(name, databases_hash);
+ }
+}
+
+static
+void
+xb_register_include_filter_entry(
+ const char* name
+)
+{
+ xb_register_filter_entry(name, &databases_include_hash,
+ &tables_include_hash);
+}
+
+static
+void
+xb_register_exclude_filter_entry(
+ const char* name
+)
+{
+ xb_register_filter_entry(name, &databases_exclude_hash,
+ &tables_exclude_hash);
+}
+
+/***********************************************************************
+Register new table for the filter. */
+static
+void
+xb_register_table(
+/*==============*/
+ const char* name) /*!< in: name of table */
+{
+ if (strchr(name, '.') == NULL) {
+ msg("xtrabackup: `%s` is not fully qualified name.\n", name);
+ exit(EXIT_FAILURE);
+ }
+
+ xb_register_include_filter_entry(name);
+}
+
+static
+void
+xb_add_regex_to_list(
+ const char* regex, /*!< in: regex */
+ const char* error_context, /*!< in: context to error message */
+ regex_list_t* list) /*! in: list to put new regex to */
+{
+ char errbuf[100];
+ int ret;
+
+ regex_t compiled_regex;
+ ret = regcomp(&compiled_regex, regex, REG_EXTENDED);
+
+ if (ret != 0) {
+ regerror(ret, &compiled_regex, errbuf, sizeof(errbuf));
+ msg("xtrabackup: error: %s regcomp(%s): %s\n",
+ error_context, regex, errbuf);
+ exit(EXIT_FAILURE);
+ }
+
+ list->push_back(compiled_regex);
+}
+
+/***********************************************************************
+Register new regex for the include filter. */
+static
+void
+xb_register_include_regex(
+/*==============*/
+ const char* regex) /*!< in: regex */
+{
+ xb_add_regex_to_list(regex, "tables", &regex_include_list);
+}
+
+/***********************************************************************
+Register new regex for the exclude filter. */
+static
+void
+xb_register_exclude_regex(
+/*==============*/
+ const char* regex) /*!< in: regex */
+{
+ xb_add_regex_to_list(regex, "tables-exclude", &regex_exclude_list);
+}
+
+typedef void (*insert_entry_func_t)(const char*);
+
+/***********************************************************************
+Scan string and load filter entries from it. */
+static
+void
+xb_load_list_string(
+/*================*/
+ char* list, /*!< in: string representing a list */
+ const char* delimiters, /*!< in: delimiters of entries */
+ insert_entry_func_t ins) /*!< in: callback to add entry */
+{
+ char* p;
+ char* saveptr;
+
+ p = strtok_r(list, delimiters, &saveptr);
+ while (p) {
+
+ ins(p);
+
+ p = strtok_r(NULL, delimiters, &saveptr);
+ }
+}
+
+/***********************************************************************
+Scan file and load filter entries from it. */
+static
+void
+xb_load_list_file(
+/*==============*/
+ const char* filename, /*!< in: name of file */
+ insert_entry_func_t ins) /*!< in: callback to add entry */
+{
+ char name_buf[NAME_LEN*2+2];
+ FILE* fp;
+
+ /* read and store the filenames */
+ fp = fopen(filename, "r");
+ if (!fp) {
+ msg("xtrabackup: cannot open %s\n",
+ filename);
+ exit(EXIT_FAILURE);
+ }
+ while (fgets(name_buf, sizeof(name_buf), fp) != NULL) {
+ char* p = strchr(name_buf, '\n');
+ if (p) {
+ *p = '\0';
+ } else {
+ msg("xtrabackup: `%s...` name is too long", name_buf);
+ exit(EXIT_FAILURE);
+ }
+
+ ins(name_buf);
+ }
+
+ fclose(fp);
+}
+
+
+static
+void
+xb_filters_init()
+{
+ if (xtrabackup_databases) {
+ xb_load_list_string(xtrabackup_databases, " \t",
+ xb_register_include_filter_entry);
+ }
+
+ if (xtrabackup_databases_file) {
+ xb_load_list_file(xtrabackup_databases_file,
+ xb_register_include_filter_entry);
+ }
+
+ if (xtrabackup_databases_exclude) {
+ xb_load_list_string(xtrabackup_databases_exclude, " \t",
+ xb_register_exclude_filter_entry);
+ }
+
+ if (xtrabackup_tables) {
+ xb_load_list_string(xtrabackup_tables, ",",
+ xb_register_include_regex);
+ }
+
+ if (xtrabackup_tables_file) {
+ xb_load_list_file(xtrabackup_tables_file, xb_register_table);
+ }
+
+ if (xtrabackup_tables_exclude) {
+ xb_load_list_string(xtrabackup_tables_exclude, ",",
+ xb_register_exclude_regex);
+ }
+}
+
+static
+void
+xb_filter_hash_free(hash_table_t* hash)
+{
+ ulint i;
+
+ /* free the hash elements */
+ for (i = 0; i < hash_get_n_cells(hash); i++) {
+ xb_filter_entry_t* table;
+
+ table = static_cast<xb_filter_entry_t *>
+ (HASH_GET_FIRST(hash, i));
+
+ while (table) {
+ xb_filter_entry_t* prev_table = table;
+
+ table = static_cast<xb_filter_entry_t *>
+ (HASH_GET_NEXT(name_hash, prev_table));
+
+ HASH_DELETE(xb_filter_entry_t, name_hash, hash,
+ ut_fold_string(prev_table->name), prev_table);
+ ut_free(prev_table);
+ }
+ }
+
+ /* free hash */
+ hash_table_free(hash);
+}
+
+static void xb_regex_list_free(regex_list_t* list)
+{
+ while (list->size() > 0) {
+ xb_regfree(&list->front());
+ list->pop_front();
+ }
+}
+
+/************************************************************************
+Destroy table filters for partial backup. */
+static
+void
+xb_filters_free()
+{
+ xb_regex_list_free(&regex_include_list);
+ xb_regex_list_free(&regex_exclude_list);
+
+ if (tables_include_hash) {
+ xb_filter_hash_free(tables_include_hash);
+ }
+
+ if (tables_exclude_hash) {
+ xb_filter_hash_free(tables_exclude_hash);
+ }
+
+ if (databases_include_hash) {
+ xb_filter_hash_free(databases_include_hash);
+ }
+
+ if (databases_exclude_hash) {
+ xb_filter_hash_free(databases_exclude_hash);
+ }
+}
+
+/*********************************************************************//**
+Creates or opens the log files and closes them.
+@return DB_SUCCESS or error code */
+static
+ulint
+open_or_create_log_file(
+/*====================*/
+ ibool create_new_db, /*!< in: TRUE if we should create a
+ new database */
+ ibool* log_file_created, /*!< out: TRUE if new log file
+ created */
+ ibool log_file_has_been_opened,/*!< in: TRUE if a log file has been
+ opened before: then it is an error
+ to try to create another log file */
+ ulint k, /*!< in: log group number */
+ ulint i) /*!< in: log file number in group */
+{
+ ibool ret;
+ os_offset_t size;
+ char name[10000];
+ ulint dirnamelen;
+
+ UT_NOT_USED(create_new_db);
+ UT_NOT_USED(log_file_has_been_opened);
+ UT_NOT_USED(k);
+ ut_ad(k == 0);
+
+ *log_file_created = FALSE;
+
+ srv_normalize_path_for_win(srv_log_group_home_dir);
+
+ dirnamelen = strlen(srv_log_group_home_dir);
+ ut_a(dirnamelen < (sizeof name) - 10 - sizeof "ib_logfile");
+ memcpy(name, srv_log_group_home_dir, dirnamelen);
+
+ /* Add a path separator if needed. */
+ if (dirnamelen && name[dirnamelen - 1] != SRV_PATH_SEPARATOR) {
+ name[dirnamelen++] = SRV_PATH_SEPARATOR;
+ }
+
+ sprintf(name + dirnamelen, "%s%lu", "ib_logfile", (ulong) i);
+
+ files[i] = os_file_create(innodb_file_log_key, name,
+ OS_FILE_OPEN, OS_FILE_NORMAL,
+ OS_LOG_FILE, &ret,0);
+ if (ret == FALSE) {
+ fprintf(stderr, "InnoDB: Error in opening %s\n", name);
+
+ return(DB_ERROR);
+ }
+
+ size = os_file_get_size(files[i]);
+
+ if (size != srv_log_file_size * UNIV_PAGE_SIZE) {
+
+ fprintf(stderr,
+ "InnoDB: Error: log file %s is"
+ " of different size " UINT64PF " bytes\n"
+ "InnoDB: than specified in the .cnf"
+ " file " UINT64PF " bytes!\n",
+ name, size, srv_log_file_size * UNIV_PAGE_SIZE);
+
+ return(DB_ERROR);
+ }
+
+ ret = os_file_close(files[i]);
+ ut_a(ret);
+
+ if (i == 0) {
+ /* Create in memory the file space object
+ which is for this log group */
+
+ fil_space_create(name,
+ 2 * k + SRV_LOG_SPACE_FIRST_ID, 0, FIL_LOG, 0, 0);
+ }
+
+ ut_a(fil_validate());
+
+ ut_a(fil_node_create(name, (ulint)srv_log_file_size,
+ 2 * k + SRV_LOG_SPACE_FIRST_ID, FALSE));
+ if (i == 0) {
+ log_group_init(k, srv_n_log_files,
+ srv_log_file_size * UNIV_PAGE_SIZE,
+ 2 * k + SRV_LOG_SPACE_FIRST_ID,
+ SRV_LOG_SPACE_FIRST_ID + 1); /* dummy arch
+ space id */
+ }
+
+ return(DB_SUCCESS);
+}
+
+/*********************************************************************//**
+Normalizes init parameter values to use units we use inside InnoDB.
+@return DB_SUCCESS or error code */
+static
+void
+xb_normalize_init_values(void)
+/*==========================*/
+{
+ ulint i;
+
+ for (i = 0; i < srv_n_data_files; i++) {
+ srv_data_file_sizes[i] = srv_data_file_sizes[i]
+ * ((1024 * 1024) / UNIV_PAGE_SIZE);
+ }
+
+ srv_last_file_size_max = srv_last_file_size_max
+ * ((1024 * 1024) / UNIV_PAGE_SIZE);
+
+ srv_log_file_size = srv_log_file_size / UNIV_PAGE_SIZE;
+
+ srv_log_buffer_size = srv_log_buffer_size / UNIV_PAGE_SIZE;
+
+ srv_lock_table_size = 5 * (srv_buf_pool_size / UNIV_PAGE_SIZE);
+}
+
+/***********************************************************************
+Set the open files limit. Based on set_max_open_files().
+
+@return the resulting open files limit. May be less or more than the requested
+value. */
+static uint
+xb_set_max_open_files(
+/*==================*/
+ uint max_file_limit) /*!<in: open files limit */
+{
+#if defined(RLIMIT_NOFILE)
+ struct rlimit rlimit;
+ uint old_cur;
+
+ if (getrlimit(RLIMIT_NOFILE, &rlimit)) {
+
+ goto end;
+ }
+
+ old_cur = (uint) rlimit.rlim_cur;
+
+ if (rlimit.rlim_cur == RLIM_INFINITY) {
+
+ rlimit.rlim_cur = max_file_limit;
+ }
+
+ if (rlimit.rlim_cur >= max_file_limit) {
+
+ max_file_limit = rlimit.rlim_cur;
+ goto end;
+ }
+
+ rlimit.rlim_cur = rlimit.rlim_max = max_file_limit;
+
+ if (setrlimit(RLIMIT_NOFILE, &rlimit)) {
+
+ max_file_limit = old_cur; /* Use original value */
+ } else {
+
+ rlimit.rlim_cur = 0; /* Safety if next call fails */
+
+ (void) getrlimit(RLIMIT_NOFILE, &rlimit);
+
+ if (rlimit.rlim_cur) {
+
+ /* If call didn't fail */
+ max_file_limit = (uint) rlimit.rlim_cur;
+ }
+ }
+
+end:
+ return(max_file_limit);
+#else
+ return(0);
+#endif
+}
+
+void
+xtrabackup_backup_func(void)
+{
+ MY_STAT stat_info;
+ lsn_t latest_cp;
+ uint i;
+ uint count;
+ os_ib_mutex_t count_mutex;
+ data_thread_ctxt_t *data_threads;
+
+#ifdef USE_POSIX_FADVISE
+ msg("xtrabackup: uses posix_fadvise().\n");
+#endif
+
+ /* cd to datadir */
+
+ if (my_setwd(mysql_real_data_home,MYF(MY_WME)))
+ {
+ msg("xtrabackup: cannot my_setwd %s\n", mysql_real_data_home);
+ exit(EXIT_FAILURE);
+ }
+ msg("xtrabackup: cd to %s\n", mysql_real_data_home);
+
+ msg("xtrabackup: open files limit requested %u, set to %u\n",
+ (uint) xb_open_files_limit,
+ xb_set_max_open_files(xb_open_files_limit));
+
+ mysql_data_home= mysql_data_home_buff;
+ mysql_data_home[0]=FN_CURLIB; // all paths are relative from here
+ mysql_data_home[1]=0;
+
+ srv_read_only_mode = TRUE;
+
+ srv_backup_mode = TRUE;
+ srv_close_files = (bool)xb_close_files;
+
+ if (srv_close_files)
+ msg("xtrabackup: warning: close-files specified. Use it "
+ "at your own risk. If there are DDL operations like table DROP TABLE "
+ "or RENAME TABLE during the backup, inconsistent backup will be "
+ "produced.\n");
+
+ /* initialize components */
+ if(innodb_init_param())
+ exit(EXIT_FAILURE);
+
+ xb_normalize_init_values();
+
+
+ if (srv_file_flush_method_str == NULL) {
+ /* These are the default options */
+ srv_unix_file_flush_method = SRV_UNIX_FSYNC;
+ } else if (0 == ut_strcmp(srv_file_flush_method_str, "fsync")) {
+ srv_unix_file_flush_method = SRV_UNIX_FSYNC;
+ } else if (0 == ut_strcmp(srv_file_flush_method_str, "O_DSYNC")) {
+ srv_unix_file_flush_method = SRV_UNIX_O_DSYNC;
+
+ } else if (0 == ut_strcmp(srv_file_flush_method_str, "O_DIRECT")) {
+ srv_unix_file_flush_method = SRV_UNIX_O_DIRECT;
+ msg("xtrabackup: using O_DIRECT\n");
+ } else if (0 == ut_strcmp(srv_file_flush_method_str, "littlesync")) {
+ srv_unix_file_flush_method = SRV_UNIX_LITTLESYNC;
+
+ } else if (0 == ut_strcmp(srv_file_flush_method_str, "nosync")) {
+ srv_unix_file_flush_method = SRV_UNIX_NOSYNC;
+ } else if (0 == ut_strcmp(srv_file_flush_method_str, "ALL_O_DIRECT")) {
+ srv_unix_file_flush_method = SRV_UNIX_ALL_O_DIRECT;
+ msg("xtrabackup: using ALL_O_DIRECT\n");
+ } else if (0 == ut_strcmp(srv_file_flush_method_str,
+ "O_DIRECT_NO_FSYNC")) {
+ srv_unix_file_flush_method = SRV_UNIX_O_DIRECT_NO_FSYNC;
+ msg("xtrabackup: using O_DIRECT_NO_FSYNC\n");
+ } else {
+ msg("xtrabackup: Unrecognized value %s for "
+ "innodb_flush_method\n", srv_file_flush_method_str);
+ exit(EXIT_FAILURE);
+ }
+
+ /* We can only use synchronous unbuffered IO on Windows for now */
+ if (srv_file_flush_method_str != NULL) {
+ msg("xtrabackupp: Warning: "
+ "ignoring innodb_flush_method = %s on Windows.\n", srv_file_flush_method_str);
+ }
+
+#ifdef _WIN32
+ srv_win_file_flush_method = SRV_WIN_IO_UNBUFFERED;
+ srv_use_native_aio = FALSE;
+#endif
+
+ if (srv_buf_pool_size >= 1000 * 1024 * 1024) {
+ /* Here we still have srv_pool_size counted
+ in kilobytes (in 4.0 this was in bytes)
+ srv_boot() converts the value to
+ pages; if buffer pool is less than 1000 MB,
+ assume fewer threads. */
+ srv_max_n_threads = 50000;
+
+ } else if (srv_buf_pool_size >= 8 * 1024 * 1024) {
+
+ srv_max_n_threads = 10000;
+ } else {
+ srv_max_n_threads = 1000; /* saves several MB of memory,
+ especially in 64-bit
+ computers */
+ }
+
+ srv_general_init();
+ ut_crc32_init();
+ crc_init();
+
+#ifdef WITH_INNODB_DISALLOW_WRITES
+ srv_allow_writes_event = os_event_create();
+ os_event_set(srv_allow_writes_event);
+#endif
+
+ xb_filters_init();
+
+ {
+ ibool log_file_created;
+ ibool log_created = FALSE;
+ ibool log_opened = FALSE;
+ ulint err;
+ ulint i;
+
+ xb_fil_io_init();
+
+ log_init();
+
+ lock_sys_create(srv_lock_table_size);
+
+ for (i = 0; i < srv_n_log_files; i++) {
+ err = open_or_create_log_file(FALSE, &log_file_created,
+ log_opened, 0, i);
+ if (err != DB_SUCCESS) {
+
+ //return((int) err);
+ exit(EXIT_FAILURE);
+ }
+
+ if (log_file_created) {
+ log_created = TRUE;
+ } else {
+ log_opened = TRUE;
+ }
+ if ((log_opened && log_created)) {
+ msg(
+ "xtrabackup: Error: all log files must be created at the same time.\n"
+ "xtrabackup: All log files must be created also in database creation.\n"
+ "xtrabackup: If you want bigger or smaller log files, shut down the\n"
+ "xtrabackup: database and make sure there were no errors in shutdown.\n"
+ "xtrabackup: Then delete the existing log files. Edit the .cnf file\n"
+ "xtrabackup: and start the database again.\n");
+
+ //return(DB_ERROR);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ /* log_file_created must not be TRUE, if online */
+ if (log_file_created) {
+ msg("xtrabackup: Something wrong with source files...\n");
+ exit(EXIT_FAILURE);
+ }
+
+ }
+
+ /* create extra LSN dir if it does not exist. */
+ if (xtrabackup_extra_lsndir
+ &&!my_stat(xtrabackup_extra_lsndir,&stat_info,MYF(0))
+ && (my_mkdir(xtrabackup_extra_lsndir,0777,MYF(0)) < 0)) {
+ msg("xtrabackup: Error: cannot mkdir %d: %s\n",
+ my_errno, xtrabackup_extra_lsndir);
+ exit(EXIT_FAILURE);
+ }
+
+ /* create target dir if not exist */
+ if (!xtrabackup_stream_str && !my_stat(xtrabackup_target_dir,&stat_info,MYF(0))
+ && (my_mkdir(xtrabackup_target_dir,0777,MYF(0)) < 0)){
+ msg("xtrabackup: Error: cannot mkdir %d: %s\n",
+ my_errno, xtrabackup_target_dir);
+ exit(EXIT_FAILURE);
+ }
+
+ {
+ fil_system_t* f_system = fil_system;
+
+ /* definition from recv_recovery_from_checkpoint_start() */
+ log_group_t* max_cp_group;
+ ulint max_cp_field;
+ byte* buf;
+ byte* log_hdr_buf_;
+ byte* log_hdr_buf;
+ ulint err;
+
+ /* start back ground thread to copy newer log */
+ os_thread_id_t log_copying_thread_id;
+ datafiles_iter_t *it;
+
+ log_hdr_buf_ = static_cast<byte *>
+ (ut_malloc(LOG_FILE_HDR_SIZE + UNIV_PAGE_SIZE_MAX));
+ log_hdr_buf = static_cast<byte *>
+ (ut_align(log_hdr_buf_, UNIV_PAGE_SIZE_MAX));
+
+ /* get current checkpoint_lsn */
+ /* Look for the latest checkpoint from any of the log groups */
+
+ mutex_enter(&log_sys->mutex);
+
+ err = recv_find_max_checkpoint(&max_cp_group, &max_cp_field);
+
+ if (err != DB_SUCCESS) {
+
+ ut_free(log_hdr_buf_);
+ exit(EXIT_FAILURE);
+ }
+
+ log_group_read_checkpoint_info(max_cp_group, max_cp_field);
+ buf = log_sys->checkpoint_buf;
+
+ checkpoint_lsn_start = mach_read_from_8(buf + LOG_CHECKPOINT_LSN);
+ checkpoint_no_start = mach_read_from_8(buf + LOG_CHECKPOINT_NO);
+
+ mutex_exit(&log_sys->mutex);
+
+reread_log_header:
+ fil_io(OS_FILE_READ | OS_FILE_LOG, true, max_cp_group->space_id,
+ 0,
+ 0, 0, LOG_FILE_HDR_SIZE,
+ log_hdr_buf, max_cp_group, NULL);
+
+ /* check consistency of log file header to copy */
+ mutex_enter(&log_sys->mutex);
+
+ err = recv_find_max_checkpoint(&max_cp_group, &max_cp_field);
+
+ if (err != DB_SUCCESS) {
+
+ ut_free(log_hdr_buf_);
+ exit(EXIT_FAILURE);
+ }
+
+ log_group_read_checkpoint_info(max_cp_group, max_cp_field);
+ buf = log_sys->checkpoint_buf;
+
+ if(checkpoint_no_start != mach_read_from_8(buf + LOG_CHECKPOINT_NO)) {
+
+ checkpoint_lsn_start = mach_read_from_8(buf + LOG_CHECKPOINT_LSN);
+ checkpoint_no_start = mach_read_from_8(buf + LOG_CHECKPOINT_NO);
+ mutex_exit(&log_sys->mutex);
+ goto reread_log_header;
+ }
+
+ mutex_exit(&log_sys->mutex);
+
+ xtrabackup_init_datasinks();
+
+ if (!select_history()) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* open the log file */
+ memset(&stat_info, 0, sizeof(MY_STAT));
+ dst_log_file = ds_open(ds_redo, XB_LOG_FILENAME, &stat_info);
+ if (dst_log_file == NULL) {
+ msg("xtrabackup: error: failed to open the target stream for "
+ "'%s'.\n", XB_LOG_FILENAME);
+ ut_free(log_hdr_buf_);
+ exit(EXIT_FAILURE);
+ }
+
+ /* label it */
+ strcpy((char*) log_hdr_buf + LOG_FILE_WAS_CREATED_BY_HOT_BACKUP,
+ "xtrabkup ");
+ ut_sprintf_timestamp(
+ (char*) log_hdr_buf + (LOG_FILE_WAS_CREATED_BY_HOT_BACKUP
+ + (sizeof "xtrabkup ") - 1));
+
+ if (ds_write(dst_log_file, log_hdr_buf, LOG_FILE_HDR_SIZE)) {
+ msg("xtrabackup: error: write to logfile failed\n");
+ ut_free(log_hdr_buf_);
+ exit(EXIT_FAILURE);
+ }
+
+ ut_free(log_hdr_buf_);
+
+ /* start flag */
+ log_copying = TRUE;
+
+ /* start io throttle */
+ if(xtrabackup_throttle) {
+ os_thread_id_t io_watching_thread_id;
+
+ io_ticket = xtrabackup_throttle;
+ wait_throttle = os_event_create();
+
+ os_thread_create(io_watching_thread, NULL,
+ &io_watching_thread_id);
+ }
+
+ mutex_enter(&log_sys->mutex);
+ xtrabackup_choose_lsn_offset(checkpoint_lsn_start);
+ mutex_exit(&log_sys->mutex);
+
+ /* copy log file by current position */
+ if(xtrabackup_copy_logfile(checkpoint_lsn_start, FALSE))
+ exit(EXIT_FAILURE);
+
+
+ log_copying_stop = os_event_create();
+ os_thread_create(log_copying_thread, NULL, &log_copying_thread_id);
+
+ /* Populate fil_system with tablespaces to copy */
+ err = xb_load_tablespaces();
+ if (err != DB_SUCCESS) {
+ msg("xtrabackup: error: xb_load_tablespaces() failed with"
+ "error code %lu\n", err);
+ exit(EXIT_FAILURE);
+ }
+
+ /* FLUSH CHANGED_PAGE_BITMAPS call */
+ if (!flush_changed_page_bitmaps()) {
+ exit(EXIT_FAILURE);
+ }
+ debug_sync_point("xtrabackup_suspend_at_start");
+
+ if (xtrabackup_incremental) {
+ if (!xtrabackup_incremental_force_scan) {
+ changed_page_bitmap = xb_page_bitmap_init();
+ }
+ if (!changed_page_bitmap) {
+ msg("xtrabackup: using the full scan for incremental "
+ "backup\n");
+ } else if (incremental_lsn != checkpoint_lsn_start) {
+ /* Do not print that bitmaps are used when dummy bitmap
+ is build for an empty LSN range. */
+ msg("xtrabackup: using the changed page bitmap\n");
+ }
+ }
+
+ ut_a(xtrabackup_parallel > 0);
+
+ if (xtrabackup_parallel > 1) {
+ msg("xtrabackup: Starting %u threads for parallel data "
+ "files transfer\n", xtrabackup_parallel);
+ }
+
+ it = datafiles_iter_new(f_system);
+ if (it == NULL) {
+ msg("xtrabackup: Error: datafiles_iter_new() failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Create data copying threads */
+ data_threads = (data_thread_ctxt_t *)
+ ut_malloc(sizeof(data_thread_ctxt_t) * xtrabackup_parallel);
+ count = xtrabackup_parallel;
+ count_mutex = os_mutex_create();
+
+ for (i = 0; i < (uint) xtrabackup_parallel; i++) {
+ data_threads[i].it = it;
+ data_threads[i].num = i+1;
+ data_threads[i].count = &count;
+ data_threads[i].count_mutex = count_mutex;
+ os_thread_create(data_copy_thread_func, data_threads + i,
+ &data_threads[i].id);
+ }
+
+ /* Wait for threads to exit */
+ while (1) {
+ os_thread_sleep(1000000);
+ os_mutex_enter(count_mutex);
+ if (count == 0) {
+ os_mutex_exit(count_mutex);
+ break;
+ }
+ os_mutex_exit(count_mutex);
+ }
+
+ os_mutex_free(count_mutex);
+ ut_free(data_threads);
+ datafiles_iter_free(it);
+
+ if (changed_page_bitmap) {
+ xb_page_bitmap_deinit(changed_page_bitmap);
+ }
+ }
+
+ if (!backup_start()) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* read the latest checkpoint lsn */
+ latest_cp = 0;
+ {
+ log_group_t* max_cp_group;
+ ulint max_cp_field;
+ ulint err;
+
+ mutex_enter(&log_sys->mutex);
+
+ err = recv_find_max_checkpoint(&max_cp_group, &max_cp_field);
+
+ if (err != DB_SUCCESS) {
+ msg("xtrabackup: Error: recv_find_max_checkpoint() failed.\n");
+ mutex_exit(&log_sys->mutex);
+ goto skip_last_cp;
+ }
+
+ log_group_read_checkpoint_info(max_cp_group, max_cp_field);
+
+ xtrabackup_choose_lsn_offset(checkpoint_lsn_start);
+
+ latest_cp = mach_read_from_8(log_sys->checkpoint_buf +
+ LOG_CHECKPOINT_LSN);
+
+ mutex_exit(&log_sys->mutex);
+
+ msg("xtrabackup: The latest check point (for incremental): "
+ "'" LSN_PF "'\n", latest_cp);
+ }
+skip_last_cp:
+ /* stop log_copying_thread */
+ log_copying = FALSE;
+ os_event_set(log_copying_stop);
+ msg("xtrabackup: Stopping log copying thread.\n");
+ while (log_copying_running) {
+ msg(".");
+ os_thread_sleep(200000); /*0.2 sec*/
+ }
+ msg("\n");
+
+ os_event_free(log_copying_stop);
+ if (ds_close(dst_log_file)) {
+ exit(EXIT_FAILURE);
+ }
+
+ if(!xtrabackup_incremental) {
+ strcpy(metadata_type, "full-backuped");
+ metadata_from_lsn = 0;
+ } else {
+ strcpy(metadata_type, "incremental");
+ metadata_from_lsn = incremental_lsn;
+ }
+ metadata_to_lsn = latest_cp;
+ metadata_last_lsn = log_copy_scanned_lsn;
+
+ if (!xtrabackup_stream_metadata(ds_meta)) {
+ msg("xtrabackup: Error: failed to stream metadata.\n");
+ exit(EXIT_FAILURE);
+ }
+ if (xtrabackup_extra_lsndir) {
+ char filename[FN_REFLEN];
+
+ sprintf(filename, "%s/%s", xtrabackup_extra_lsndir,
+ XTRABACKUP_METADATA_FILENAME);
+ if (!xtrabackup_write_metadata(filename)) {
+ msg("xtrabackup: Error: failed to write metadata "
+ "to '%s'.\n", filename);
+ exit(EXIT_FAILURE);
+ }
+
+ }
+
+ if (!backup_finish()) {
+ exit(EXIT_FAILURE);
+ }
+
+ xtrabackup_destroy_datasinks();
+
+ if (wait_throttle) {
+ /* wait for io_watching_thread completion */
+ while (io_watching_thread_running) {
+ os_thread_sleep(1000000);
+ }
+ os_event_free(wait_throttle);
+ wait_throttle = NULL;
+ }
+
+ msg("xtrabackup: Transaction log of lsn (" LSN_PF ") to (" LSN_PF
+ ") was copied.\n", checkpoint_lsn_start, log_copy_scanned_lsn);
+ xb_filters_free();
+
+ xb_data_files_close();
+
+ /* Make sure that the latest checkpoint made it to xtrabackup_logfile */
+ if (latest_cp > log_copy_scanned_lsn) {
+ msg("xtrabackup: error: last checkpoint LSN (" LSN_PF
+ ") is larger than last copied LSN (" LSN_PF ").\n",
+ latest_cp, log_copy_scanned_lsn);
+ exit(EXIT_FAILURE);
+ }
+}
+
+/* ================= stats ================= */
+static my_bool
+xtrabackup_stats_level(
+ dict_index_t* index,
+ ulint level)
+{
+ ulint space;
+ page_t* page;
+
+ rec_t* node_ptr;
+
+ ulint right_page_no;
+
+ page_cur_t cursor;
+
+ mtr_t mtr;
+ mem_heap_t* heap = mem_heap_create(256);
+
+ ulint* offsets = NULL;
+
+ ulonglong n_pages, n_pages_extern;
+ ulonglong sum_data, sum_data_extern;
+ ulonglong n_recs;
+ ulint page_size;
+ buf_block_t* block;
+ ulint zip_size;
+
+ n_pages = sum_data = n_recs = 0;
+ n_pages_extern = sum_data_extern = 0;
+
+
+ if (level == 0)
+ fprintf(stdout, " leaf pages: ");
+ else
+ fprintf(stdout, " level %lu pages: ", level);
+
+ mtr_start(&mtr);
+
+ mtr_x_lock(&(index->lock), &mtr);
+ block = btr_root_block_get(index, RW_X_LATCH, &mtr);
+ page = buf_block_get_frame(block);
+
+ space = page_get_space_id(page);
+ zip_size = fil_space_get_zip_size(space);
+
+ while (level != btr_page_get_level(page, &mtr)) {
+
+ ut_a(space == buf_block_get_space(block));
+ ut_a(space == page_get_space_id(page));
+ ut_a(!page_is_leaf(page));
+
+ page_cur_set_before_first(block, &cursor);
+ page_cur_move_to_next(&cursor);
+
+ node_ptr = page_cur_get_rec(&cursor);
+ offsets = rec_get_offsets(node_ptr, index, offsets,
+ ULINT_UNDEFINED, &heap);
+ block = btr_node_ptr_get_child(node_ptr, index, offsets, &mtr);
+ page = buf_block_get_frame(block);
+ }
+
+loop:
+ mem_heap_empty(heap);
+ offsets = NULL;
+ mtr_x_lock(&(index->lock), &mtr);
+
+ right_page_no = btr_page_get_next(page, &mtr);
+
+
+ /*=================================*/
+ //fprintf(stdout, "%lu ", (ulint) buf_frame_get_page_no(page));
+
+ n_pages++;
+ sum_data += page_get_data_size(page);
+ n_recs += page_get_n_recs(page);
+
+
+ if (level == 0) {
+ page_cur_t cur;
+ ulint n_fields;
+ ulint i;
+ mem_heap_t* local_heap = NULL;
+ ulint offsets_[REC_OFFS_NORMAL_SIZE];
+ ulint* local_offsets = offsets_;
+
+ *offsets_ = (sizeof offsets_) / sizeof *offsets_;
+
+ page_cur_set_before_first(block, &cur);
+ page_cur_move_to_next(&cur);
+
+ for (;;) {
+ if (page_cur_is_after_last(&cur)) {
+ break;
+ }
+
+ local_offsets = rec_get_offsets(cur.rec, index, local_offsets,
+ ULINT_UNDEFINED, &local_heap);
+ n_fields = rec_offs_n_fields(local_offsets);
+
+ for (i = 0; i < n_fields; i++) {
+ if (rec_offs_nth_extern(local_offsets, i)) {
+ page_t* local_page;
+ ulint space_id;
+ ulint page_no;
+ ulint offset;
+ byte* blob_header;
+ ulint part_len;
+ mtr_t local_mtr;
+ ulint local_len;
+ byte* data;
+ buf_block_t* local_block;
+
+ data = rec_get_nth_field(cur.rec, local_offsets, i, &local_len);
+
+ ut_a(local_len >= BTR_EXTERN_FIELD_REF_SIZE);
+ local_len -= BTR_EXTERN_FIELD_REF_SIZE;
+
+ space_id = mach_read_from_4(data + local_len + BTR_EXTERN_SPACE_ID);
+ page_no = mach_read_from_4(data + local_len + BTR_EXTERN_PAGE_NO);
+ offset = mach_read_from_4(data + local_len + BTR_EXTERN_OFFSET);
+
+ if (offset != FIL_PAGE_DATA)
+ msg("\nWarning: several record may share same external page.\n");
+
+ for (;;) {
+ mtr_start(&local_mtr);
+
+ local_block = btr_block_get(space_id, zip_size, page_no, RW_S_LATCH, index, &local_mtr);
+ local_page = buf_block_get_frame(local_block);
+ blob_header = local_page + offset;
+#define BTR_BLOB_HDR_PART_LEN 0
+#define BTR_BLOB_HDR_NEXT_PAGE_NO 4
+ //part_len = btr_blob_get_part_len(blob_header);
+ part_len = mach_read_from_4(blob_header + BTR_BLOB_HDR_PART_LEN);
+
+ //page_no = btr_blob_get_next_page_no(blob_header);
+ page_no = mach_read_from_4(blob_header + BTR_BLOB_HDR_NEXT_PAGE_NO);
+
+ offset = FIL_PAGE_DATA;
+
+
+
+
+ /*=================================*/
+ //fprintf(stdout, "[%lu] ", (ulint) buf_frame_get_page_no(page));
+
+ n_pages_extern++;
+ sum_data_extern += part_len;
+
+
+ mtr_commit(&local_mtr);
+
+ if (page_no == FIL_NULL)
+ break;
+ }
+ }
+ }
+
+ page_cur_move_to_next(&cur);
+ }
+ }
+
+
+
+
+ mtr_commit(&mtr);
+ if (right_page_no != FIL_NULL) {
+ mtr_start(&mtr);
+ block = btr_block_get(space, zip_size, right_page_no,
+ RW_X_LATCH, index, &mtr);
+ page = buf_block_get_frame(block);
+ goto loop;
+ }
+ mem_heap_free(heap);
+
+ if (zip_size) {
+ page_size = zip_size;
+ } else {
+ page_size = UNIV_PAGE_SIZE;
+ }
+
+ if (level == 0)
+ fprintf(stdout, "recs=%llu, ", n_recs);
+
+ fprintf(stdout, "pages=%llu, data=%llu bytes, data/pages=%lld%%",
+ n_pages, sum_data,
+ ((sum_data * 100)/ page_size)/n_pages);
+
+
+ if (level == 0 && n_pages_extern) {
+ putc('\n', stdout);
+ /* also scan blob pages*/
+ fprintf(stdout, " external pages: ");
+
+ fprintf(stdout, "pages=%llu, data=%llu bytes, data/pages=%lld%%",
+ n_pages_extern, sum_data_extern,
+ ((sum_data_extern * 100)/ page_size)/n_pages_extern);
+ }
+
+ putc('\n', stdout);
+
+ if (level > 0) {
+ xtrabackup_stats_level(index, level - 1);
+ }
+
+ return(TRUE);
+}
+
+static void
+xtrabackup_stats_func(int argc, char **argv)
+{
+ ulint n;
+
+ /* cd to datadir */
+
+ if (my_setwd(mysql_real_data_home,MYF(MY_WME)))
+ {
+ msg("xtrabackup: cannot my_setwd %s\n", mysql_real_data_home);
+ exit(EXIT_FAILURE);
+ }
+ msg("xtrabackup: cd to %s\n", mysql_real_data_home);
+ encryption_plugin_prepare_init(argc, argv);
+ mysql_data_home= mysql_data_home_buff;
+ mysql_data_home[0]=FN_CURLIB; // all paths are relative from here
+ mysql_data_home[1]=0;
+
+ /* set read only */
+ srv_read_only_mode = TRUE;
+
+ /* initialize components */
+ if(innodb_init_param())
+ exit(EXIT_FAILURE);
+
+ /* Check if the log files have been created, otherwise innodb_init()
+ will crash when called with srv_read_only == TRUE */
+ for (n = 0; n < srv_n_log_files; n++) {
+ char logname[FN_REFLEN];
+ ibool exists;
+ os_file_type_t type;
+
+ snprintf(logname, sizeof(logname), "%s%c%s%lu",
+ srv_log_group_home_dir, SRV_PATH_SEPARATOR,
+ "ib_logfile", (ulong) n);
+ srv_normalize_path_for_win(logname);
+
+ if (!os_file_status(logname, &exists, &type) || !exists ||
+ type != OS_FILE_TYPE_FILE) {
+ msg("xtrabackup: Error: "
+ "Cannot find log file %s.\n", logname);
+ msg("xtrabackup: Error: "
+ "to use the statistics feature, you need a "
+ "clean copy of the database including "
+ "correctly sized log files, so you need to "
+ "execute with --prepare twice to use this "
+ "functionality on a backup.\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ msg("xtrabackup: Starting 'read-only' InnoDB instance to gather "
+ "index statistics.\n"
+ "xtrabackup: Using %lld bytes for buffer pool (set by "
+ "--use-memory parameter)\n", xtrabackup_use_memory);
+
+ if(innodb_init())
+ exit(EXIT_FAILURE);
+
+ xb_filters_init();
+
+ fprintf(stdout, "\n\n<INDEX STATISTICS>\n");
+
+ /* gather stats */
+
+ {
+ dict_table_t* sys_tables;
+ dict_index_t* sys_index;
+ dict_table_t* table;
+ btr_pcur_t pcur;
+ rec_t* rec;
+ byte* field;
+ ulint len;
+ mtr_t mtr;
+
+ /* Enlarge the fatal semaphore wait timeout during the InnoDB table
+ monitor printout */
+
+ os_increment_counter_by_amount(server_mutex,
+ srv_fatal_semaphore_wait_threshold,
+ 72000);
+
+ mutex_enter(&(dict_sys->mutex));
+
+ mtr_start(&mtr);
+
+ sys_tables = dict_table_get_low("SYS_TABLES");
+ sys_index = UT_LIST_GET_FIRST(sys_tables->indexes);
+
+ btr_pcur_open_at_index_side(TRUE, sys_index, BTR_SEARCH_LEAF, &pcur,
+ TRUE, 0, &mtr);
+loop:
+ btr_pcur_move_to_next_user_rec(&pcur, &mtr);
+
+ rec = btr_pcur_get_rec(&pcur);
+
+ if (!btr_pcur_is_on_user_rec(&pcur))
+ {
+ /* end of index */
+
+ btr_pcur_close(&pcur);
+ mtr_commit(&mtr);
+
+ mutex_exit(&(dict_sys->mutex));
+
+ /* Restore the fatal semaphore wait timeout */
+ os_increment_counter_by_amount(server_mutex,
+ srv_fatal_semaphore_wait_threshold,
+ -72000);
+
+ goto end;
+ }
+
+ field = rec_get_nth_field_old(rec, 0, &len);
+
+ if (!rec_get_deleted_flag(rec, 0)) {
+
+ /* We found one */
+
+ char* table_name = mem_strdupl((char*) field, len);
+
+ btr_pcur_store_position(&pcur, &mtr);
+
+ mtr_commit(&mtr);
+
+ table = dict_table_get_low(table_name);
+ mem_free(table_name);
+
+ if (table && check_if_skip_table(table->name))
+ goto skip;
+
+
+ if (table == NULL) {
+ fputs("InnoDB: Failed to load table ", stderr);
+ ut_print_namel(stderr, NULL, TRUE, (char*) field, len);
+ putc('\n', stderr);
+ } else {
+ dict_index_t* index;
+
+ /* The table definition was corrupt if there
+ is no index */
+
+ if (dict_table_get_first_index(table)) {
+ dict_stats_update_transient(table);
+ }
+
+ //dict_table_print_low(table);
+
+ index = UT_LIST_GET_FIRST(table->indexes);
+ while (index != NULL) {
+{
+ ib_int64_t n_vals;
+
+ if (index->n_user_defined_cols > 0) {
+ n_vals = index->stat_n_diff_key_vals[
+ index->n_user_defined_cols];
+ } else {
+ n_vals = index->stat_n_diff_key_vals[1];
+ }
+
+ fprintf(stdout,
+ " table: %s, index: %s, space id: %lu, root page: %lu"
+ ", zip size: %lu"
+ "\n estimated statistics in dictionary:\n"
+ " key vals: %lu, leaf pages: %lu, size pages: %lu\n"
+ " real statistics:\n",
+ table->name, index->name,
+ (ulong) index->space,
+ (ulong) index->page,
+ (ulong) fil_space_get_zip_size(index->space),
+ (ulong) n_vals,
+ (ulong) index->stat_n_leaf_pages,
+ (ulong) index->stat_index_size);
+
+ {
+ mtr_t local_mtr;
+ page_t* root;
+ ulint page_level;
+
+ mtr_start(&local_mtr);
+
+ mtr_x_lock(&(index->lock), &local_mtr);
+ root = btr_root_get(index, &local_mtr);
+ page_level = btr_page_get_level(root, &local_mtr);
+
+ xtrabackup_stats_level(index, page_level);
+
+ mtr_commit(&local_mtr);
+ }
+
+ putc('\n', stdout);
+}
+ index = UT_LIST_GET_NEXT(indexes, index);
+ }
+ }
+
+skip:
+ mtr_start(&mtr);
+
+ btr_pcur_restore_position(BTR_SEARCH_LEAF, &pcur, &mtr);
+ }
+
+ goto loop;
+ }
+
+end:
+ putc('\n', stdout);
+
+ fflush(stdout);
+
+ xb_filters_free();
+
+ /* shutdown InnoDB */
+ if(innodb_end())
+ exit(EXIT_FAILURE);
+}
+
+/* ================= prepare ================= */
+
+static my_bool
+xtrabackup_init_temp_log(void)
+{
+ os_file_t src_file = XB_FILE_UNDEFINED;
+ char src_path[FN_REFLEN];
+ char dst_path[FN_REFLEN];
+ ibool success;
+
+ ulint field;
+ byte* log_buf= (byte *)malloc(UNIV_PAGE_SIZE_MAX * 128); /* 2 MB */
+
+ ib_int64_t file_size;
+
+ lsn_t max_no;
+ lsn_t max_lsn;
+ lsn_t checkpoint_no;
+
+ ulint fold;
+
+ bool checkpoint_found;
+
+ max_no = 0;
+
+ if (!log_buf) {
+ goto error;
+ }
+
+ if (!xb_init_log_block_size()) {
+ goto error;
+ }
+
+ if(!xtrabackup_incremental_dir) {
+ sprintf(dst_path, "%s/ib_logfile0", xtrabackup_target_dir);
+ sprintf(src_path, "%s/%s", xtrabackup_target_dir,
+ XB_LOG_FILENAME);
+ } else {
+ sprintf(dst_path, "%s/ib_logfile0", xtrabackup_incremental_dir);
+ sprintf(src_path, "%s/%s", xtrabackup_incremental_dir,
+ XB_LOG_FILENAME);
+ }
+
+ srv_normalize_path_for_win(dst_path);
+ srv_normalize_path_for_win(src_path);
+retry:
+ src_file = os_file_create_simple_no_error_handling(0, src_path,
+ OS_FILE_OPEN,
+ OS_FILE_READ_WRITE,
+ &success,0);
+ if (!success) {
+ /* The following call prints an error message */
+ os_file_get_last_error(TRUE);
+
+ msg("xtrabackup: Warning: cannot open %s. will try to find.\n",
+ src_path);
+
+ /* check if ib_logfile0 may be xtrabackup_logfile */
+ src_file = os_file_create_simple_no_error_handling(0, dst_path,
+ OS_FILE_OPEN,
+ OS_FILE_READ_WRITE,
+ &success,0);
+ if (!success) {
+ os_file_get_last_error(TRUE);
+ msg(" xtrabackup: Fatal error: cannot find %s.\n",
+ src_path);
+
+ goto error;
+ }
+
+ success = os_file_read(src_file, log_buf, 0,
+ LOG_FILE_HDR_SIZE);
+ if (!success) {
+ goto error;
+ }
+
+ if ( ut_memcmp(log_buf + LOG_FILE_WAS_CREATED_BY_HOT_BACKUP,
+ (byte*)"xtrabkup", (sizeof "xtrabkup") - 1) == 0) {
+ msg(" xtrabackup: 'ib_logfile0' seems to be "
+ "'xtrabackup_logfile'. will retry.\n");
+
+ os_file_close(src_file);
+ src_file = XB_FILE_UNDEFINED;
+
+ /* rename and try again */
+ success = os_file_rename(0, dst_path, src_path);
+ if (!success) {
+ goto error;
+ }
+
+ goto retry;
+ }
+
+ msg(" xtrabackup: Fatal error: cannot find %s.\n",
+ src_path);
+
+ os_file_close(src_file);
+ src_file = XB_FILE_UNDEFINED;
+
+ goto error;
+ }
+
+ file_size = os_file_get_size(src_file);
+
+
+ /* TODO: We should skip the following modifies, if it is not the first time. */
+
+ /* read log file header */
+ success = os_file_read(src_file, log_buf, 0, LOG_FILE_HDR_SIZE);
+ if (!success) {
+ goto error;
+ }
+
+ if ( ut_memcmp(log_buf + LOG_FILE_WAS_CREATED_BY_HOT_BACKUP,
+ (byte*)"xtrabkup", (sizeof "xtrabkup") - 1) != 0 ) {
+ msg("xtrabackup: notice: xtrabackup_logfile was already used "
+ "to '--prepare'.\n");
+ goto skip_modify;
+ } else {
+ /* clear it later */
+ //memset(log_buf + LOG_FILE_WAS_CREATED_BY_HOT_BACKUP,
+ // ' ', 4);
+ }
+
+ checkpoint_found = false;
+
+ /* read last checkpoint lsn */
+ for (field = LOG_CHECKPOINT_1; field <= LOG_CHECKPOINT_2;
+ field += LOG_CHECKPOINT_2 - LOG_CHECKPOINT_1) {
+ if (!recv_check_cp_is_consistent(const_cast<const byte *>
+ (log_buf + field)))
+ goto not_consistent;
+
+ checkpoint_no = mach_read_from_8(log_buf + field +
+ LOG_CHECKPOINT_NO);
+
+ if (checkpoint_no >= max_no) {
+
+ max_no = checkpoint_no;
+ max_lsn = mach_read_from_8(log_buf + field +
+ LOG_CHECKPOINT_LSN);
+ checkpoint_found = true;
+ }
+not_consistent:
+ ;
+ }
+
+ if (!checkpoint_found) {
+ msg("xtrabackup: No valid checkpoint found.\n");
+ goto error;
+ }
+
+
+ /* It seems to be needed to overwrite the both checkpoint area. */
+ mach_write_to_8(log_buf + LOG_CHECKPOINT_1 + LOG_CHECKPOINT_LSN,
+ max_lsn);
+ mach_write_to_4(log_buf + LOG_CHECKPOINT_1
+ + LOG_CHECKPOINT_OFFSET_LOW32,
+ LOG_FILE_HDR_SIZE +
+ (ulint)(max_lsn -
+ ut_uint64_align_down(max_lsn,
+ OS_FILE_LOG_BLOCK_SIZE)));
+ mach_write_to_4(log_buf + LOG_CHECKPOINT_1
+ + LOG_CHECKPOINT_OFFSET_HIGH32, 0);
+ fold = ut_fold_binary(log_buf + LOG_CHECKPOINT_1, LOG_CHECKPOINT_CHECKSUM_1);
+ mach_write_to_4(log_buf + LOG_CHECKPOINT_1 + LOG_CHECKPOINT_CHECKSUM_1, fold);
+
+ fold = ut_fold_binary(log_buf + LOG_CHECKPOINT_1 + LOG_CHECKPOINT_LSN,
+ LOG_CHECKPOINT_CHECKSUM_2 - LOG_CHECKPOINT_LSN);
+ mach_write_to_4(log_buf + LOG_CHECKPOINT_1 + LOG_CHECKPOINT_CHECKSUM_2, fold);
+
+ mach_write_to_8(log_buf + LOG_CHECKPOINT_2 + LOG_CHECKPOINT_LSN,
+ max_lsn);
+ mach_write_to_4(log_buf + LOG_CHECKPOINT_2
+ + LOG_CHECKPOINT_OFFSET_LOW32,
+ LOG_FILE_HDR_SIZE +
+ (ulint)(max_lsn -
+ ut_uint64_align_down(max_lsn,
+ OS_FILE_LOG_BLOCK_SIZE)));
+ mach_write_to_4(log_buf + LOG_CHECKPOINT_2
+ + LOG_CHECKPOINT_OFFSET_HIGH32, 0);
+ fold = ut_fold_binary(log_buf + LOG_CHECKPOINT_2, LOG_CHECKPOINT_CHECKSUM_1);
+ mach_write_to_4(log_buf + LOG_CHECKPOINT_2 + LOG_CHECKPOINT_CHECKSUM_1, fold);
+
+ fold = ut_fold_binary(log_buf + LOG_CHECKPOINT_2 + LOG_CHECKPOINT_LSN,
+ LOG_CHECKPOINT_CHECKSUM_2 - LOG_CHECKPOINT_LSN);
+ mach_write_to_4(log_buf + LOG_CHECKPOINT_2 + LOG_CHECKPOINT_CHECKSUM_2, fold);
+
+
+ success = os_file_write(src_path, src_file, log_buf, 0,
+ LOG_FILE_HDR_SIZE);
+ if (!success) {
+ goto error;
+ }
+
+ /* expand file size (9/8) and align to UNIV_PAGE_SIZE_MAX */
+
+ if (file_size % UNIV_PAGE_SIZE_MAX) {
+ memset(log_buf, 0, UNIV_PAGE_SIZE_MAX);
+ success = os_file_write(src_path, src_file, log_buf,
+ file_size,
+ UNIV_PAGE_SIZE_MAX
+ - (ulint) (file_size
+ % UNIV_PAGE_SIZE_MAX));
+ if (!success) {
+ goto error;
+ }
+
+ file_size = os_file_get_size(src_file);
+ }
+
+ /* TODO: We should judge whether the file is already expanded or not... */
+ {
+ ulint expand;
+
+ memset(log_buf, 0, UNIV_PAGE_SIZE_MAX * 128);
+ expand = (ulint) (file_size / UNIV_PAGE_SIZE_MAX / 8);
+
+ for (; expand > 128; expand -= 128) {
+ success = os_file_write(src_path, src_file, log_buf,
+ file_size,
+ UNIV_PAGE_SIZE_MAX * 128);
+ if (!success) {
+ goto error;
+ }
+ file_size += UNIV_PAGE_SIZE_MAX * 128;
+ }
+
+ if (expand) {
+ success = os_file_write(src_path, src_file, log_buf,
+ file_size,
+ expand * UNIV_PAGE_SIZE_MAX);
+ if (!success) {
+ goto error;
+ }
+ file_size += UNIV_PAGE_SIZE_MAX * expand;
+ }
+ }
+
+ /* make larger than 2MB */
+ if (file_size < 2*1024*1024L) {
+ memset(log_buf, 0, UNIV_PAGE_SIZE_MAX);
+ while (file_size < 2*1024*1024L) {
+ success = os_file_write(src_path, src_file, log_buf,
+ file_size,
+ UNIV_PAGE_SIZE_MAX);
+ if (!success) {
+ goto error;
+ }
+ file_size += UNIV_PAGE_SIZE_MAX;
+ }
+ file_size = os_file_get_size(src_file);
+ }
+
+ msg("xtrabackup: xtrabackup_logfile detected: size=" INT64PF ", "
+ "start_lsn=(" LSN_PF ")\n", file_size, max_lsn);
+
+ os_file_close(src_file);
+ src_file = XB_FILE_UNDEFINED;
+
+ /* fake InnoDB */
+ innobase_log_files_in_group_save = innobase_log_files_in_group;
+ srv_log_group_home_dir_save = srv_log_group_home_dir;
+ innobase_log_file_size_save = innobase_log_file_size;
+
+ srv_log_group_home_dir = NULL;
+ innobase_log_file_size = file_size;
+ innobase_log_files_in_group = 1;
+
+ srv_thread_concurrency = 0;
+
+ /* rename 'xtrabackup_logfile' to 'ib_logfile0' */
+ success = os_file_rename(0, src_path, dst_path);
+ if (!success) {
+ goto error;
+ }
+ xtrabackup_logfile_is_renamed = TRUE;
+ free(log_buf);
+ return(FALSE);
+
+skip_modify:
+ free(log_buf);
+ os_file_close(src_file);
+ src_file = XB_FILE_UNDEFINED;
+ return(FALSE);
+
+error:
+ free(log_buf);
+ if (src_file != XB_FILE_UNDEFINED)
+ os_file_close(src_file);
+ msg("xtrabackup: Error: xtrabackup_init_temp_log() failed.\n");
+ return(TRUE); /*ERROR*/
+}
+
+/***********************************************************************
+Generates path to the meta file path from a given path to an incremental .delta
+by replacing trailing ".delta" with ".meta", or returns error if 'delta_path'
+does not end with the ".delta" character sequence.
+@return TRUE on success, FALSE on error. */
+static
+ibool
+get_meta_path(
+ const char *delta_path, /* in: path to a .delta file */
+ char *meta_path) /* out: path to the corresponding .meta
+ file */
+{
+ size_t len = strlen(delta_path);
+
+ if (len <= 6 || strcmp(delta_path + len - 6, ".delta")) {
+ return FALSE;
+ }
+ memcpy(meta_path, delta_path, len - 6);
+ strcpy(meta_path + len - 6, XB_DELTA_INFO_SUFFIX);
+
+ return TRUE;
+}
+
+/****************************************************************//**
+Create a new tablespace on disk and return the handle to its opened
+file. Code adopted from fil_create_new_single_table_tablespace with
+the main difference that only disk file is created without updating
+the InnoDB in-memory dictionary data structures.
+
+@return TRUE on success, FALSE on error. */
+static
+ibool
+xb_space_create_file(
+/*==================*/
+ const char* path, /*!<in: path to tablespace */
+ ulint space_id, /*!<in: space id */
+ ulint flags __attribute__((unused)),/*!<in: tablespace
+ flags */
+ os_file_t* file) /*!<out: file handle */
+{
+ ibool ret;
+ byte* buf;
+ byte* page;
+
+ *file = os_file_create_simple_no_error_handling(0, path, OS_FILE_CREATE,
+ OS_FILE_READ_WRITE,
+ &ret,0);
+ if (!ret) {
+ msg("xtrabackup: cannot create file %s\n", path);
+ return ret;
+ }
+
+ ret = os_file_set_size(path, *file,
+ FIL_IBD_FILE_INITIAL_SIZE * UNIV_PAGE_SIZE);
+ if (!ret) {
+ msg("xtrabackup: cannot set size for file %s\n", path);
+ os_file_close(*file);
+ os_file_delete(0, path);
+ return ret;
+ }
+
+ buf = static_cast<byte *>(ut_malloc(3 * UNIV_PAGE_SIZE));
+ /* Align the memory for file i/o if we might have O_DIRECT set */
+ page = static_cast<byte *>(ut_align(buf, UNIV_PAGE_SIZE));
+
+ memset(page, '\0', UNIV_PAGE_SIZE);
+
+ fsp_header_init_fields(page, space_id, flags);
+ mach_write_to_4(page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID, space_id);
+
+ if (!fsp_flags_is_compressed(flags)) {
+ buf_flush_init_for_writing(page, NULL, 0);
+
+ ret = os_file_write(path, *file, page, 0, UNIV_PAGE_SIZE);
+ }
+ else {
+ page_zip_des_t page_zip;
+ ulint zip_size;
+
+ zip_size = fsp_flags_get_zip_size(flags);
+ page_zip_set_size(&page_zip, zip_size);
+ page_zip.data = page + UNIV_PAGE_SIZE;
+ fprintf(stderr, "zip_size = %lu\n", zip_size);
+
+#ifdef UNIV_DEBUG
+ page_zip.m_start =
+#endif /* UNIV_DEBUG */
+ page_zip.m_end = page_zip.m_nonempty =
+ page_zip.n_blobs = 0;
+
+ buf_flush_init_for_writing(page, &page_zip, 0);
+
+ ret = os_file_write(path, *file, page_zip.data, 0,
+ zip_size);
+ }
+
+ ut_free(buf);
+
+ if (!ret) {
+ msg("xtrabackup: could not write the first page to %s\n",
+ path);
+ os_file_close(*file);
+ os_file_delete(0, path);
+ return ret;
+ }
+
+ return TRUE;
+}
+
+/***********************************************************************
+Searches for matching tablespace file for given .delta file and space_id
+in given directory. When matching tablespace found, renames it to match the
+name of .delta file. If there was a tablespace with matching name and
+mismatching ID, renames it to xtrabackup_tmp_#ID.ibd. If there was no
+matching file, creates a new tablespace.
+@return file handle of matched or created file */
+static
+os_file_t
+xb_delta_open_matching_space(
+ const char* dbname, /* in: path to destination database dir */
+ const char* name, /* in: name of delta file (without .delta) */
+ ulint space_id, /* in: space id of delta file */
+ ulint zip_size, /* in: zip_size of tablespace */
+ char* real_name, /* out: full path of destination file */
+ size_t real_name_len, /* out: buffer size for real_name */
+ ibool* success) /* out: indicates error. TRUE = success */
+{
+ char dest_dir[FN_REFLEN];
+ char dest_space_name[FN_REFLEN];
+ ibool ok;
+ fil_space_t* fil_space;
+ os_file_t file = 0;
+ ulint tablespace_flags;
+ xb_filter_entry_t* table;
+
+ ut_a(dbname != NULL ||
+ !fil_is_user_tablespace_id(space_id) ||
+ space_id == ULINT_UNDEFINED);
+
+ *success = FALSE;
+
+ if (dbname) {
+ snprintf(dest_dir, FN_REFLEN, "%s/%s",
+ xtrabackup_target_dir, dbname);
+ srv_normalize_path_for_win(dest_dir);
+
+ snprintf(dest_space_name, FN_REFLEN, "%s/%s", dbname, name);
+ } else {
+ snprintf(dest_dir, FN_REFLEN, "%s", xtrabackup_target_dir);
+ srv_normalize_path_for_win(dest_dir);
+
+ snprintf(dest_space_name, FN_REFLEN, "%s", name);
+ }
+
+ snprintf(real_name, real_name_len,
+ "%s/%s",
+ xtrabackup_target_dir, dest_space_name);
+ srv_normalize_path_for_win(real_name);
+ /* Truncate ".ibd" */
+ dest_space_name[strlen(dest_space_name) - 4] = '\0';
+
+ /* Create the database directory if it doesn't exist yet */
+ if (!os_file_create_directory(dest_dir, FALSE)) {
+ msg("xtrabackup: error: cannot create dir %s\n", dest_dir);
+ return file;
+ }
+
+ if (!fil_is_user_tablespace_id(space_id)) {
+ goto found;
+ }
+
+ /* remember space name for further reference */
+ table = static_cast<xb_filter_entry_t *>
+ (ut_malloc(sizeof(xb_filter_entry_t) +
+ strlen(dest_space_name) + 1));
+
+ table->name = ((char*)table) + sizeof(xb_filter_entry_t);
+ strcpy(table->name, dest_space_name);
+ HASH_INSERT(xb_filter_entry_t, name_hash, inc_dir_tables_hash,
+ ut_fold_string(table->name), table);
+
+ mutex_enter(&fil_system->mutex);
+ fil_space = fil_space_get_by_name(dest_space_name);
+ mutex_exit(&fil_system->mutex);
+
+ if (fil_space != NULL) {
+ if (fil_space->id == space_id || space_id == ULINT_UNDEFINED) {
+ /* we found matching space */
+ goto found;
+ } else {
+
+ char tmpname[FN_REFLEN];
+
+ snprintf(tmpname, FN_REFLEN, "%s/xtrabackup_tmp_#%lu",
+ dbname, fil_space->id);
+
+ msg("xtrabackup: Renaming %s to %s.ibd\n",
+ fil_space->name, tmpname);
+
+ if (!fil_rename_tablespace(NULL, fil_space->id,
+ tmpname, NULL))
+ {
+ msg("xtrabackup: Cannot rename %s to %s\n",
+ fil_space->name, tmpname);
+ goto exit;
+ }
+ }
+ }
+
+ if (space_id == ULINT_UNDEFINED)
+ {
+ msg("xtrabackup: Error: Cannot handle DDL operation on tablespace "
+ "%s\n", dest_space_name);
+ exit(EXIT_FAILURE);
+ }
+ mutex_enter(&fil_system->mutex);
+ fil_space = fil_space_get_by_id(space_id);
+ mutex_exit(&fil_system->mutex);
+ if (fil_space != NULL) {
+ char tmpname[FN_REFLEN];
+
+ strncpy(tmpname, dest_space_name, FN_REFLEN);
+
+ msg("xtrabackup: Renaming %s to %s\n",
+ fil_space->name, dest_space_name);
+
+ if (!fil_rename_tablespace(NULL, fil_space->id, tmpname,
+ NULL))
+ {
+ msg("xtrabackup: Cannot rename %s to %s\n",
+ fil_space->name, dest_space_name);
+ goto exit;
+ }
+
+ goto found;
+ }
+
+ /* No matching space found. create the new one. */
+
+ if (!fil_space_create(dest_space_name, space_id, 0,
+ FIL_TABLESPACE, 0, false)) {
+ msg("xtrabackup: Cannot create tablespace %s\n",
+ dest_space_name);
+ goto exit;
+ }
+
+ /* Calculate correct tablespace flags for compressed tablespaces. */
+ if (!zip_size || zip_size == ULINT_UNDEFINED) {
+ tablespace_flags = 0;
+ }
+ else {
+ tablespace_flags
+ = (get_bit_shift(zip_size >> PAGE_ZIP_MIN_SIZE_SHIFT
+ << 1)
+ << DICT_TF_ZSSIZE_SHIFT)
+ | DICT_TF_COMPACT
+ | (DICT_TF_FORMAT_ZIP << DICT_TF_FORMAT_SHIFT);
+ ut_a(dict_tf_get_zip_size(tablespace_flags)
+ == zip_size);
+ }
+ *success = xb_space_create_file(real_name, space_id, tablespace_flags,
+ &file);
+ goto exit;
+
+found:
+ /* open the file and return it's handle */
+
+ file = os_file_create_simple_no_error_handling(0, real_name,
+ OS_FILE_OPEN,
+ OS_FILE_READ_WRITE,
+ &ok,0);
+
+ if (ok) {
+ *success = TRUE;
+ } else {
+ msg("xtrabackup: Cannot open file %s\n", real_name);
+ }
+
+exit:
+
+ return file;
+}
+
+/************************************************************************
+Applies a given .delta file to the corresponding data file.
+@return TRUE on success */
+static
+ibool
+xtrabackup_apply_delta(
+ const char* dirname, /* in: dir name of incremental */
+ const char* dbname, /* in: database name (ibdata: NULL) */
+ const char* filename, /* in: file name (not a path),
+ including the .delta extension */
+ void* /*data*/)
+{
+ os_file_t src_file = XB_FILE_UNDEFINED;
+ os_file_t dst_file = XB_FILE_UNDEFINED;
+ char src_path[FN_REFLEN];
+ char dst_path[FN_REFLEN];
+ char meta_path[FN_REFLEN];
+ char space_name[FN_REFLEN];
+ ibool success;
+
+ ibool last_buffer = FALSE;
+ ulint page_in_buffer;
+ ulint incremental_buffers = 0;
+
+ xb_delta_info_t info;
+ ulint page_size;
+ ulint page_size_shift;
+ byte* incremental_buffer_base = NULL;
+ byte* incremental_buffer;
+
+ size_t offset;
+
+ ut_a(xtrabackup_incremental);
+
+ if (dbname) {
+ snprintf(src_path, sizeof(src_path), "%s/%s/%s",
+ dirname, dbname, filename);
+ snprintf(dst_path, sizeof(dst_path), "%s/%s/%s",
+ xtrabackup_real_target_dir, dbname, filename);
+ } else {
+ snprintf(src_path, sizeof(src_path), "%s/%s",
+ dirname, filename);
+ snprintf(dst_path, sizeof(dst_path), "%s/%s",
+ xtrabackup_real_target_dir, filename);
+ }
+ dst_path[strlen(dst_path) - 6] = '\0';
+
+ strncpy(space_name, filename, FN_REFLEN);
+ space_name[strlen(space_name) - 6] = 0;
+
+ if (!get_meta_path(src_path, meta_path)) {
+ goto error;
+ }
+
+ srv_normalize_path_for_win(dst_path);
+ srv_normalize_path_for_win(src_path);
+ srv_normalize_path_for_win(meta_path);
+
+ if (!xb_read_delta_metadata(meta_path, &info)) {
+ goto error;
+ }
+
+ page_size = info.page_size;
+ page_size_shift = get_bit_shift(page_size);
+ msg("xtrabackup: page size for %s is %lu bytes\n",
+ src_path, page_size);
+ if (page_size_shift < 10 ||
+ page_size_shift > UNIV_PAGE_SIZE_SHIFT_MAX) {
+ msg("xtrabackup: error: invalid value of page_size "
+ "(%lu bytes) read from %s\n", page_size, meta_path);
+ goto error;
+ }
+
+ src_file = os_file_create_simple_no_error_handling(0, src_path,
+ OS_FILE_OPEN,
+ OS_FILE_READ_WRITE,
+ &success,0);
+ if (!success) {
+ os_file_get_last_error(TRUE);
+ msg("xtrabackup: error: cannot open %s\n", src_path);
+ goto error;
+ }
+
+ posix_fadvise(src_file, 0, 0, POSIX_FADV_SEQUENTIAL);
+
+ os_file_set_nocache(src_file, src_path, "OPEN");
+
+ dst_file = xb_delta_open_matching_space(
+ dbname, space_name, info.space_id, info.zip_size,
+ dst_path, sizeof(dst_path), &success);
+ if (!success) {
+ msg("xtrabackup: error: cannot open %s\n", dst_path);
+ goto error;
+ }
+
+ posix_fadvise(dst_file, 0, 0, POSIX_FADV_DONTNEED);
+
+ os_file_set_nocache(dst_file, dst_path, "OPEN");
+
+ /* allocate buffer for incremental backup (4096 pages) */
+ incremental_buffer_base = static_cast<byte *>
+ (ut_malloc((page_size / 4 + 1) *
+ page_size));
+ incremental_buffer = static_cast<byte *>
+ (ut_align(incremental_buffer_base,
+ page_size));
+
+ msg("Applying %s to %s...\n", src_path, dst_path);
+
+ while (!last_buffer) {
+ ulint cluster_header;
+
+ /* read to buffer */
+ /* first block of block cluster */
+ offset = ((incremental_buffers * (page_size / 4))
+ << page_size_shift);
+ success = os_file_read(src_file, incremental_buffer,
+ offset, page_size);
+ if (!success) {
+ goto error;
+ }
+
+ cluster_header = mach_read_from_4(incremental_buffer);
+ switch(cluster_header) {
+ case 0x78747261UL: /*"xtra"*/
+ break;
+ case 0x58545241UL: /*"XTRA"*/
+ last_buffer = TRUE;
+ break;
+ default:
+ msg("xtrabackup: error: %s seems not "
+ ".delta file.\n", src_path);
+ goto error;
+ }
+
+ for (page_in_buffer = 1; page_in_buffer < page_size / 4;
+ page_in_buffer++) {
+ if (mach_read_from_4(incremental_buffer + page_in_buffer * 4)
+ == 0xFFFFFFFFUL)
+ break;
+ }
+
+ ut_a(last_buffer || page_in_buffer == page_size / 4);
+
+ /* read whole of the cluster */
+ success = os_file_read(src_file, incremental_buffer,
+ offset, page_in_buffer * page_size);
+ if (!success) {
+ goto error;
+ }
+
+ posix_fadvise(src_file, offset, page_in_buffer * page_size,
+ POSIX_FADV_DONTNEED);
+
+ for (page_in_buffer = 1; page_in_buffer < page_size / 4;
+ page_in_buffer++) {
+ ulint offset_on_page;
+
+ offset_on_page = mach_read_from_4(incremental_buffer + page_in_buffer * 4);
+
+ if (offset_on_page == 0xFFFFFFFFUL)
+ break;
+
+ success = os_file_write(dst_path, dst_file,
+ incremental_buffer +
+ page_in_buffer * page_size,
+ (offset_on_page <<
+ page_size_shift),
+ page_size);
+ if (!success) {
+ goto error;
+ }
+ }
+
+ incremental_buffers++;
+ }
+
+ if (incremental_buffer_base)
+ ut_free(incremental_buffer_base);
+ if (src_file != XB_FILE_UNDEFINED)
+ os_file_close(src_file);
+ if (dst_file != XB_FILE_UNDEFINED)
+ os_file_close(dst_file);
+ return TRUE;
+
+error:
+ if (incremental_buffer_base)
+ ut_free(incremental_buffer_base);
+ if (src_file != XB_FILE_UNDEFINED)
+ os_file_close(src_file);
+ if (dst_file != XB_FILE_UNDEFINED)
+ os_file_close(dst_file);
+ msg("xtrabackup: Error: xtrabackup_apply_delta(): "
+ "failed to apply %s to %s.\n", src_path, dst_path);
+ return FALSE;
+}
+
+/************************************************************************
+Callback to handle datadir entry. Function of this type will be called
+for each entry which matches the mask by xb_process_datadir.
+@return should return TRUE on success */
+typedef ibool (*handle_datadir_entry_func_t)(
+/*=========================================*/
+ const char* data_home_dir, /*!<in: path to datadir */
+ const char* db_name, /*!<in: database name */
+ const char* file_name, /*!<in: file name with suffix */
+ void* arg); /*!<in: caller-provided data */
+
+/************************************************************************
+Callback to handle datadir entry. Deletes entry if it has no matching
+fil_space in fil_system directory.
+@return FALSE if delete attempt was unsuccessful */
+static
+ibool
+rm_if_not_found(
+ const char* data_home_dir, /*!<in: path to datadir */
+ const char* db_name, /*!<in: database name */
+ const char* file_name, /*!<in: file name with suffix */
+ void* arg __attribute__((unused)))
+{
+ char name[FN_REFLEN];
+ xb_filter_entry_t* table;
+
+ snprintf(name, FN_REFLEN, "%s/%s", db_name, file_name);
+ /* Truncate ".ibd" */
+ name[strlen(name) - 4] = '\0';
+
+ HASH_SEARCH(name_hash, inc_dir_tables_hash, ut_fold_string(name),
+ xb_filter_entry_t*,
+ table, (void) 0,
+ !strcmp(table->name, name));
+
+ if (!table) {
+ snprintf(name, FN_REFLEN, "%s/%s/%s", data_home_dir,
+ db_name, file_name);
+ return os_file_delete(0, name);
+ }
+
+ return(TRUE);
+}
+
+/************************************************************************
+Function enumerates files in datadir (provided by path) which are matched
+by provided suffix. For each entry callback is called.
+@return FALSE if callback for some entry returned FALSE */
+static
+ibool
+xb_process_datadir(
+ const char* path, /*!<in: datadir path */
+ const char* suffix, /*!<in: suffix to match
+ against */
+ handle_datadir_entry_func_t func, /*!<in: callback */
+ void* data) /*!<in: additional argument for
+ callback */
+{
+ ulint ret;
+ char dbpath[FN_REFLEN];
+ os_file_dir_t dir;
+ os_file_dir_t dbdir;
+ os_file_stat_t dbinfo;
+ os_file_stat_t fileinfo;
+ ulint suffix_len;
+ dberr_t err = DB_SUCCESS;
+ static char current_dir[2];
+
+ current_dir[0] = FN_CURLIB;
+ current_dir[1] = 0;
+ srv_data_home = current_dir;
+
+ suffix_len = strlen(suffix);
+
+ /* datafile */
+ dbdir = os_file_opendir(path, FALSE);
+
+ if (dbdir != NULL) {
+ ret = fil_file_readdir_next_file(&err, path, dbdir,
+ &fileinfo);
+ while (ret == 0) {
+ if (fileinfo.type == OS_FILE_TYPE_DIR) {
+ goto next_file_item_1;
+ }
+
+ if (strlen(fileinfo.name) > suffix_len
+ && 0 == strcmp(fileinfo.name +
+ strlen(fileinfo.name) - suffix_len,
+ suffix)) {
+ if (!func(
+ path, NULL,
+ fileinfo.name, data))
+ {
+ return(FALSE);
+ }
+ }
+next_file_item_1:
+ ret = fil_file_readdir_next_file(&err,
+ path, dbdir,
+ &fileinfo);
+ }
+
+ os_file_closedir(dbdir);
+ } else {
+ msg("xtrabackup: Cannot open dir %s\n",
+ path);
+ }
+
+ /* single table tablespaces */
+ dir = os_file_opendir(path, FALSE);
+
+ if (dir == NULL) {
+ msg("xtrabackup: Cannot open dir %s\n",
+ path);
+ }
+
+ ret = fil_file_readdir_next_file(&err, path, dir,
+ &dbinfo);
+ while (ret == 0) {
+ if (dbinfo.type == OS_FILE_TYPE_FILE
+ || dbinfo.type == OS_FILE_TYPE_UNKNOWN) {
+
+ goto next_datadir_item;
+ }
+
+ sprintf(dbpath, "%s/%s", path,
+ dbinfo.name);
+ srv_normalize_path_for_win(dbpath);
+
+ dbdir = os_file_opendir(dbpath, FALSE);
+
+ if (dbdir != NULL) {
+
+ ret = fil_file_readdir_next_file(&err, dbpath, dbdir,
+ &fileinfo);
+ while (ret == 0) {
+
+ if (fileinfo.type == OS_FILE_TYPE_DIR) {
+
+ goto next_file_item_2;
+ }
+
+ if (strlen(fileinfo.name) > suffix_len
+ && 0 == strcmp(fileinfo.name +
+ strlen(fileinfo.name) -
+ suffix_len,
+ suffix)) {
+ /* The name ends in suffix; process
+ the file */
+ if (!func(
+ path,
+ dbinfo.name,
+ fileinfo.name, data))
+ {
+ return(FALSE);
+ }
+ }
+next_file_item_2:
+ ret = fil_file_readdir_next_file(&err,
+ dbpath, dbdir,
+ &fileinfo);
+ }
+
+ os_file_closedir(dbdir);
+ }
+next_datadir_item:
+ ret = fil_file_readdir_next_file(&err,
+ path,
+ dir, &dbinfo);
+ }
+
+ os_file_closedir(dir);
+
+ return(TRUE);
+}
+
+/************************************************************************
+Applies all .delta files from incremental_dir to the full backup.
+@return TRUE on success. */
+static
+ibool
+xtrabackup_apply_deltas()
+{
+ return xb_process_datadir(xtrabackup_incremental_dir, ".delta",
+ xtrabackup_apply_delta, NULL);
+}
+
+static my_bool
+xtrabackup_close_temp_log(my_bool clear_flag)
+{
+ os_file_t src_file = XB_FILE_UNDEFINED;
+ char src_path[FN_REFLEN];
+ char dst_path[FN_REFLEN];
+ ibool success;
+ byte log_buf[UNIV_PAGE_SIZE_MAX];
+
+ if (!xtrabackup_logfile_is_renamed)
+ return(FALSE);
+
+ /* rename 'ib_logfile0' to 'xtrabackup_logfile' */
+ if(!xtrabackup_incremental_dir) {
+ sprintf(dst_path, "%s/ib_logfile0", xtrabackup_target_dir);
+ sprintf(src_path, "%s/%s", xtrabackup_target_dir,
+ XB_LOG_FILENAME);
+ } else {
+ sprintf(dst_path, "%s/ib_logfile0", xtrabackup_incremental_dir);
+ sprintf(src_path, "%s/%s", xtrabackup_incremental_dir,
+ XB_LOG_FILENAME);
+ }
+
+ srv_normalize_path_for_win(dst_path);
+ srv_normalize_path_for_win(src_path);
+
+ success = os_file_rename(0, dst_path, src_path);
+ if (!success) {
+ goto error;
+ }
+ xtrabackup_logfile_is_renamed = FALSE;
+
+ if (!clear_flag)
+ return(FALSE);
+
+ /* clear LOG_FILE_WAS_CREATED_BY_HOT_BACKUP field */
+ src_file = os_file_create_simple_no_error_handling(0, src_path,
+ OS_FILE_OPEN,
+ OS_FILE_READ_WRITE,
+ &success,0);
+ if (!success) {
+ goto error;
+ }
+
+ success = os_file_read(src_file, log_buf, 0, LOG_FILE_HDR_SIZE);
+ if (!success) {
+ goto error;
+ }
+
+ memset(log_buf + LOG_FILE_WAS_CREATED_BY_HOT_BACKUP, ' ', 4);
+
+ success = os_file_write(src_path, src_file, log_buf, 0,
+ LOG_FILE_HDR_SIZE);
+ if (!success) {
+ goto error;
+ }
+
+ os_file_close(src_file);
+ src_file = XB_FILE_UNDEFINED;
+
+ innobase_log_files_in_group = innobase_log_files_in_group_save;
+ srv_log_group_home_dir = srv_log_group_home_dir_save;
+ innobase_log_file_size = innobase_log_file_size_save;
+
+ return(FALSE);
+error:
+ if (src_file != XB_FILE_UNDEFINED)
+ os_file_close(src_file);
+ msg("xtrabackup: Error: xtrabackup_close_temp_log() failed.\n");
+ return(TRUE); /*ERROR*/
+}
+
+
+/*********************************************************************//**
+Write the meta data (index user fields) config file.
+@return true in case of success otherwise false. */
+static
+bool
+xb_export_cfg_write_index_fields(
+/*===========================*/
+ const dict_index_t* index, /*!< in: write the meta data for
+ this index */
+ FILE* file) /*!< in: file to write to */
+{
+ byte row[sizeof(ib_uint32_t) * 2];
+
+ for (ulint i = 0; i < index->n_fields; ++i) {
+ byte* ptr = row;
+ const dict_field_t* field = &index->fields[i];
+
+ mach_write_to_4(ptr, field->prefix_len);
+ ptr += sizeof(ib_uint32_t);
+
+ mach_write_to_4(ptr, field->fixed_len);
+
+ if (fwrite(row, 1, sizeof(row), file) != sizeof(row)) {
+
+ msg("xtrabackup: Error: writing index fields.");
+
+ return(false);
+ }
+
+ /* Include the NUL byte in the length. */
+ ib_uint32_t len = (ib_uint32_t)strlen(field->name) + 1;
+ ut_a(len > 1);
+
+ mach_write_to_4(row, len);
+
+ if (fwrite(row, 1, sizeof(len), file) != sizeof(len)
+ || fwrite(field->name, 1, len, file) != len) {
+
+ msg("xtrabackup: Error: writing index column.");
+
+ return(false);
+ }
+ }
+
+ return(true);
+}
+
+/*********************************************************************//**
+Write the meta data config file index information.
+@return true in case of success otherwise false. */
+static __attribute__((nonnull, warn_unused_result))
+bool
+xb_export_cfg_write_indexes(
+/*======================*/
+ const dict_table_t* table, /*!< in: write the meta data for
+ this table */
+ FILE* file) /*!< in: file to write to */
+{
+ {
+ byte row[sizeof(ib_uint32_t)];
+
+ /* Write the number of indexes in the table. */
+ mach_write_to_4(row, UT_LIST_GET_LEN(table->indexes));
+
+ if (fwrite(row, 1, sizeof(row), file) != sizeof(row)) {
+ msg("xtrabackup: Error: writing index count.");
+
+ return(false);
+ }
+ }
+
+ bool ret = true;
+
+ /* Write the index meta data. */
+ for (const dict_index_t* index = UT_LIST_GET_FIRST(table->indexes);
+ index != 0 && ret;
+ index = UT_LIST_GET_NEXT(indexes, index)) {
+
+ byte* ptr;
+ byte row[sizeof(ib_uint64_t)
+ + sizeof(ib_uint32_t) * 8];
+
+ ptr = row;
+
+ ut_ad(sizeof(ib_uint64_t) == 8);
+ mach_write_to_8(ptr, index->id);
+ ptr += sizeof(ib_uint64_t);
+
+ mach_write_to_4(ptr, index->space);
+ ptr += sizeof(ib_uint32_t);
+
+ mach_write_to_4(ptr, index->page);
+ ptr += sizeof(ib_uint32_t);
+
+ mach_write_to_4(ptr, index->type);
+ ptr += sizeof(ib_uint32_t);
+
+ mach_write_to_4(ptr, index->trx_id_offset);
+ ptr += sizeof(ib_uint32_t);
+
+ mach_write_to_4(ptr, index->n_user_defined_cols);
+ ptr += sizeof(ib_uint32_t);
+
+ mach_write_to_4(ptr, index->n_uniq);
+ ptr += sizeof(ib_uint32_t);
+
+ mach_write_to_4(ptr, index->n_nullable);
+ ptr += sizeof(ib_uint32_t);
+
+ mach_write_to_4(ptr, index->n_fields);
+
+ if (fwrite(row, 1, sizeof(row), file) != sizeof(row)) {
+
+ msg("xtrabackup: Error: writing index meta-data.");
+
+ return(false);
+ }
+
+ /* Write the length of the index name.
+ NUL byte is included in the length. */
+ ib_uint32_t len = (ib_uint32_t)strlen(index->name) + 1;
+ ut_a(len > 1);
+
+ mach_write_to_4(row, len);
+
+ if (fwrite(row, 1, sizeof(len), file) != sizeof(len)
+ || fwrite(index->name, 1, len, file) != len) {
+
+ msg("xtrabackup: Error: writing index name.");
+
+ return(false);
+ }
+
+ ret = xb_export_cfg_write_index_fields(index, file);
+ }
+
+ return(ret);
+}
+
+/*********************************************************************//**
+Write the meta data (table columns) config file. Serialise the contents of
+dict_col_t structure, along with the column name. All fields are serialized
+as ib_uint32_t.
+@return true in case of success otherwise false. */
+static __attribute__((nonnull, warn_unused_result))
+bool
+xb_export_cfg_write_table(
+/*====================*/
+ const dict_table_t* table, /*!< in: write the meta data for
+ this table */
+ FILE* file) /*!< in: file to write to */
+{
+ dict_col_t* col;
+ byte row[sizeof(ib_uint32_t) * 7];
+
+ col = table->cols;
+
+ for (ulint i = 0; i < table->n_cols; ++i, ++col) {
+ byte* ptr = row;
+
+ mach_write_to_4(ptr, col->prtype);
+ ptr += sizeof(ib_uint32_t);
+
+ mach_write_to_4(ptr, col->mtype);
+ ptr += sizeof(ib_uint32_t);
+
+ mach_write_to_4(ptr, col->len);
+ ptr += sizeof(ib_uint32_t);
+
+ mach_write_to_4(ptr, col->mbminmaxlen);
+ ptr += sizeof(ib_uint32_t);
+
+ mach_write_to_4(ptr, col->ind);
+ ptr += sizeof(ib_uint32_t);
+
+ mach_write_to_4(ptr, col->ord_part);
+ ptr += sizeof(ib_uint32_t);
+
+ mach_write_to_4(ptr, col->max_prefix);
+
+ if (fwrite(row, 1, sizeof(row), file) != sizeof(row)) {
+ msg("xtrabackup: Error: writing table column data.");
+
+ return(false);
+ }
+
+ /* Write out the column name as [len, byte array]. The len
+ includes the NUL byte. */
+ ib_uint32_t len;
+ const char* col_name;
+
+ col_name = dict_table_get_col_name(table, dict_col_get_no(col));
+
+ /* Include the NUL byte in the length. */
+ len = (ib_uint32_t)strlen(col_name) + 1;
+ ut_a(len > 1);
+
+ mach_write_to_4(row, len);
+
+ if (fwrite(row, 1, sizeof(len), file) != sizeof(len)
+ || fwrite(col_name, 1, len, file) != len) {
+
+ msg("xtrabackup: Error: writing column name.");
+
+ return(false);
+ }
+ }
+
+ return(true);
+}
+
+/*********************************************************************//**
+Write the meta data config file header.
+@return true in case of success otherwise false. */
+static __attribute__((nonnull, warn_unused_result))
+bool
+xb_export_cfg_write_header(
+/*=====================*/
+ const dict_table_t* table, /*!< in: write the meta data for
+ this table */
+ FILE* file) /*!< in: file to write to */
+{
+ byte value[sizeof(ib_uint32_t)];
+
+ /* Write the meta-data version number. */
+ mach_write_to_4(value, IB_EXPORT_CFG_VERSION_V1);
+
+ if (fwrite(&value, 1, sizeof(value), file) != sizeof(value)) {
+ msg("xtrabackup: Error: writing meta-data version number.");
+
+ return(false);
+ }
+
+ /* Write the server hostname. */
+ ib_uint32_t len;
+ const char* hostname = "Hostname unknown";
+
+ /* The server hostname includes the NUL byte. */
+ len = (ib_uint32_t)strlen(hostname) + 1;
+ mach_write_to_4(value, len);
+
+ if (fwrite(&value, 1, sizeof(value), file) != sizeof(value)
+ || fwrite(hostname, 1, len, file) != len) {
+
+ msg("xtrabackup: Error: writing hostname.");
+
+ return(false);
+ }
+
+ /* The table name includes the NUL byte. */
+ ut_a(table->name != 0);
+ len = (ib_uint32_t)strlen(table->name) + 1;
+
+ /* Write the table name. */
+ mach_write_to_4(value, len);
+
+ if (fwrite(&value, 1, sizeof(value), file) != sizeof(value)
+ || fwrite(table->name, 1, len, file) != len) {
+
+ msg("xtrabackup: Error: writing table name.");
+
+ return(false);
+ }
+
+ byte row[sizeof(ib_uint32_t) * 3];
+
+ /* Write the next autoinc value. */
+ mach_write_to_8(row, table->autoinc);
+
+ if (fwrite(row, 1, sizeof(ib_uint64_t), file) != sizeof(ib_uint64_t)) {
+ msg("xtrabackup: Error: writing table autoinc value.");
+
+ return(false);
+ }
+
+ byte* ptr = row;
+
+ /* Write the system page size. */
+ mach_write_to_4(ptr, UNIV_PAGE_SIZE);
+ ptr += sizeof(ib_uint32_t);
+
+ /* Write the table->flags. */
+ mach_write_to_4(ptr, table->flags);
+ ptr += sizeof(ib_uint32_t);
+
+ /* Write the number of columns in the table. */
+ mach_write_to_4(ptr, table->n_cols);
+
+ if (fwrite(row, 1, sizeof(row), file) != sizeof(row)) {
+ msg("xtrabackup: Error: writing table meta-data.");
+
+ return(false);
+ }
+
+ return(true);
+}
+
+/*********************************************************************//**
+Write MySQL 5.6-style meta data config file.
+@return true in case of success otherwise false. */
+static
+bool
+xb_export_cfg_write(
+ const fil_node_t* node,
+ const dict_table_t* table) /*!< in: write the meta data for
+ this table */
+{
+ char file_path[FN_REFLEN];
+ FILE* file;
+ bool success;
+
+ strcpy(file_path, node->name);
+ strcpy(file_path + strlen(file_path) - 4, ".cfg");
+
+ file = fopen(file_path, "w+b");
+
+ if (file == NULL) {
+ msg("xtrabackup: Error: cannot open %s\n", node->name);
+
+ success = false;
+ } else {
+
+ success = xb_export_cfg_write_header(table, file);
+
+ if (success) {
+ success = xb_export_cfg_write_table(table, file);
+ }
+
+ if (success) {
+ success = xb_export_cfg_write_indexes(table, file);
+ }
+
+ if (fclose(file) != 0) {
+ msg("xtrabackup: Error: cannot close %s\n", node->name);
+ success = false;
+ }
+
+ }
+
+ return(success);
+
+}
+
+/********************************************************************//**
+Searches archived log files in archived log directory. The min and max
+LSN's of found files as well as archived log file size are stored in
+xtrabackup_arch_first_file_lsn, xtrabackup_arch_last_file_lsn and
+xtrabackup_arch_file_size respectively.
+@return true on success
+*/
+static
+bool
+xtrabackup_arch_search_files(
+/*=========================*/
+ ib_uint64_t start_lsn) /*!< in: filter out log files
+ witch does not contain data
+ with lsn < start_lsn */
+{
+ os_file_dir_t dir;
+ os_file_stat_t fileinfo;
+ ut_ad(innobase_log_arch_dir);
+
+ dir = os_file_opendir(innobase_log_arch_dir, FALSE);
+ if (!dir) {
+ msg("xtrabackup: error: cannot open archived log directory %s\n",
+ innobase_log_arch_dir);
+ return false;
+ }
+
+ while(!os_file_readdir_next_file(innobase_log_arch_dir,
+ dir,
+ &fileinfo) ) {
+ lsn_t log_file_lsn;
+ char* log_str_end_lsn_ptr;
+
+ if (strncmp(fileinfo.name,
+ IB_ARCHIVED_LOGS_PREFIX,
+ sizeof(IB_ARCHIVED_LOGS_PREFIX) - 1)) {
+ continue;
+ }
+
+ log_file_lsn = strtoll(fileinfo.name +
+ sizeof(IB_ARCHIVED_LOGS_PREFIX) - 1,
+ &log_str_end_lsn_ptr, 10);
+
+ if (*log_str_end_lsn_ptr) {
+ continue;
+ }
+
+ if (log_file_lsn + (fileinfo.size - LOG_FILE_HDR_SIZE) < start_lsn) {
+ continue;
+ }
+
+ if (!xtrabackup_arch_first_file_lsn ||
+ log_file_lsn < xtrabackup_arch_first_file_lsn) {
+ xtrabackup_arch_first_file_lsn = log_file_lsn;
+ }
+ if (log_file_lsn > xtrabackup_arch_last_file_lsn) {
+ xtrabackup_arch_last_file_lsn = log_file_lsn;
+ }
+
+ //TODO: find the more suitable way to extract archived log file
+ //size
+ if (fileinfo.size > (ib_int64_t)xtrabackup_arch_file_size) {
+ xtrabackup_arch_file_size = fileinfo.size;
+ }
+ }
+
+ return xtrabackup_arch_first_file_lsn != 0;
+}
+
+static
+void
+innodb_free_param()
+{
+ srv_free_paths_and_sizes();
+ free(internal_innobase_data_file_path);
+ internal_innobase_data_file_path = NULL;
+ free_tmpdir(&mysql_tmpdir_list);
+}
+
+
+/**************************************************************************
+Store the current binary log coordinates in a specified file.
+@return 'false' on error. */
+static bool
+store_binlog_info(
+/*==============*/
+ const char *filename) /*!< in: output file name */
+{
+ FILE *fp;
+
+ if (trx_sys_mysql_bin_log_name[0] == '\0') {
+ return(true);
+ }
+
+ fp = fopen(filename, "w");
+
+ if (!fp) {
+ msg("xtrabackup: failed to open '%s'\n", filename);
+ return(false);
+ }
+
+ fprintf(fp, "%s\t" UINT64PF "\n",
+ trx_sys_mysql_bin_log_name, trx_sys_mysql_bin_log_pos);
+ fclose(fp);
+
+ return(true);
+}
+
+static void
+xtrabackup_prepare_func(int argc, char ** argv)
+{
+ ulint err;
+ datafiles_iter_t *it;
+ fil_node_t *node;
+ fil_space_t *space;
+ char metadata_path[FN_REFLEN];
+
+ /* cd to target-dir */
+
+ if (my_setwd(xtrabackup_real_target_dir,MYF(MY_WME)))
+ {
+ msg("xtrabackup: cannot my_setwd %s\n",
+ xtrabackup_real_target_dir);
+ exit(EXIT_FAILURE);
+ }
+ msg("xtrabackup: cd to %s\n", xtrabackup_real_target_dir);
+
+ encryption_plugin_prepare_init(argc, argv);
+
+ xtrabackup_target_dir= mysql_data_home_buff;
+ xtrabackup_target_dir[0]=FN_CURLIB; // all paths are relative from here
+ xtrabackup_target_dir[1]=0;
+
+ /*
+ read metadata of target, we don't need metadata reading in the case
+ archived logs applying
+ */
+ sprintf(metadata_path, "%s/%s", xtrabackup_target_dir,
+ XTRABACKUP_METADATA_FILENAME);
+
+ if (!xtrabackup_read_metadata(metadata_path)) {
+ msg("xtrabackup: Error: failed to read metadata from '%s'\n",
+ metadata_path);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!innobase_log_arch_dir)
+ {
+ if (!strcmp(metadata_type, "full-backuped")) {
+ msg("xtrabackup: This target seems to be not prepared "
+ "yet.\n");
+ } else if (!strcmp(metadata_type, "log-applied")) {
+ msg("xtrabackup: This target seems to be already "
+ "prepared with --apply-log-only.\n");
+ goto skip_check;
+ } else if (!strcmp(metadata_type, "full-prepared")) {
+ msg("xtrabackup: This target seems to be already "
+ "prepared.\n");
+ } else {
+ msg("xtrabackup: This target seems not to have correct "
+ "metadata...\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (xtrabackup_incremental) {
+ msg("xtrabackup: error: applying incremental backup "
+ "needs target prepared with --apply-log-only.\n");
+ exit(EXIT_FAILURE);
+ }
+skip_check:
+ if (xtrabackup_incremental
+ && metadata_to_lsn != incremental_lsn) {
+ msg("xtrabackup: error: This incremental backup seems "
+ "not to be proper for the target.\n"
+ "xtrabackup: Check 'to_lsn' of the target and "
+ "'from_lsn' of the incremental.\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ /* Create logfiles for recovery from 'xtrabackup_logfile', before start InnoDB */
+ srv_max_n_threads = 1000;
+ ut_mem_init();
+ /* temporally dummy value to avoid crash */
+ srv_page_size_shift = 14;
+ srv_page_size = (1 << srv_page_size_shift);
+ os_sync_init();
+ sync_init();
+ os_io_init_simple();
+ mem_init(srv_mem_pool_size);
+ ut_crc32_init();
+
+#ifdef WITH_INNODB_DISALLOW_WRITES
+ srv_allow_writes_event = os_event_create();
+ os_event_set(srv_allow_writes_event);
+#endif
+
+ xb_filters_init();
+
+ if(!innobase_log_arch_dir && xtrabackup_init_temp_log())
+ goto error_cleanup;
+
+ if(innodb_init_param()) {
+ goto error_cleanup;
+ }
+
+ xb_normalize_init_values();
+
+ if (xtrabackup_incremental || innobase_log_arch_dir) {
+ err = xb_data_files_init();
+ if (err != DB_SUCCESS) {
+ msg("xtrabackup: error: xb_data_files_init() failed "
+ "with error code %lu\n", err);
+ goto error_cleanup;
+ }
+ }
+ if (xtrabackup_incremental) {
+ inc_dir_tables_hash = hash_create(1000);
+
+ if(!xtrabackup_apply_deltas()) {
+ xb_data_files_close();
+ xb_filter_hash_free(inc_dir_tables_hash);
+ goto error_cleanup;
+ }
+ }
+ if (xtrabackup_incremental || innobase_log_arch_dir) {
+ xb_data_files_close();
+ }
+ if (xtrabackup_incremental) {
+ /* Cleanup datadir from tablespaces deleted between full and
+ incremental backups */
+
+ xb_process_datadir("./", ".ibd", rm_if_not_found, NULL);
+
+ xb_filter_hash_free(inc_dir_tables_hash);
+ }
+ if (fil_system) {
+ fil_close();
+ }
+
+ mem_close();
+ ut_free_all_mem();
+
+ innodb_free_param();
+ sync_close();
+ sync_initialized = FALSE;
+
+ /* Reset the configuration as it might have been changed by
+ xb_data_files_init(). */
+ if(innodb_init_param()) {
+ goto error_cleanup;
+ }
+
+ srv_apply_log_only = (bool) xtrabackup_apply_log_only;
+
+ /* increase IO threads */
+ if(srv_n_file_io_threads < 10) {
+ srv_n_read_io_threads = 4;
+ srv_n_write_io_threads = 4;
+ }
+
+ if (innobase_log_arch_dir) {
+ srv_arch_dir = innobase_log_arch_dir;
+ srv_archive_recovery = true;
+ if (xtrabackup_archived_to_lsn) {
+ if (xtrabackup_archived_to_lsn < metadata_last_lsn) {
+ msg("xtrabackup: warning: logs applying lsn "
+ "limit " UINT64PF " is "
+ "less than metadata last-lsn " UINT64PF
+ " and will be set to metadata last-lsn value\n",
+ xtrabackup_archived_to_lsn,
+ metadata_last_lsn);
+ xtrabackup_archived_to_lsn = metadata_last_lsn;
+ }
+ if (xtrabackup_archived_to_lsn < min_flushed_lsn) {
+ msg("xtrabackup: error: logs applying "
+ "lsn limit " UINT64PF " is less than "
+ "min_flushed_lsn " UINT64PF
+ ", there is nothing to do\n",
+ xtrabackup_archived_to_lsn,
+ min_flushed_lsn);
+ goto error_cleanup;
+ }
+ }
+ srv_archive_recovery_limit_lsn= xtrabackup_archived_to_lsn;
+ /*
+ Unfinished transactions are not rolled back during log applying
+ as they can be finished at the firther files applyings.
+ */
+ xtrabackup_apply_log_only = srv_apply_log_only = true;
+
+ if (!xtrabackup_arch_search_files(min_flushed_lsn)) {
+ goto error_cleanup;
+ }
+
+ /*
+ Check if last log file last lsn is big enough to overlap
+ last scanned lsn read from metadata.
+ */
+ if (xtrabackup_arch_last_file_lsn +
+ xtrabackup_arch_file_size -
+ LOG_FILE_HDR_SIZE < metadata_last_lsn) {
+ msg("xtrabackup: error: there are no enough archived logs "
+ "to apply\n");
+ goto error_cleanup;
+ }
+ }
+
+ msg("xtrabackup: Starting InnoDB instance for recovery.\n"
+ "xtrabackup: Using %lld bytes for buffer pool "
+ "(set by --use-memory parameter)\n", xtrabackup_use_memory);
+
+ srv_max_buf_pool_modified_pct = (double)max_buf_pool_modified_pct;
+
+ if (srv_max_dirty_pages_pct_lwm > srv_max_buf_pool_modified_pct) {
+ srv_max_dirty_pages_pct_lwm = srv_max_buf_pool_modified_pct;
+ }
+
+ if(innodb_init())
+ goto error_cleanup;
+
+ if (xtrabackup_incremental) {
+
+ it = datafiles_iter_new(fil_system);
+ if (it == NULL) {
+ msg("xtrabackup: Error: datafiles_iter_new() failed.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ while ((node = datafiles_iter_next(it)) != NULL) {
+ byte *header;
+ ulint size;
+ ulint actual_size;
+ mtr_t mtr;
+ buf_block_t *block;
+ ulint flags;
+
+ space = node->space;
+
+ /* Align space sizes along with fsp header. We want to process
+ each space once, so skip all nodes except the first one in a
+ multi-node space. */
+ if (UT_LIST_GET_PREV(chain, node) != NULL) {
+ continue;
+ }
+
+ mtr_start(&mtr);
+
+ mtr_s_lock(fil_space_get_latch(space->id, &flags), &mtr);
+
+ block = buf_page_get(space->id,
+ dict_tf_get_zip_size(flags),
+ 0, RW_S_LATCH, &mtr);
+ header = FSP_HEADER_OFFSET + buf_block_get_frame(block);
+
+ size = mtr_read_ulint(header + FSP_SIZE, MLOG_4BYTES,
+ &mtr);
+
+ mtr_commit(&mtr);
+
+ fil_extend_space_to_desired_size(&actual_size, space->id, size);
+ }
+
+ datafiles_iter_free(it);
+
+ } /* if (xtrabackup_incremental) */
+
+ if (xtrabackup_export) {
+ msg("xtrabackup: export option is specified.\n");
+ os_file_t info_file = XB_FILE_UNDEFINED;
+ char info_file_path[FN_REFLEN];
+ ibool success;
+ char table_name[FN_REFLEN];
+
+ byte* page;
+ byte* buf = NULL;
+
+ buf = static_cast<byte *>(ut_malloc(UNIV_PAGE_SIZE * 2));
+ page = static_cast<byte *>(ut_align(buf, UNIV_PAGE_SIZE));
+
+ /* flush insert buffer at shutdwon */
+ innobase_fast_shutdown = 0;
+
+ it = datafiles_iter_new(fil_system);
+ if (it == NULL) {
+ msg("xtrabackup: Error: datafiles_iter_new() "
+ "failed.\n");
+ exit(EXIT_FAILURE);
+ }
+ while ((node = datafiles_iter_next(it)) != NULL) {
+ int len;
+ char *next, *prev, *p;
+ dict_table_t* table;
+ dict_index_t* index;
+ ulint n_index;
+
+ space = node->space;
+
+ /* treat file_per_table only */
+ if (!fil_is_user_tablespace_id(space->id)) {
+ continue;
+ }
+
+ /* node exist == file exist, here */
+ strcpy(info_file_path, node->name);
+#ifdef _WIN32
+ for (int i = 0; info_file_path[i]; i++)
+ if (info_file_path[i] == '\\')
+ info_file_path[i]= '/';
+#endif
+ strcpy(info_file_path +
+ strlen(info_file_path) -
+ 4, ".exp");
+
+ len =(ib_uint32_t)strlen(info_file_path);
+
+ p = info_file_path;
+ prev = NULL;
+ while ((next = strchr(p, '/')) != NULL)
+ {
+ prev = p;
+ p = next + 1;
+ }
+ info_file_path[len - 4] = 0;
+ strncpy(table_name, prev, FN_REFLEN);
+
+ info_file_path[len - 4] = '.';
+
+ mutex_enter(&(dict_sys->mutex));
+
+ table = dict_table_get_low(table_name);
+ if (!table) {
+ msg("xtrabackup: error: "
+ "cannot find dictionary "
+ "record of table %s\n",
+ table_name);
+ goto next_node;
+ }
+
+ /* Write MySQL 5.6 .cfg file */
+ if (!xb_export_cfg_write(node, table)) {
+ goto next_node;
+ }
+
+ index = dict_table_get_first_index(table);
+ n_index = UT_LIST_GET_LEN(table->indexes);
+ if (n_index > 31) {
+ msg("xtrabackup: warning: table '%s' has more "
+ "than 31 indexes, .exp file was not "
+ "generated. Table will fail to import "
+ "on server version prior to 5.6.\n",
+ table->name);
+ goto next_node;
+ }
+
+ /* init exp file */
+ memset(page, 0, UNIV_PAGE_SIZE);
+ mach_write_to_4(page , 0x78706f72UL);
+ mach_write_to_4(page + 4, 0x74696e66UL);/*"xportinf"*/
+ mach_write_to_4(page + 8, n_index);
+ strncpy((char *) page + 12,
+ table_name, 500);
+
+ msg("xtrabackup: export metadata of "
+ "table '%s' to file `%s` "
+ "(%lu indexes)\n",
+ table_name, info_file_path,
+ n_index);
+
+ n_index = 1;
+ while (index) {
+ mach_write_to_8(page + n_index * 512, index->id);
+ mach_write_to_4(page + n_index * 512 + 8,
+ index->page);
+ strncpy((char *) page + n_index * 512 +
+ 12, index->name, 500);
+
+ msg("xtrabackup: name=%s, "
+ "id.low=%lu, page=%lu\n",
+ index->name,
+ (ulint)(index->id &
+ 0xFFFFFFFFUL),
+ (ulint) index->page);
+ index = dict_table_get_next_index(index);
+ n_index++;
+ }
+
+ srv_normalize_path_for_win(info_file_path);
+ info_file = os_file_create(
+ 0,
+ info_file_path,
+ OS_FILE_OVERWRITE,
+ OS_FILE_NORMAL, OS_DATA_FILE,
+ &success,0);
+ if (!success) {
+ os_file_get_last_error(TRUE);
+ goto next_node;
+ }
+ success = os_file_write(info_file_path,
+ info_file, page,
+ 0, UNIV_PAGE_SIZE);
+ if (!success) {
+ os_file_get_last_error(TRUE);
+ goto next_node;
+ }
+ success = os_file_flush(info_file);
+ if (!success) {
+ os_file_get_last_error(TRUE);
+ goto next_node;
+ }
+next_node:
+ if (info_file != XB_FILE_UNDEFINED) {
+ os_file_close(info_file);
+ info_file = XB_FILE_UNDEFINED;
+ }
+ mutex_exit(&(dict_sys->mutex));
+ }
+
+ ut_free(buf);
+ }
+
+ /* print the binary log position */
+ trx_sys_print_mysql_binlog_offset();
+ msg("\n");
+
+ /* output to xtrabackup_binlog_pos_innodb and (if
+ backup_safe_binlog_info was available on the server) to
+ xtrabackup_binlog_info. In the latter case xtrabackup_binlog_pos_innodb
+ becomes redundant and is created only for compatibility. */
+ if (!store_binlog_info("xtrabackup_binlog_pos_innodb") ||
+ (recover_binlog_info &&
+ !store_binlog_info(XTRABACKUP_BINLOG_INFO))) {
+
+ exit(EXIT_FAILURE);
+ }
+
+ if (innobase_log_arch_dir)
+ srv_start_lsn = log_sys->lsn = recv_sys->recovered_lsn;
+
+ /* Check whether the log is applied enough or not. */
+ if ((xtrabackup_incremental
+ && srv_start_lsn < incremental_to_lsn)
+ ||(!xtrabackup_incremental
+ && srv_start_lsn < metadata_to_lsn)) {
+ msg("xtrabackup: error: "
+ "The transaction log file is corrupted.\n"
+ "xtrabackup: error: "
+ "The log was not applied to the intended LSN!\n");
+ msg("xtrabackup: Log applied to lsn " LSN_PF "\n",
+ srv_start_lsn);
+ if (xtrabackup_incremental) {
+ msg("xtrabackup: The intended lsn is " LSN_PF "\n",
+ incremental_to_lsn);
+ } else {
+ msg("xtrabackup: The intended lsn is " LSN_PF "\n",
+ metadata_to_lsn);
+ }
+ exit(EXIT_FAILURE);
+ }
+#ifdef WITH_WSREP
+ xb_write_galera_info(xtrabackup_incremental);
+#endif
+
+ if(innodb_end())
+ goto error_cleanup;
+
+ innodb_free_param();
+
+ sync_initialized = FALSE;
+
+ /* re-init necessary components */
+ ut_mem_init();
+ os_sync_init();
+ sync_init();
+ os_io_init_simple();
+
+ if(xtrabackup_close_temp_log(TRUE))
+ exit(EXIT_FAILURE);
+
+ /* output to metadata file */
+ {
+ char filename[FN_REFLEN];
+
+ strcpy(metadata_type, srv_apply_log_only ?
+ "log-applied" : "full-prepared");
+
+ if(xtrabackup_incremental
+ && metadata_to_lsn < incremental_to_lsn)
+ {
+ metadata_to_lsn = incremental_to_lsn;
+ metadata_last_lsn = incremental_last_lsn;
+ }
+
+ sprintf(filename, "%s/%s", xtrabackup_target_dir, XTRABACKUP_METADATA_FILENAME);
+ if (!xtrabackup_write_metadata(filename)) {
+
+ msg("xtrabackup: Error: failed to write metadata "
+ "to '%s'\n", filename);
+ exit(EXIT_FAILURE);
+ }
+
+ if(xtrabackup_extra_lsndir) {
+ sprintf(filename, "%s/%s", xtrabackup_extra_lsndir, XTRABACKUP_METADATA_FILENAME);
+ if (!xtrabackup_write_metadata(filename)) {
+ msg("xtrabackup: Error: failed to write "
+ "metadata to '%s'\n", filename);
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+
+ if (!apply_log_finish()) {
+ exit(EXIT_FAILURE);
+ }
+
+ sync_close();
+ sync_initialized = FALSE;
+ if (fil_system) {
+ fil_close();
+ }
+
+ ut_free_all_mem();
+
+ /* start InnoDB once again to create log files */
+
+ if (!xtrabackup_apply_log_only) {
+
+ /* xtrabackup_incremental_dir is used to indicate that
+ we are going to apply incremental backup. Here we already
+ applied incremental backup and are about to do final prepare
+ of the full backup */
+ xtrabackup_incremental_dir = NULL;
+
+ if(innodb_init_param()) {
+ goto error;
+ }
+
+ srv_apply_log_only = false;
+
+ /* increase IO threads */
+ if(srv_n_file_io_threads < 10) {
+ srv_n_read_io_threads = 4;
+ srv_n_write_io_threads = 4;
+ }
+
+ srv_shutdown_state = SRV_SHUTDOWN_NONE;
+
+ if(innodb_init())
+ goto error;
+
+ if(innodb_end())
+ goto error;
+
+ innodb_free_param();
+
+ }
+
+ xb_filters_free();
+
+ return;
+
+error_cleanup:
+ xtrabackup_close_temp_log(FALSE);
+ xb_filters_free();
+
+error:
+ exit(EXIT_FAILURE);
+}
+
+/**************************************************************************
+Append group name to xb_load_default_groups list. */
+static
+void
+append_defaults_group(const char *group, const char *default_groups[],
+ size_t default_groups_size)
+{
+ uint i;
+ bool appended = false;
+ for (i = 0; i < default_groups_size - 1; i++) {
+ if (default_groups[i] == NULL) {
+ default_groups[i] = group;
+ appended = true;
+ break;
+ }
+ }
+ ut_a(appended);
+}
+
+bool
+xb_init()
+{
+ const char *mixed_options[4] = {NULL, NULL, NULL, NULL};
+ int n_mixed_options;
+
+ /* sanity checks */
+
+ if (opt_slave_info
+ && opt_no_lock
+ && !opt_safe_slave_backup) {
+ msg("Error: --slave-info is used with --no-lock but "
+ "without --safe-slave-backup. The binlog position "
+ "cannot be consistent with the backup data.\n");
+ return(false);
+ }
+
+ if (opt_rsync && xtrabackup_stream_fmt) {
+ msg("Error: --rsync doesn't work with --stream\n");
+ return(false);
+ }
+
+ n_mixed_options = 0;
+
+ if (opt_decompress) {
+ mixed_options[n_mixed_options++] = "--decompress";
+ } else if (opt_decrypt) {
+ mixed_options[n_mixed_options++] = "--decrypt";
+ }
+
+ if (xtrabackup_copy_back) {
+ mixed_options[n_mixed_options++] = "--copy-back";
+ }
+
+ if (xtrabackup_move_back) {
+ mixed_options[n_mixed_options++] = "--move-back";
+ }
+
+ if (xtrabackup_prepare) {
+ mixed_options[n_mixed_options++] = "--apply-log";
+ }
+
+ if (n_mixed_options > 1) {
+ msg("Error: %s and %s are mutually exclusive\n",
+ mixed_options[0], mixed_options[1]);
+ return(false);
+ }
+
+ if (xtrabackup_backup) {
+ if ((mysql_connection = xb_mysql_connect()) == NULL) {
+ return(false);
+ }
+
+ if (!get_mysql_vars(mysql_connection)) {
+ return(false);
+ }
+
+ encryption_plugin_backup_init(mysql_connection);
+ history_start_time = time(NULL);
+
+ }
+
+ return(true);
+}
+
+
+extern void init_signals(void);
+
+#include <sql_locale.h>
+
+/* Messages . Avoid loading errmsg.sys file */
+void setup_error_messages()
+{
+ static const char *all_msgs[ER_ERROR_LAST - ER_ERROR_FIRST +1];
+ my_default_lc_messages = &my_locale_en_US;
+ my_default_lc_messages->errmsgs->errmsgs = all_msgs;
+
+ /* Populate the necessary error messages */
+ struct {
+ int id;
+ const char *fmt;
+ }
+ xb_msgs[] =
+ {
+ { ER_DATABASE_NAME,"Database" },
+ { ER_TABLE_NAME,"Table"},
+ { ER_PARTITION_NAME, "Partition" },
+ { ER_SUBPARTITION_NAME, "Subpartition" },
+ { ER_TEMPORARY_NAME, "Temporary"},
+ { ER_RENAMED_NAME, "Renamed"},
+ { ER_CANT_FIND_DL_ENTRY, "Can't find symbol '%-.128s' in library"},
+ { ER_CANT_OPEN_LIBRARY, "Can't open shared library '%-.192s' (errno: %d, %-.128s)" },
+ { ER_OUTOFMEMORY, "Out of memory; restart server and try again (needed %d bytes)" },
+ { ER_CANT_OPEN_LIBRARY, "Can't open shared library '%-.192s' (errno: %d, %-.128s)" },
+ { ER_UDF_NO_PATHS, "No paths allowed for shared library" },
+ { ER_CANT_INITIALIZE_UDF,"Can't initialize function '%-.192s'; %-.80s"},
+ { ER_PLUGIN_IS_NOT_LOADED,"Plugin '%-.192s' is not loaded" }
+ };
+
+ for (int i = 0; i < (int)array_elements(all_msgs); i++)
+ all_msgs[i] = "Unknown error";
+
+ for (int i = 0; i < (int)array_elements(xb_msgs); i++)
+ all_msgs[xb_msgs[i].id - ER_ERROR_FIRST] = xb_msgs[i].fmt;
+}
+
+extern my_bool(*dict_check_if_skip_table)(const char* name) ;
+
+void
+handle_options(int argc, char **argv, char ***argv_client, char ***argv_server)
+{
+ /* Setup some variables for Innodb.*/
+
+ srv_xtrabackup = true;
+
+
+ files_charset_info = &my_charset_utf8_general_ci;
+ dict_check_if_skip_table = check_if_skip_table;
+
+ setup_error_messages();
+ sys_var_init();
+ plugin_mutex_init();
+ mysql_rwlock_init(key_rwlock_LOCK_system_variables_hash, &LOCK_system_variables_hash);
+ opt_stack_trace = 1;
+ test_flags |= TEST_SIGINT;
+ init_signals();
+#ifndef _WIN32
+ /* Exit process on SIGINT. */
+ my_sigset(SIGINT, SIG_DFL);
+#endif
+
+ sf_leaking_memory = 0; /* don't report memory leaks on early exist */
+
+ int i;
+ int ho_error;
+
+ char* target_dir = NULL;
+ bool prepare = false;
+
+ char conf_file[FN_REFLEN];
+ int argc_client = argc;
+ int argc_server = argc;
+
+ /* scan options for group and config file to load defaults from */
+ for (i = 1; i < argc; i++) {
+
+ char *optend = strcend(argv[i], '=');
+
+ if (strncmp(argv[i], "--defaults-group",
+ optend - argv[i]) == 0) {
+ defaults_group = optend + 1;
+ append_defaults_group(defaults_group,
+ xb_server_default_groups,
+ array_elements(xb_server_default_groups));
+ }
+
+ if (strncmp(argv[i], "--login-path",
+ optend - argv[i]) == 0) {
+ append_defaults_group(optend + 1,
+ xb_client_default_groups,
+ array_elements(xb_client_default_groups));
+ }
+
+ if (!strncmp(argv[i], "--prepare",
+ optend - argv[i])) {
+ prepare = true;
+ }
+
+ if (!strncmp(argv[i], "--apply-log",
+ optend - argv[i])) {
+ prepare = true;
+ }
+
+ if (!strncmp(argv[i], "--target-dir",
+ optend - argv[i]) && *optend) {
+ target_dir = optend + 1;
+ }
+
+ if (!*optend && argv[i][0] != '-') {
+ target_dir = argv[i];
+ }
+ }
+
+ snprintf(conf_file, sizeof(conf_file), "my");
+
+ if (prepare && target_dir) {
+ snprintf(conf_file, sizeof(conf_file),
+ "%s/backup-my.cnf", target_dir);
+ if (!strncmp(argv[1], "--defaults-file=", 16)) {
+ /* Remove defaults-file*/
+ for (int i = 2; ; i++) {
+ if ((argv[i-1]= argv[i]) == 0)
+ break;
+ }
+ argc--;
+ }
+ }
+
+ *argv_client = argv;
+ *argv_server = argv;
+ if (load_defaults(conf_file, xb_server_default_groups,
+ &argc_server, argv_server)) {
+ exit(EXIT_FAILURE);
+ }
+
+ int n;
+ for (n = 0; (*argv_server)[n]; n++) {};
+ argc_server = n;
+
+ print_param_str <<
+ "# This MySQL options file was generated by XtraBackup.\n"
+ "[" << defaults_group << "]\n";
+
+ /* We want xtrabackup to ignore unknown options, because it only
+ recognizes a small subset of server variables */
+ my_getopt_skip_unknown = TRUE;
+
+ /* Reset u_max_value for all options, as we don't want the
+ --maximum-... modifier to set the actual option values */
+ for (my_option *optp= xb_server_options; optp->name; optp++) {
+ optp->u_max_value = (G_PTR *) &global_max_value;
+ }
+
+
+ /* Throw a descriptive error if --defaults-file or --defaults-extra-file
+ is not the first command line argument */
+ for (int i = 2 ; i < argc ; i++) {
+ char *optend = strcend((argv)[i], '=');
+
+ if (optend - argv[i] == 15 &&
+ !strncmp(argv[i], "--defaults-file", optend - argv[i])) {
+
+ msg("xtrabackup: Error: --defaults-file "
+ "must be specified first on the command "
+ "line\n");
+ exit(EXIT_FAILURE);
+ }
+ if (optend - argv[i] == 21 &&
+ !strncmp(argv[i], "--defaults-extra-file",
+ optend - argv[i])) {
+
+ msg("xtrabackup: Error: --defaults-extra-file "
+ "must be specified first on the command "
+ "line\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (argc_server > 0
+ && (ho_error=handle_options(&argc_server, argv_server,
+ xb_server_options, xb_get_one_option)))
+ exit(ho_error);
+
+ if (load_defaults(conf_file, xb_client_default_groups,
+ &argc_client, argv_client)) {
+ exit(EXIT_FAILURE);
+ }
+
+ for (n = 0; (*argv_client)[n]; n++) {};
+ argc_client = n;
+
+ if (strcmp(base_name(my_progname), INNOBACKUPEX_BIN_NAME) == 0 &&
+ argc_client > 0) {
+ /* emulate innobackupex script */
+ innobackupex_mode = true;
+ if (!ibx_handle_options(&argc_client, argv_client)) {
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (argc_client > 0
+ && (ho_error=handle_options(&argc_client, argv_client,
+ xb_client_options, xb_get_one_option)))
+ exit(ho_error);
+
+ /* Reject command line arguments that don't look like options, i.e. are
+ not of the form '-X' (single-character options) or '--option' (long
+ options) */
+ for (int i = 0 ; i < argc_client ; i++) {
+ const char * const opt = (*argv_client)[i];
+
+ if (strncmp(opt, "--", 2) &&
+ !(strlen(opt) == 2 && opt[0] == '-')) {
+ bool server_option = true;
+
+ for (int j = 0; j < argc_server; j++) {
+ if (opt == (*argv_server)[j]) {
+ server_option = false;
+ break;
+ }
+ }
+
+ if (!server_option) {
+ msg("xtrabackup: Error:"
+ " unknown argument: '%s'\n", opt);
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+}
+
+/* ================= main =================== */
+extern my_bool(*fil_check_if_skip_database_by_path)(const char* name);
+
+int main(int argc, char **argv)
+{
+ char **client_defaults, **server_defaults;
+ char cwd[FN_REFLEN];
+ static char INNOBACKUPEX_EXE[]= "innobackupex";
+ if (argc > 1 && (strcmp(argv[1], "--innobackupex") == 0))
+ {
+ argv++;
+ argc--;
+ argv[0] = INNOBACKUPEX_EXE;
+ innobackupex_mode = true;
+ }
+
+ /* Setup skip fil_load_single_tablespaces callback.*/
+ fil_check_if_skip_database_by_path = check_if_skip_database_by_path;
+
+ init_signals();
+ MY_INIT(argv[0]);
+
+ pthread_key_create(&THR_THD, NULL);
+ my_pthread_setspecific_ptr(THR_THD, NULL);
+
+ xb_regex_init();
+
+ capture_tool_command(argc, argv);
+
+ if (mysql_server_init(-1, NULL, NULL))
+ {
+ exit(EXIT_FAILURE);
+ }
+
+ system_charset_info = &my_charset_utf8_general_ci;
+ key_map_full.set_all();
+
+ handle_options(argc, argv, &client_defaults, &server_defaults);
+
+ int argc_server;
+ for (argc_server = 0; server_defaults[argc_server]; argc_server++) {}
+
+ int argc_client;
+ for (argc_client = 0; client_defaults[argc_client]; argc_client++) {}
+
+
+ if (innobackupex_mode) {
+ if (!ibx_init()) {
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if ((!xtrabackup_print_param) && (!xtrabackup_prepare) && (strcmp(mysql_data_home, "./") == 0)) {
+ if (!xtrabackup_print_param)
+ usage();
+ msg("\nxtrabackup: Error: Please set parameter 'datadir'\n");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Expand target-dir, incremental-basedir, etc. */
+
+ my_getwd(cwd, sizeof(cwd), MYF(0));
+
+ my_load_path(xtrabackup_real_target_dir,
+ xtrabackup_target_dir, cwd);
+ unpack_dirname(xtrabackup_real_target_dir,
+ xtrabackup_real_target_dir);
+ xtrabackup_target_dir= xtrabackup_real_target_dir;
+
+ if (xtrabackup_incremental_basedir) {
+ my_load_path(xtrabackup_real_incremental_basedir,
+ xtrabackup_incremental_basedir, cwd);
+ unpack_dirname(xtrabackup_real_incremental_basedir,
+ xtrabackup_real_incremental_basedir);
+ xtrabackup_incremental_basedir =
+ xtrabackup_real_incremental_basedir;
+ }
+
+ if (xtrabackup_incremental_dir) {
+ my_load_path(xtrabackup_real_incremental_dir,
+ xtrabackup_incremental_dir, cwd);
+ unpack_dirname(xtrabackup_real_incremental_dir,
+ xtrabackup_real_incremental_dir);
+ xtrabackup_incremental_dir = xtrabackup_real_incremental_dir;
+ }
+
+ if (xtrabackup_extra_lsndir) {
+ my_load_path(xtrabackup_real_extra_lsndir,
+ xtrabackup_extra_lsndir, cwd);
+ unpack_dirname(xtrabackup_real_extra_lsndir,
+ xtrabackup_real_extra_lsndir);
+ xtrabackup_extra_lsndir = xtrabackup_real_extra_lsndir;
+ }
+
+ /* get default temporary directory */
+ if (!opt_mysql_tmpdir || !opt_mysql_tmpdir[0]) {
+ opt_mysql_tmpdir = getenv("TMPDIR");
+#if defined(__WIN__)
+ if (!opt_mysql_tmpdir) {
+ opt_mysql_tmpdir = getenv("TEMP");
+ }
+ if (!opt_mysql_tmpdir) {
+ opt_mysql_tmpdir = getenv("TMP");
+ }
+#endif
+ if (!opt_mysql_tmpdir || !opt_mysql_tmpdir[0]) {
+ opt_mysql_tmpdir = const_cast<char*>(DEFAULT_TMPDIR);
+ }
+ }
+
+ /* temporary setting of enough size */
+ srv_page_size_shift = UNIV_PAGE_SIZE_SHIFT_MAX;
+ srv_page_size = UNIV_PAGE_SIZE_MAX;
+ if (xtrabackup_backup && xtrabackup_incremental) {
+ /* direct specification is only for --backup */
+ /* and the lsn is prior to the other option */
+
+ char* endchar;
+ int error = 0;
+ incremental_lsn = strtoll(xtrabackup_incremental, &endchar, 10);
+ if (*endchar != '\0')
+ error = 1;
+
+ if (error) {
+ msg("xtrabackup: value '%s' may be wrong format for "
+ "incremental option.\n", xtrabackup_incremental);
+ exit(EXIT_FAILURE);
+ }
+ } else if (xtrabackup_backup && xtrabackup_incremental_basedir) {
+ char filename[FN_REFLEN];
+
+ sprintf(filename, "%s/%s", xtrabackup_incremental_basedir, XTRABACKUP_METADATA_FILENAME);
+
+ if (!xtrabackup_read_metadata(filename)) {
+ msg("xtrabackup: error: failed to read metadata from "
+ "%s\n", filename);
+ exit(EXIT_FAILURE);
+ }
+
+ incremental_lsn = metadata_to_lsn;
+ xtrabackup_incremental = xtrabackup_incremental_basedir; //dummy
+ } else if (xtrabackup_prepare && xtrabackup_incremental_dir) {
+ char filename[FN_REFLEN];
+
+ sprintf(filename, "%s/%s", xtrabackup_incremental_dir, XTRABACKUP_METADATA_FILENAME);
+
+ if (!xtrabackup_read_metadata(filename)) {
+ msg("xtrabackup: error: failed to read metadata from "
+ "%s\n", filename);
+ exit(EXIT_FAILURE);
+ }
+
+ incremental_lsn = metadata_from_lsn;
+ incremental_to_lsn = metadata_to_lsn;
+ incremental_last_lsn = metadata_last_lsn;
+ xtrabackup_incremental = xtrabackup_incremental_dir; //dummy
+
+ } else if (opt_incremental_history_name) {
+ xtrabackup_incremental = opt_incremental_history_name;
+ } else if (opt_incremental_history_uuid) {
+ xtrabackup_incremental = opt_incremental_history_uuid;
+ } else {
+ xtrabackup_incremental = NULL;
+ }
+
+ if (!xb_init()) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* --print-param */
+ if (xtrabackup_print_param) {
+
+ printf("%s", print_param_str.str().c_str());
+
+ exit(EXIT_SUCCESS);
+ }
+
+ print_version();
+ if (xtrabackup_incremental) {
+ msg("incremental backup from " LSN_PF " is enabled.\n",
+ incremental_lsn);
+ }
+
+ if (xtrabackup_export && innobase_file_per_table == FALSE) {
+ msg("xtrabackup: auto-enabling --innodb-file-per-table due to "
+ "the --export option\n");
+ innobase_file_per_table = TRUE;
+ }
+
+ if (xtrabackup_incremental && xtrabackup_stream &&
+ xtrabackup_stream_fmt == XB_STREAM_FMT_TAR) {
+ msg("xtrabackup: error: "
+ "streaming incremental backups are incompatible with the \n"
+ "'tar' streaming format. Use --stream=xbstream instead.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if ((xtrabackup_compress || xtrabackup_encrypt) && xtrabackup_stream &&
+ xtrabackup_stream_fmt == XB_STREAM_FMT_TAR) {
+ msg("xtrabackup: error: "
+ "compressed and encrypted backups are incompatible with the \n"
+ "'tar' streaming format. Use --stream=xbstream instead.\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (!xtrabackup_prepare &&
+ (innobase_log_arch_dir || xtrabackup_archived_to_lsn)) {
+
+ /* Default my.cnf can contain innobase_log_arch_dir option set
+ for server, reset it to allow backup. */
+ innobase_log_arch_dir= NULL;
+ xtrabackup_archived_to_lsn= 0;
+ msg("xtrabackup: warning: "
+ "as --innodb-log-arch-dir and --to-archived-lsn can be used "
+ "only with --prepare they will be reset\n");
+ }
+
+ /* cannot execute both for now */
+ {
+ int num = 0;
+
+ if (xtrabackup_backup) num++;
+ if (xtrabackup_stats) num++;
+ if (xtrabackup_prepare) num++;
+ if (xtrabackup_copy_back) num++;
+ if (xtrabackup_move_back) num++;
+ if (xtrabackup_decrypt_decompress) num++;
+ if (num != 1) { /* !XOR (for now) */
+ usage();
+ exit(EXIT_FAILURE);
+ }
+ }
+
+#ifndef __WIN__
+ if (xtrabackup_debug_sync) {
+ signal(SIGCONT, sigcont_handler);
+ }
+#endif
+
+ /* --backup */
+ if (xtrabackup_backup)
+ xtrabackup_backup_func();
+
+ /* --stats */
+ if (xtrabackup_stats)
+ xtrabackup_stats_func(argc_server,server_defaults);
+
+ /* --prepare */
+ if (xtrabackup_prepare) {
+ xtrabackup_prepare_func(argc_server, server_defaults);
+ }
+
+ if (xtrabackup_copy_back || xtrabackup_move_back) {
+ if (!check_if_param_set("datadir")) {
+ msg("Error: datadir must be specified.\n");
+ exit(EXIT_FAILURE);
+ }
+ if (!copy_back())
+ exit(EXIT_FAILURE);
+ }
+
+ if (xtrabackup_decrypt_decompress && !decrypt_decompress()) {
+ exit(EXIT_FAILURE);
+ }
+
+ backup_cleanup();
+
+ if (innobackupex_mode) {
+ ibx_cleanup();
+ }
+
+
+ free_defaults(client_defaults);
+ free_defaults(server_defaults);
+
+ if (THR_THD)
+ (void) pthread_key_delete(THR_THD);
+
+ msg_ts("completed OK!\n");
+
+ exit(EXIT_SUCCESS);
+}