xtrabackup.cc 201 KB
Newer Older
1
/******************************************************
2
MariaBackup: hot backup tool for InnoDB
Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
3
(c) 2009-2017 Percona LLC and/or its affiliates
4 5 6
Originally Created 3/3/2009 Yasufumi Kinoshita
Written by Alexey Kopytov, Aleksandr Kuzminsky, Stewart Smith, Vadim Tkachenko,
Yasufumi Kinoshita, Ignacio Nin and Baron Schwartz.
7
(c) 2017, 2022, MariaDB Corporation.
8
Portions written by Marko Mäkelä.
9 10 11 12 13 14 15 16 17 18 19 20

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
21
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1335  USA
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38

*******************************************************

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
Vicențiu Ciorbaru's avatar
Vicențiu Ciorbaru committed
39 40
this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
Street, Fifth Floor, Boston, MA 02110-1335 USA
41 42 43 44 45

*******************************************************/

//#define XTRABACKUP_TARGET_IS_PLUGIN

Michael Widenius's avatar
Michael Widenius committed
46
#include <my_global.h>
47 48
#include <my_config.h>
#include <unireg.h>
49 50 51 52 53
#include <mysql_version.h>
#include <my_base.h>
#include <my_getopt.h>
#include <mysql_com.h>
#include <my_default.h>
54
#include <scope.h>
55
#include <sql_class.h>
56 57 58 59 60 61

#include <fcntl.h>
#include <string.h>

#ifdef __linux__
# include <sys/prctl.h>
62
# include <sys/resource.h>
63 64
#endif

65 66 67 68
#ifdef __APPLE__
# include "libproc.h"
#endif

69 70 71 72
#ifdef __FreeBSD__
# include <sys/sysctl.h>
#endif

73 74 75 76

#include <btr0sea.h>
#include <lock0lock.h>
#include <log0recv.h>
77
#include <log0crypt.h>
78 79 80
#include <row0mysql.h>
#include <row0quiesce.h>
#include <srv0start.h>
81
#include "trx0sys.h"
82
#include <buf0dblwr.h>
Marko Mäkelä's avatar
Marko Mäkelä committed
83
#include <buf0flu.h>
84
#include "ha_innodb.h"
85

Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
86
#include <list>
87 88
#include <sstream>
#include <set>
89
#include <fstream>
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
#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"
106
#include "backup_wsrep.h"
107 108 109 110
#include "innobackupex.h"
#include "backup_mysql.h"
#include "backup_copy.h"
#include "backup_mysql.h"
111
#include "xb_plugin.h"
112 113
#include <sql_plugin.h>
#include <srv0srv.h>
114
#include <log.h>
115
#include <derror.h>
116
#include <thr_timer.h>
117 118 119
#include "backup_debug.h"

#define MB_CORRUPTED_PAGES_FILE "innodb_corrupted_pages"
120

121 122 123 124 125 126
// disable server's systemd notification code
extern "C" {
int sd_notify() { return 0; }
int sd_notifyf() { return 0; }
}

127 128 129 130 131
int sys_var_init();

/* === xtrabackup specific options === */
char xtrabackup_real_target_dir[FN_REFLEN] = "./xtrabackup_backupfiles/";
char *xtrabackup_target_dir= xtrabackup_real_target_dir;
132
static my_bool xtrabackup_version;
133
static my_bool verbose;
134 135 136 137 138 139
my_bool xtrabackup_backup;
my_bool xtrabackup_prepare;
my_bool xtrabackup_copy_back;
my_bool xtrabackup_move_back;
my_bool xtrabackup_decrypt_decompress;
my_bool xtrabackup_print_param;
140
my_bool xtrabackup_mysqld_args;
141
my_bool xtrabackup_help;
142 143 144 145 146

my_bool xtrabackup_export;

longlong xtrabackup_use_memory;

147
uint opt_protocol;
148 149
long xtrabackup_throttle; /* 0:unlimited */
static lint io_ticket;
150 151
static mysql_cond_t wait_throttle;
static mysql_cond_t log_copying_stop;
152 153

char *xtrabackup_incremental;
154 155 156
lsn_t incremental_lsn;
lsn_t incremental_to_lsn;
lsn_t incremental_last_lsn;
157
xb_page_bitmap *changed_page_bitmap;
158

159 160 161
char *xtrabackup_incremental_basedir; /* for --backup */
char *xtrabackup_extra_lsndir; /* for --backup with --extra-lsndir */
char *xtrabackup_incremental_dir; /* for --prepare */
162 163 164 165 166

char xtrabackup_real_incremental_basedir[FN_REFLEN];
char xtrabackup_real_extra_lsndir[FN_REFLEN];
char xtrabackup_real_incremental_dir[FN_REFLEN];

167

168 169
char *xtrabackup_tmpdir;

170 171 172
char *xtrabackup_tables;
char *xtrabackup_tables_file;
char *xtrabackup_tables_exclude;
173 174
char *xb_rocksdb_datadir;
my_bool xb_backup_rocksdb = 1;
Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
175 176 177 178 179

typedef std::list<regex_t> regex_list_t;
static regex_list_t regex_include_list;
static regex_list_t regex_exclude_list;

180 181
static hash_table_t tables_include_hash;
static hash_table_t tables_exclude_hash;
182 183 184

char *xtrabackup_databases = NULL;
char *xtrabackup_databases_file = NULL;
Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
185
char *xtrabackup_databases_exclude = NULL;
186 187
static hash_table_t databases_include_hash;
static hash_table_t databases_exclude_hash;
188

189
static hash_table_t inc_dir_tables_hash;
190

191
struct xb_filter_entry_t{
192 193
	char*		name;
	ibool		has_tables;
194
	xb_filter_entry_t *name_hash;
195 196
};

Marko Mäkelä's avatar
Marko Mäkelä committed
197
/** whether log_copying_thread() is active; protected by recv_sys.mutex */
198
static bool log_copying_running;
199 200 201 202 203 204 205 206

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;
207
uint xtrabackup_compress = FALSE;
208 209 210 211 212 213
uint xtrabackup_compress_threads;
ulonglong xtrabackup_compress_chunk_size = 0;

/* sleep interval beetween log copy iterations in log copying thread
in milliseconds (default is 1 second) */
ulint xtrabackup_log_copy_interval = 1000;
214
static ulong max_buf_pool_modified_pct;
215 216

/* Ignored option (--log) for MySQL option compatibility */
217
static char*	log_ignored_opt;
218

219 220

extern my_bool opt_use_ssl;
221
extern char *opt_tls_version;
222
my_bool opt_ssl_verify_server_cert;
223
my_bool opt_extended_validation;
Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
224
my_bool opt_encrypted_backup;
225

226 227
/* === metadata of backup === */
#define XTRABACKUP_METADATA_FILENAME "xtrabackup_checkpoints"
228
char metadata_type[30] = ""; /*[full-backuped|log-applied|incremental]*/
229
static lsn_t metadata_from_lsn;
230
lsn_t metadata_to_lsn;
231
static lsn_t metadata_last_lsn;
232

233
static ds_file_t*	dst_log_file;
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248

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

/* The default values for the following, type long or longlong, start-up
parameters are declared in mysqld.cc: */

long innobase_buffer_pool_awe_mem_mb = 0;
long innobase_file_io_threads = 4;
Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
249 250
ulong innobase_read_io_threads = 4;
ulong innobase_write_io_threads = 4;
251

252
/** Store the failed read of undo tablespace ids. Protected by
Marko Mäkelä's avatar
Marko Mäkelä committed
253
recv_sys.mutex. */
254 255
static std::set<uint32_t> fail_undo_ids;

256
longlong innobase_page_size = (1LL << 14); /* 16KB */
257
char*	innobase_buffer_pool_filename = NULL;
258 259 260 261

/* The default values for the following char* start-up parameters
are determined in innobase_init below: */

262 263 264
static char*	innobase_ignored_opt;
char*	innobase_data_home_dir;
char*	innobase_data_file_path;
265

266
char *aria_log_dir_path;
267 268 269

my_bool xtrabackup_incremental_force_scan = FALSE;

270 271 272 273 274 275
/*
 * Ignore corrupt pages (disabled by default; used
 * by "innobackupex" as a command line argument).
 */
ulong xtrabackup_innodb_force_recovery = 0;

276
/* The flushed lsn which is read from data files */
277
lsn_t	flushed_lsn= 0;
278 279

ulong xb_open_files_limit= 0;
280 281
char *xb_plugin_dir;
char *xb_plugin_load;
282
my_bool xb_close_files;
283

284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343

class Datasink_free_list
{
protected:
  /*
    Simple datasink creation tracking...
    add datasinks in the reverse order you want them destroyed.
  */
#define XTRABACKUP_MAX_DATASINKS 10
  ds_ctxt_t  *m_datasinks_to_destroy[XTRABACKUP_MAX_DATASINKS];
  uint        m_actual_datasinks_to_destroy;
public:
  Datasink_free_list()
   :m_actual_datasinks_to_destroy(0)
  { }

  void add_datasink_to_destroy(ds_ctxt_t *ds)
  {
    xb_ad(m_actual_datasinks_to_destroy < XTRABACKUP_MAX_DATASINKS);
    m_datasinks_to_destroy[m_actual_datasinks_to_destroy] = ds;
    m_actual_datasinks_to_destroy++;
  }

  /*
    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.
  */
  void destroy()
  {
    for (uint i= m_actual_datasinks_to_destroy; i > 0; i--)
    {
      ds_destroy(m_datasinks_to_destroy[i - 1]);
      m_datasinks_to_destroy[i - 1] = NULL;
    }
  }
};


class Backup_datasinks: public Datasink_free_list
{
public:
  ds_ctxt_t       *m_data;
  ds_ctxt_t       *m_meta;
  ds_ctxt_t       *m_redo;

  Backup_datasinks()
   :m_data(NULL),
    m_meta(NULL),
    m_redo(NULL)
  { }
  void init();
  void destroy()
  {
    Datasink_free_list::destroy();
    *this= Backup_datasinks();
  }
  bool backup_low();
};

344 345 346 347 348 349 350 351 352 353 354 355 356

static bool	innobackupex_mode = false;

/* 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);
357
extern LOGGER logger;
358 359 360 361 362 363 364 365 366 367

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;
Marko Mäkelä's avatar
Marko Mäkelä committed
368
my_bool opt_remove_original;
369
my_bool opt_log_innodb_page_corruption;
370

371
my_bool opt_lock_ddl_per_table = FALSE;
372
static my_bool opt_check_privileges;
373

374 375 376 377 378
extern const char *innodb_checksum_algorithm_names[];
extern TYPELIB innodb_checksum_algorithm_typelib;
extern const char *innodb_flush_method_names[];
extern TYPELIB innodb_flush_method_typelib;

379 380 381 382 383 384
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;

385 386
char *opt_incremental_history_name;
char *opt_incremental_history_uuid;
387

388
char *opt_user;
389
const char *opt_password;
390 391 392 393 394
char *opt_host;
char *opt_defaults_group;
char *opt_socket;
uint opt_port;
char *opt_log_bin;
395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412

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;

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;


413 414 415
char mariabackup_exe[FN_REFLEN];
char orig_argv1[FN_REFLEN];

416 417
pthread_cond_t  scanned_lsn_cond;

418 419 420
/** Store the deferred tablespace name during --backup */
static std::set<std::string> defer_space_names;

421 422 423 424 425 426 427 428 429
typedef std::map<space_id_t,std::string> space_id_to_name_t;

struct ddl_tracker_t {
	/** Tablspaces with their ID and name, as they were copied to backup.*/
	space_id_to_name_t tables_in_backup;
	/** Drop operations found in redo log. */
	std::set<space_id_t> drops;
	/* For DDL operation found in redo log,  */
	space_id_to_name_t id_to_name;
430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468
	/** Deferred tablespaces with their ID and name which was
	found in redo log of DDL operations */
	space_id_to_name_t deferred_tables;

  /** Insert the deferred tablespace id with the name */
  void insert_defer_id(space_id_t space_id, std::string name)
  {
    auto it= defer_space_names.find(name);
    if (it != defer_space_names.end())
    {
      deferred_tables[space_id]= name;
      defer_space_names.erase(it);
    }
  }

  /** Rename the deferred tablespace with new name */
  void rename_defer(space_id_t space_id, std::string old_name,
                    std::string new_name)
  {
    if (deferred_tables.find(space_id) != deferred_tables.end())
      deferred_tables[space_id] = new_name;
    auto defer_end= defer_space_names.end();
    auto defer= defer_space_names.find(old_name);
    if (defer == defer_end)
      defer= defer_space_names.find(new_name);

    if (defer != defer_end)
    {
      deferred_tables[space_id]= new_name;
      defer_space_names.erase(defer);
    }
  }

  /** Delete the deferred tablespace */
  void delete_defer(space_id_t space_id, std::string name)
  {
    deferred_tables.erase(space_id);
    defer_space_names.erase(name);
  }
469 470
};

471
static ddl_tracker_t ddl_tracker;
472

473 474 475 476
/** Store the space ids of truncated undo log tablespaces. Protected
by recv_sys.mutex */
static std::set<uint32_t> undo_trunc_ids;

477 478 479 480 481
/** Stores the space ids of page0 INIT_PAGE redo records. It is
used to indicate whether the given deferred tablespace can
be reconstructed. */
static std::set<space_id_t> first_page_init_ids;

482
// Convert non-null terminated filename to space name
483
static std::string filename_to_spacename(const void *filename, size_t len);
484 485 486 487 488

CorruptedPages::CorruptedPages() { ut_a(!pthread_mutex_init(&m_mutex, NULL)); }

CorruptedPages::~CorruptedPages() { ut_a(!pthread_mutex_destroy(&m_mutex)); }

489 490
void CorruptedPages::add_page_no_lock(const char *space_name,
                                      page_id_t page_id,
Marko Mäkelä's avatar
Marko Mäkelä committed
491
                                      bool convert_space_name)
492
{
493
  space_info_t  &space_info = m_spaces[page_id.space()];
494
  if (space_info.space_name.empty())
495 496 497
    space_info.space_name= convert_space_name
      ? filename_to_spacename(space_name, strlen(space_name))
      : space_name;
498
  (void)space_info.pages.insert(page_id.page_no());
499 500
}

501
void CorruptedPages::add_page(const char *file_name, page_id_t page_id)
502
{
503 504 505
  pthread_mutex_lock(&m_mutex);
  add_page_no_lock(file_name, page_id, true);
  pthread_mutex_unlock(&m_mutex);
506 507
}

508
bool CorruptedPages::contains(page_id_t page_id) const
509 510 511
{
  bool result = false;
  ut_a(!pthread_mutex_lock(&m_mutex));
512
  container_t::const_iterator space_it= m_spaces.find(page_id.space());
513
  if (space_it != m_spaces.end())
514
    result = space_it->second.pages.count(page_id.page_no());
515 516 517 518
  ut_a(!pthread_mutex_unlock(&m_mutex));
  return result;
}

519
void CorruptedPages::drop_space(uint32_t space_id)
520 521 522 523 524 525
{
  ut_a(!pthread_mutex_lock(&m_mutex));
  m_spaces.erase(space_id);
  ut_a(!pthread_mutex_unlock(&m_mutex));
}

526 527
void CorruptedPages::rename_space(uint32_t space_id,
                                  const std::string &new_name)
528 529 530 531 532 533 534 535
{
  ut_a(!pthread_mutex_lock(&m_mutex));
  container_t::iterator space_it = m_spaces.find(space_id);
  if (space_it != m_spaces.end())
    space_it->second.space_name = new_name;
  ut_a(!pthread_mutex_unlock(&m_mutex));
}

536 537
bool CorruptedPages::print_to_file(ds_ctxt *ds_data,
                                   const char *filename) const
538 539 540 541 542 543 544 545 546 547 548 549 550 551
{
  std::ostringstream out;
  ut_a(!pthread_mutex_lock(&m_mutex));
  if (!m_spaces.size())
  {
    ut_a(!pthread_mutex_unlock(&m_mutex));
    return true;
  }
  for (container_t::const_iterator space_it=
           m_spaces.begin();
       space_it != m_spaces.end(); ++space_it)
  {
    out << space_it->second.space_name << " " << space_it->first << "\n";
    bool first_page_no= true;
Marko Mäkelä's avatar
Marko Mäkelä committed
552
    for (std::set<unsigned>::const_iterator page_it=
553 554 555 556 557 558 559 560 561 562 563 564
             space_it->second.pages.begin();
         page_it != space_it->second.pages.end(); ++page_it)
      if (first_page_no)
      {
        out << *page_it;
        first_page_no= false;
      }
      else
        out << " " << *page_it;
    out << "\n";
  }
  ut_a(!pthread_mutex_unlock(&m_mutex));
565 566
  if (ds_data)
    return ds_data->backup_file_print_buf(filename, out.str().c_str(),
567
                                 static_cast<int>(out.str().size()));
568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588
  std::ofstream outfile;
  outfile.open(filename);
  if (!outfile.is_open())
    die("Can't open %s, error number: %d, error message: %s", filename, errno,
        strerror(errno));
  outfile << out.str();
  return true;
}

void CorruptedPages::read_from_file(const char *file_name)
{
  MY_STAT mystat;
  if (!my_stat(file_name, &mystat, MYF(0)))
    return;
  std::ifstream infile;
  infile.open(file_name);
  if (!infile.is_open())
    die("Can't open %s, error number: %d, error message: %s", file_name, errno,
        strerror(errno));
  std::string line;
  std::string space_name;
589
  uint32_t space_id;
590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605
  ulint line_number= 0;
  while (std::getline(infile, line))
  {
    ++line_number;
    std::istringstream iss(line);
    if (line_number & 1) {
      if (!(iss >> space_name))
        die("Can't parse space name from corrupted pages file at "
            "line " ULINTPF,
            line_number);
      if (!(iss >> space_id))
        die("Can't parse space id from corrupted pages file at line " ULINTPF,
            line_number);
    }
    else
    {
Marko Mäkelä's avatar
Marko Mäkelä committed
606 607
      std::istringstream iss(line);
      unsigned page_no;
608
      while ((iss >> page_no))
609
        add_page_no_lock(space_name.c_str(), {space_id, page_no}, false);
610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625
      if (!iss.eof())
        die("Corrupted pages file parse error on line number " ULINTPF,
            line_number);
    }
  }
}

bool CorruptedPages::empty() const
{
  ut_a(!pthread_mutex_lock(&m_mutex));
  bool result= !m_spaces.size();
  ut_a(!pthread_mutex_unlock(&m_mutex));
  return result;
}

static void xb_load_single_table_tablespace(const std::string &space_name,
626
                                            bool set_size,
627
                                            uint32_t defer_space_id=0);
628
static void xb_data_files_close();
Marko Mäkelä's avatar
Marko Mäkelä committed
629
static fil_space_t* fil_space_get_by_name(const char* name);
630 631 632 633

void CorruptedPages::zero_out_free_pages()
{
  container_t non_free_pages;
Marko Mäkelä's avatar
Marko Mäkelä committed
634 635
  byte *zero_page=
      static_cast<byte *>(aligned_malloc(srv_page_size, srv_page_size));
Marko Mäkelä's avatar
Marko Mäkelä committed
636
  memset(zero_page, 0, srv_page_size);
637 638 639 640 641

  ut_a(!pthread_mutex_lock(&m_mutex));
  for (container_t::const_iterator space_it= m_spaces.begin();
       space_it != m_spaces.end(); ++space_it)
  {
642
    uint32_t space_id = space_it->first;
643 644 645 646
    const std::string &space_name = space_it->second.space_name;
    // There is no need to close tablespaces explixitly as they will be closed
    // in innodb_shutdown().
    xb_load_single_table_tablespace(space_name, false);
Marko Mäkelä's avatar
Marko Mäkelä committed
647
    fil_space_t *space = fil_space_t::get(space_id);
648 649 650
    if (!space)
      die("Can't find space object for space name %s to check corrupted page",
          space_name.c_str());
Marko Mäkelä's avatar
Marko Mäkelä committed
651
    for (std::set<unsigned>::const_iterator page_it=
652 653 654
             space_it->second.pages.begin();
         page_it != space_it->second.pages.end(); ++page_it)
    {
655 656
      if (fseg_page_is_allocated(space, *page_it))
      {
657 658 659 660
        space_info_t &space_info = non_free_pages[space_id];
        space_info.pages.insert(*page_it);
        if (space_info.space_name.empty())
          space_info.space_name = space_name;
Marko Mäkelä's avatar
Marko Mäkelä committed
661
        msg("Error: corrupted page " UINT32PF
662 663 664 665 666
            " of tablespace %s can not be fixed",
            *page_it, space_name.c_str());
      }
      else
      {
Marko Mäkelä's avatar
Marko Mäkelä committed
667 668 669 670 671
        space->reacquire();
        auto err= space
                      ->io(IORequest(IORequest::PUNCH_RANGE),
                           *page_it * srv_page_size, srv_page_size, zero_page)
                      .err;
672
        if (err != DB_SUCCESS)
Marko Mäkelä's avatar
Marko Mäkelä committed
673
          die("Can't zero out corrupted page " UINT32PF " of tablespace %s",
674
              *page_it, space_name.c_str());
Marko Mäkelä's avatar
Marko Mäkelä committed
675
        msg("Corrupted page " UINT32PF
676
            " of tablespace %s was successfully fixed.",
677 678 679
            *page_it, space_name.c_str());
      }
    }
680
    space->flush<true>();
Marko Mäkelä's avatar
Marko Mäkelä committed
681
    space->release();
682 683 684
  }
  m_spaces.swap(non_free_pages);
  ut_a(!pthread_mutex_unlock(&m_mutex));
Marko Mäkelä's avatar
Marko Mäkelä committed
685
  aligned_free(zero_page);
686 687 688 689 690
}

typedef void (*process_single_tablespace_func_t)(const char *dirname,
                                                 const char *filname,
                                                 bool is_remote,
691
                                                 bool skip_node_page0,
692
                                                 uint32_t defer_space_id);
693 694
static dberr_t enumerate_ibd_files(process_single_tablespace_func_t callback);

695
/* ======== Datafiles iterator ======== */
696
struct datafiles_iter_t {
697 698 699 700
	space_list_t::iterator space = fil_system.space_list.end();
	fil_node_t	*node = nullptr;
	bool		started = false;
	std::mutex	mutex;
701 702 703 704
};

/* ======== Datafiles iterator ======== */
static
705 706 707 708 709
fil_node_t *
datafiles_iter_next(datafiles_iter_t *it)
{
	fil_node_t *new_node;

710
	std::lock_guard<std::mutex> _(it->mutex);
711 712 713 714 715 716 717 718 719 720 721

	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;
	}

722 723 724
	it->space = (it->space == fil_system.space_list.end()) ?
		fil_system.space_list.begin() :
		std::next(it->space);
725

726
	while (it->space != fil_system.space_list.end() &&
727
	       (it->space->purpose != FIL_TYPE_TABLESPACE ||
728
		UT_LIST_GET_LEN(it->space->chain) == 0))
729 730
		++it->space;
	if (it->space == fil_system.space_list.end())
731 732 733 734 735 736 737 738 739 740
		goto end;

	it->node = UT_LIST_GET_FIRST(it->space->chain);

end:
	new_node = it->node;

	return new_node;
}

741 742 743 744 745 746 747 748 749 750 751
#ifndef DBUG_OFF
struct dbug_thread_param_t
{
	MYSQL *con;
	const char *query;
	int expect_err;
	int expect_errno;
};


/* Thread procedure used in dbug_start_query_thread. */
752
static void *dbug_execute_in_new_connection(void *arg)
753 754
{
	mysql_thread_init();
755
	dbug_thread_param_t *par= static_cast<dbug_thread_param_t*>(arg);
756 757
	int err = mysql_query(par->con, par->query);
	int err_no = mysql_errno(par->con);
758 759 760 761 762 763 764 765 766 767 768 769
	if(par->expect_err != err)
	{
		msg("FATAL: dbug_execute_in_new_connection : mysql_query '%s' returns %d, instead of expected %d",
			par->query, err, par->expect_err);
		_exit(1);
	}
	if (err && par->expect_errno && par->expect_errno != err_no)
	{
		msg("FATAL: dbug_execute_in_new_connection: mysql_query '%s' returns mysql_errno %d, instead of expected %d",
			par->query, err_no, par->expect_errno);
		_exit(1);
	}
770 771 772
	mysql_close(par->con);
	mysql_thread_end();
	delete par;
773
	return nullptr;
774 775
}

776 777
static pthread_t dbug_alter_thread;

778 779 780 781 782 783 784 785 786 787 788
/*
Execute query from a new connection, in own thread.

@param query - query to be executed
@param wait_state - if not NULL, wait until query from new connection
	reaches this state (value of column State in I_S.PROCESSLIST)
@param expected_err - if 0, query is supposed to finish successfully,
	otherwise query should return error.
@param expected_errno - if not 0, and query finished with error,
	expected mysql_errno()
*/
789
static void dbug_start_query_thread(
790 791 792 793 794 795 796 797 798 799 800
	const char *query,
	const char *wait_state,
	int expected_err,
	int expected_errno)

{
	dbug_thread_param_t *par = new dbug_thread_param_t;
	par->query = query;
	par->expect_err = expected_err;
	par->expect_errno = expected_errno;
	par->con =  xb_mysql_connect();
801 802 803

	mysql_thread_create(0, &dbug_alter_thread, nullptr,
			    dbug_execute_in_new_connection, par);
804 805

	if (!wait_state)
806
		return;
807 808 809 810 811 812 813 814

	char q[256];
	snprintf(q, sizeof(q),
		"SELECT 1 FROM INFORMATION_SCHEMA.PROCESSLIST where ID=%lu"
		" AND Command='Query' AND State='%s'",
		mysql_thread_id(par->con), wait_state);
	for (;;) {
		MYSQL_RES *result = xb_mysql_query(mysql_connection,q, true, true);
815 816 817
		bool exists = mysql_fetch_row(result) != NULL;
		mysql_free_result(result);
		if (exists) {
818 819
			goto end;
		}
820
		msg("Waiting for query '%s' on connection %lu to "
821 822 823 824 825
			" reach state '%s'", query, mysql_thread_id(par->con),
			wait_state);
		my_sleep(1000);
	}
end:
826
	msg("query '%s' on connection %lu reached state '%s'", query,
827 828 829 830
	mysql_thread_id(par->con), wait_state);
}
#endif

831 832
void mdl_lock_all()
{
833 834
  mdl_lock_init();
  datafiles_iter_t it;
835

836 837 838 839 840 841 842 843 844 845 846
  while (fil_node_t *node= datafiles_iter_next(&it))
  {
    const auto id= node->space->id;
    if (const char *name= (fil_is_user_tablespace_id(id) &&
                           node->space->chain.start)
        ? node->space->chain.start->name : nullptr)
      if (check_if_skip_table(filename_to_spacename(name,
                                                    strlen(name)).c_str()))
        continue;
    mdl_lock_table(id);
  }
847
}
848 849


850
// Convert non-null terminated filename to space name
851
// Note that in 10.6 the filename may be an undo file name
852
static std::string filename_to_spacename(const void *filename, size_t len)
853
{
854 855
  char f[FN_REFLEN];
  char *p= 0, *table, *db;
Monty's avatar
Monty committed
856
  DBUG_ASSERT(len < FN_REFLEN);
857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893

  strmake(f, (const char*) filename, len);

#ifdef _WIN32
  for (size_t i = 0; i < len; i++)
  {
    if (f[i] == '\\')
      f[i] = '/';
  }
#endif

  /* Remove extension, if exists */
  if (!(p= strrchr(f, '.')))
    goto err;
  *p= 0;

  /* Find table name */
  if (!(table= strrchr(f, '/')))
    goto err;
  *table = 0;

  /* Find database name */
  db= strrchr(f, '/');
  *table = '/';
  if (!db)
    goto err;
  {
    std::string s(db+1);
    return s;
  }

err:
  /* Not a database/table. Return original (converted) name */
  if (p)
    *p= '.';                                    // Restore removed extension
  std::string s(f);
  return s;
894
}
895

896 897
/** Report an operation to create, delete, or rename a file during backup.
@param[in]	space_id	tablespace identifier
898
@param[in]	type		redo log file operation type
899 900 901 902
@param[in]	name		file name (not NUL-terminated)
@param[in]	len		length of name, in bytes
@param[in]	new_name	new file name (NULL if not rename)
@param[in]	new_len		length of new_name, in bytes (0 if NULL) */
903
static void backup_file_op(uint32_t space_id, int type,
904 905 906
	const byte* name, ulint len,
	const byte* new_name, ulint new_len)
{
907

908 909 910
	ut_ad(name);
	ut_ad(len);
	ut_ad(!new_name == !new_len);
911
	mysql_mutex_assert_owner(&recv_sys.mutex);
912

913 914 915 916 917 918
	switch(type) {
	case FILE_CREATE:
	{
		std::string space_name = filename_to_spacename(name, len);
		ddl_tracker.id_to_name[space_id] = space_name;
		ddl_tracker.delete_defer(space_id, space_name);
919
		msg("DDL tracking : create %u \"%.*s\"", space_id, int(len), name);
920
	}
921 922 923 924 925 926 927 928 929 930 931 932 933 934
	break;
	case FILE_MODIFY:
		ddl_tracker.insert_defer_id(
			space_id, filename_to_spacename(name, len));
		break;
	case FILE_RENAME:
	{
		std::string new_space_name = filename_to_spacename(
						new_name, new_len);
		std::string old_space_name = filename_to_spacename(
						name, len);
		ddl_tracker.id_to_name[space_id] = new_space_name;
		ddl_tracker.rename_defer(space_id, old_space_name,
					 new_space_name);
935
		msg("DDL tracking : rename %u \"%.*s\",\"%.*s\"",
936
			space_id, int(len), name, int(new_len), new_name);
937 938 939
	}
	break;
	case FILE_DELETE:
940
		ddl_tracker.drops.insert(space_id);
941 942
		ddl_tracker.delete_defer(
			space_id, filename_to_spacename(name, len));
943
		msg("DDL tracking : delete %u \"%.*s\"", space_id, int(len), name);
944 945 946 947
		break;
	default:
		ut_ad(0);
		break;
948 949
	}
}
950 951


952 953 954 955 956 957 958 959 960
/*
 This callback is called if DDL operation is detected,
 at the end of backup

 Normally, DDL operations are blocked due to FTWRL,
 but in rare cases of --no-lock, they are not.

 We will abort backup in this case.
*/
961
static void backup_file_op_fail(uint32_t space_id, int type,
962 963 964
	const byte* name, ulint len,
	const byte* new_name, ulint new_len)
{
965 966 967
	bool fail = false;
	switch(type) {
	case FILE_CREATE:
968
		msg("DDL tracking : create %u \"%.*s\"", space_id, int(len), name);
969 970 971 972 973 974
		fail = !check_if_skip_table(
				filename_to_spacename(name, len).c_str());
		break;
	case FILE_MODIFY:
		break;
	case FILE_RENAME:
975
		msg("DDL tracking : rename %u \"%.*s\",\"%.*s\"",
976
			space_id, int(len), name, int(new_len), new_name);
977 978 979 980 981 982 983 984
		fail = !check_if_skip_table(
				filename_to_spacename(name, len).c_str())
		       || !check_if_skip_table(
				filename_to_spacename(new_name, new_len).c_str());
		break;
	case FILE_DELETE:
		fail = !check_if_skip_table(
				filename_to_spacename(name, len).c_str());
985
		msg("DDL tracking : delete %u \"%.*s\"", space_id, int(len), name);
986 987 988 989
		break;
	default:
		ut_ad(0);
		break;
990
	}
991

992
	if (fail) {
993
		ut_a(opt_no_lock);
994
		die("DDL operation detected in the late phase of backup."
995
			"Backup is inconsistent. Remove --no-lock option to fix.");
996 997 998
	}
}

999 1000 1001 1002
static void backup_undo_trunc(uint32_t space_id)
{
  undo_trunc_ids.insert(space_id);
}
1003

1004 1005
/* Function to store the space id of page0 INIT_PAGE
@param	space_id	space id which has page0 init page */
1006
static void backup_first_page_op(space_id_t space_id)
1007 1008 1009
{
  first_page_init_ids.insert(space_id);
}
1010

1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041
/*
  Retrieve default data directory, to be used with --copy-back.

  On Windows, default datadir is ..\data, relative to the
  directory where mariabackup.exe is located(usually "bin")

  Elsewhere, the compiled-in constant MYSQL_DATADIR is used.
*/
static char *get_default_datadir() {
	static char ddir[] = MYSQL_DATADIR;
#ifdef _WIN32
	static char buf[MAX_PATH];
	DWORD size = (DWORD)sizeof(buf) - 1;
	if (GetModuleFileName(NULL, buf, size) <= size)
	{
		char *p;
		if ((p = strrchr(buf, '\\')))
		{
			*p = 0;
			if ((p = strrchr(buf, '\\')))
			{
				strncpy(p + 1, "data", buf + MAX_PATH - p);
				return buf;
			}
		}
	}
#endif
	return ddir;
}


1042 1043 1044 1045 1046 1047
/* ======== Date copying thread context ======== */

typedef struct {
	datafiles_iter_t 	*it;
	uint			num;
	uint			*count;
1048
	pthread_mutex_t*	count_mutex;
1049
	CorruptedPages *corrupted_pages;
1050
	Backup_datasinks *datasinks;
1051 1052 1053
} data_thread_ctxt_t;

/* ======== for option and variables ======== */
1054
#include <../../client/client_priv.h>
1055 1056 1057

enum options_xtrabackup
{
1058 1059
  OPT_XTRA_TARGET_DIR= 1000, /* make sure it is larger
                                than OPT_MAX_CLIENT_OPTION */
1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075
  OPT_XTRA_BACKUP,
  OPT_XTRA_PREPARE,
  OPT_XTRA_EXPORT,
  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_TABLES,
  OPT_XTRA_TABLES_FILE,
  OPT_XTRA_DATABASES,
  OPT_XTRA_DATABASES_FILE,
  OPT_XTRA_PARALLEL,
1076
  OPT_XTRA_EXTENDED_VALIDATION,
Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
1077
  OPT_XTRA_ENCRYPTED_BACKUP,
1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088
  OPT_XTRA_STREAM,
  OPT_XTRA_COMPRESS,
  OPT_XTRA_COMPRESS_THREADS,
  OPT_XTRA_COMPRESS_CHUNK_SIZE,
  OPT_LOG,
  OPT_INNODB,
  OPT_INNODB_DATA_FILE_PATH,
  OPT_INNODB_DATA_HOME_DIR,
  OPT_INNODB_ADAPTIVE_HASH_INDEX,
  OPT_INNODB_DOUBLEWRITE,
  OPT_INNODB_FILE_PER_TABLE,
1089
  OPT_INNODB_FLUSH_METHOD,
1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106
  OPT_INNODB_LOG_GROUP_HOME_DIR,
  OPT_INNODB_MAX_DIRTY_PAGES_PCT,
  OPT_INNODB_MAX_PURGE_LAG,
  OPT_INNODB_STATUS_FILE,
  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_BUFFER_POOL_FILENAME,
  OPT_INNODB_LOCK_WAIT_TIMEOUT,
  OPT_INNODB_LOG_BUFFER_SIZE,
1107 1108 1109
#if defined __linux__ || defined _WIN32
  OPT_INNODB_LOG_FILE_BUFFERING,
#endif
1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145
  OPT_INNODB_LOG_FILE_SIZE,
  OPT_INNODB_OPEN_FILES,
  OPT_XTRA_DEBUG_SYNC,
  OPT_INNODB_CHECKSUM_ALGORITHM,
  OPT_INNODB_UNDO_DIRECTORY,
  OPT_INNODB_UNDO_TABLESPACES,
  OPT_XTRA_INCREMENTAL_FORCE_SCAN,
  OPT_DEFAULTS_GROUP,
  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_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,

Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
1146 1147
  OPT_XTRA_TABLES_EXCLUDE,
  OPT_XTRA_DATABASES_EXCLUDE,
1148
  OPT_PROTOCOL,
Marko Mäkelä's avatar
Marko Mäkelä committed
1149
  OPT_INNODB_COMPRESSION_LEVEL,
1150 1151
  OPT_LOCK_DDL_PER_TABLE,
  OPT_ROCKSDB_DATADIR,
1152
  OPT_BACKUP_ROCKSDB,
1153
  OPT_XTRA_CHECK_PRIVILEGES,
Marko Mäkelä's avatar
Marko Mäkelä committed
1154
  OPT_XTRA_MYSQLD_ARGS,
1155
  OPT_XB_IGNORE_INNODB_PAGE_CORRUPTION,
1156 1157
  OPT_INNODB_FORCE_RECOVERY,
  OPT_ARIA_LOG_DIR_PATH
1158 1159
};

1160 1161 1162
struct my_option xb_client_options[]= {
    {"verbose", 'V', "display verbose output", (G_PTR *) &verbose,
     (G_PTR *) &verbose, 0, GET_BOOL, NO_ARG, FALSE, 0, 0, 0, 0, 0},
1163
    {"version", 'v', "print version information",
1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184
     (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},
    {"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},
    {"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,
1185 1186
     "The value is used in place of innodb_buffer_pool_size. "
     "This option is only relevant when the --prepare option is specified.",
1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369
     (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},
    {"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_DATABASES_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},

    {"stream", OPT_XTRA_STREAM,
     "Stream all backup files to the standard output "
     "in the specified format."
     "Supported format is 'mbstream' or 'xbstream'.",
     (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},

    {"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 MariaDB Galera 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 "
1370
     "transfers. When this option is specified, " XB_TOOL_NAME " uses rsync "
1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381
     "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 "
1382
     "overwritten. If --copy-back or --move-back has to copy a file from "
1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447
     "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},

    {"protocol", OPT_PROTOCOL,
     "The protocol to use for connection (tcp, socket, pipe, memory).", 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 "
1448
     XB_HISTORY_TABLE " history record to base an "
1449 1450 1451 1452 1453 1454
     "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 "
1455 1456
     "that name, no successful backups by that name), an error will be returned."
     " It is used with the --incremental option.",
1457 1458 1459 1460 1461 1462
     (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 "
1463
     "stored in the " XB_HISTORY_TABLE " table to base an "
1464 1465
     "incremental backup on. --incremental-history-name, "
     "--incremental-basedir and --incremental-lsn. If no valid lsn can be "
1466 1467
     "found (no success record with that uuid), an error will be returned."
     " It is used with the --incremental option.",
1468 1469 1470 1471 1472 1473 1474 1475 1476 1477
     (uchar *) &opt_incremental_history_uuid,
     (uchar *) &opt_incremental_history_uuid, 0, GET_STR, REQUIRED_ARG, 0, 0,
     0, 0, 0, 0},

    {"remove-original", OPT_REMOVE_ORIGINAL,
     "Remove .qp files after 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 "
1478
     "before " XB_TOOL_NAME " will issue the global lock. Default is all.",
1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491
     (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 "
1492
     XB_HISTORY_TABLE " table. An optional history "
1493 1494 1495 1496 1497
     "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,
1498
     "This option specifies the number of seconds " XB_TOOL_NAME " waits "
1499 1500
     "between starting FLUSH TABLES WITH READ LOCK and killing those "
     "queries that block it. Default is 0 seconds, which means "
1501
     XB_TOOL_NAME " will not attempt to kill any queries.",
1502 1503 1504 1505 1506
     (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,
1507
     "This option specifies time in seconds that " XB_TOOL_NAME " should wait "
1508
     "for queries that would block FTWRL before running it. If there are "
1509 1510
     "still such queries when the timeout expires, " XB_TOOL_NAME " terminates "
     "with an error. Default is 0, in which case " XB_TOOL_NAME " does not "
1511 1512 1513 1514 1515 1516
     "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 "
1517
     XB_TOOL_NAME " to detect long-running queries with a non-zero value "
1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532
     "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},


    {"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,
1533
     "This option controls how backup should retrieve server's binary log "
1534
     "coordinates corresponding to the backup. Possible values are OFF, ON, "
1535
     "LOCKLESS and AUTO.",
1536 1537 1538 1539 1540 1541 1542 1543
     &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},
1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554

    {"log-innodb-page-corruption", OPT_XB_IGNORE_INNODB_PAGE_CORRUPTION,
     "Continue backup if innodb corrupted pages are found. The pages are "
     "logged in " MB_CORRUPTED_PAGES_FILE
     " and backup is finished with error. "
     "--prepare will try to fix corrupted pages. If " MB_CORRUPTED_PAGES_FILE
     " exists after --prepare in base backup directory, backup still contains "
     "corrupted pages and can not be considered as consistent.",
     &opt_log_innodb_page_corruption, &opt_log_innodb_page_corruption, 0,
     GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},

1555
#define MYSQL_CLIENT
1556
#include "sslopt-longopts.h"
1557
#undef MYSQL_CLIENT
1558
    {0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}};
1559 1560 1561

uint xb_client_options_count = array_elements(xb_client_options);

1562 1563 1564 1565 1566
#ifndef DBUG_OFF
/** Parameters to DBUG */
static const char *dbug_option;
#endif

1567 1568 1569 1570 1571
#ifdef HAVE_URING
extern const char *io_uring_may_be_unsafe;
bool innodb_use_native_aio_default();
#endif

1572 1573 1574 1575 1576 1577
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 "
1578
#if defined(_WIN32)
1579 1580 1581 1582 1583 1584 1585 1586
   "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,
Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
1587 1588
   "Number of threads to use for parallel datafiles transfer. "
   "The default value is 1.",
1589 1590 1591
   (G_PTR*) &xtrabackup_parallel, (G_PTR*) &xtrabackup_parallel, 0, GET_INT,
   REQUIRED_ARG, 1, 1, INT_MAX, 0, 0, 0},

1592
  {"extended_validation", OPT_XTRA_EXTENDED_VALIDATION,
1593 1594 1595
   "Enable extended validation for Innodb data pages during backup phase. "
   "Will slow down backup considerably, in case encryption is used. "
   "May fail if tables are created during the backup.",
1596 1597 1598 1599
   (G_PTR*)&opt_extended_validation,
   (G_PTR*)&opt_extended_validation,
   0, GET_BOOL, NO_ARG, FALSE, 0, 0, 0, 0, 0},

Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
1600
  {"encrypted_backup", OPT_XTRA_ENCRYPTED_BACKUP,
1601
   "In --backup, assume that nonzero key_version implies that the page"
Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
1602
   " is encrypted. Use --backup --skip-encrypted-backup to allow"
1603
   " copying unencrypted that were originally created before MySQL 5.1.48.",
Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
1604 1605
   (G_PTR*)&opt_encrypted_backup,
   (G_PTR*)&opt_encrypted_backup,
1606
   0, GET_BOOL, NO_ARG, TRUE, 0, 0, 0, 0, 0},
1607

1608
  {"log", OPT_LOG, "Ignored option for MySQL option compatibility",
1609 1610 1611
   (G_PTR*) &log_ignored_opt, (G_PTR*) &log_ignored_opt, 0,
   GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},

1612
  {"log_bin", OPT_LOG, "Base name for the log sequence",
1613
   &opt_log_bin, &opt_log_bin, 0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
1614

1615
  {"innodb", OPT_INNODB, "Ignored option for MySQL option compatibility",
1616 1617
   (G_PTR*) &innobase_ignored_opt, (G_PTR*) &innobase_ignored_opt, 0,
   GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
1618
#ifdef BTR_CUR_HASH_ADAPT
1619
  {"innodb_adaptive_hash_index", OPT_INNODB_ADAPTIVE_HASH_INDEX,
1620
   "Enable InnoDB adaptive hash index (disabled by default).",
1621 1622
   &btr_search_enabled,
   &btr_search_enabled,
1623
   0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
1624
#endif /* BTR_CUR_HASH_ADAPT */
1625 1626
  {"innodb_autoextend_increment", OPT_INNODB_AUTOEXTEND_INCREMENT,
   "Data file autoextend increment in megabytes",
1627 1628
   (G_PTR*) &sys_tablespace_auto_extend_increment,
   (G_PTR*) &sys_tablespace_auto_extend_increment,
1629
   0, GET_UINT, REQUIRED_ARG, 8, 1, 1000, 0, 1, 0},
1630 1631
  {"innodb_data_file_path", OPT_INNODB_DATA_FILE_PATH,
   "Path to individual files and their sizes.", &innobase_data_file_path,
1632
   &innobase_data_file_path, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1633 1634
  {"innodb_data_home_dir", OPT_INNODB_DATA_HOME_DIR,
   "The common part for InnoDB table spaces.", &innobase_data_home_dir,
1635
   &innobase_data_home_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1636 1637
  {"innodb_doublewrite", OPT_INNODB_DOUBLEWRITE,
   "Enable InnoDB doublewrite buffer during --prepare.",
1638 1639
   (G_PTR*) &srv_use_doublewrite_buf,
   (G_PTR*) &srv_use_doublewrite_buf, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657
  {"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.",
1658 1659
   (G_PTR*) &srv_file_per_table,
   (G_PTR*) &srv_file_per_table, 0, GET_BOOL, NO_ARG,
1660
   FALSE, 0, 0, 0, 0, 0},
1661

1662 1663 1664 1665
  {"innodb_flush_method", OPT_INNODB_FLUSH_METHOD,
   "With which method to flush data.",
   &srv_file_flush_method, &srv_file_flush_method,
   &innodb_flush_method_typelib, GET_ENUM, REQUIRED_ARG,
1666
   IF_WIN(SRV_ALL_O_DIRECT_FSYNC, SRV_O_DIRECT), 0, 0, 0, 0, 0},
1667

1668
  {"innodb_log_buffer_size", OPT_INNODB_LOG_BUFFER_SIZE,
1669 1670 1671 1672
   "Redo log buffer size in bytes.",
   (G_PTR*) &log_sys.buf_size, (G_PTR*) &log_sys.buf_size, 0,
   IF_WIN(GET_ULL,GET_ULONG), REQUIRED_ARG, 2U << 20,
   2U << 20, SIZE_T_MAX, 0, 4096, 0},
1673 1674 1675 1676 1677 1678 1679
#if defined __linux__ || defined _WIN32
  {"innodb_log_file_buffering", OPT_INNODB_LOG_FILE_BUFFERING,
   "Whether the file system cache for ib_logfile0 is enabled during --backup",
   (G_PTR*) &log_sys.log_buffered,
   (G_PTR*) &log_sys.log_buffered, 0, GET_BOOL, NO_ARG,
   TRUE, 0, 0, 0, 0, 0},
#endif
1680
  {"innodb_log_file_size", OPT_INNODB_LOG_FILE_SIZE,
1681 1682
   "Ignored for mysqld option compatibility",
   (G_PTR*) &srv_log_file_size, (G_PTR*) &srv_log_file_size, 0,
1683 1684
   GET_ULL, REQUIRED_ARG, 96 << 20, 4 << 20,
   std::numeric_limits<ulonglong>::max(), 0, 4096, 0},
1685 1686
  {"innodb_log_group_home_dir", OPT_INNODB_LOG_GROUP_HOME_DIR,
   "Path to InnoDB log files.", &srv_log_group_home_dir,
1687
   &srv_log_group_home_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
1688 1689 1690 1691 1692 1693 1694
  {"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_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,
1695 1696 1697 1698 1699 1700
#ifdef HAVE_URING
   innodb_use_native_aio_default(),
#else
   TRUE,
#endif
   0, 0, 0, 0, 0},
1701 1702 1703 1704 1705 1706 1707
  {"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_buffer_pool_filename", OPT_INNODB_BUFFER_POOL_FILENAME,
1708
   "Ignored for mysqld option compatibility",
1709 1710 1711 1712
   (G_PTR*) &innobase_buffer_pool_filename,
   (G_PTR*) &innobase_buffer_pool_filename,
   0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},

1713 1714 1715 1716 1717
#ifndef DBUG_OFF /* unfortunately "debug" collides with existing options */
  {"dbug", '#', "Built in DBUG debugger.",
   &dbug_option, &dbug_option, 0, GET_STR, OPT_ARG,
   0, 0, 0, 0, 0, 0},
#endif
1718 1719 1720

  {"innodb_checksum_algorithm", OPT_INNODB_CHECKSUM_ALGORITHM,
  "The algorithm InnoDB uses for page checksumming. [CRC32, STRICT_CRC32, "
1721
   "FULL_CRC32, STRICT_FULL_CRC32]", &srv_checksum_algorithm,
1722
   &srv_checksum_algorithm, &innodb_checksum_algorithm_typelib, GET_ENUM,
1723
   REQUIRED_ARG, SRV_CHECKSUM_ALGORITHM_CRC32, 0, 0, 0, 0, 0},
1724

1725 1726
  {"innodb_undo_directory", OPT_INNODB_UNDO_DIRECTORY,
   "Directory where undo tablespace files live, this path can be absolute.",
1727
   &srv_undo_dir, &srv_undo_dir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0,
1728 1729 1730 1731 1732
   0},

  {"innodb_undo_tablespaces", OPT_INNODB_UNDO_TABLESPACES,
   "Number of undo tablespaces to use.",
   (G_PTR*)&srv_undo_tablespaces, (G_PTR*)&srv_undo_tablespaces,
1733
   0, GET_UINT, REQUIRED_ARG, 0, 0, 126, 0, 1, 0},
1734

1735 1736 1737 1738 1739
  {"innodb_compression_level", OPT_INNODB_COMPRESSION_LEVEL,
   "Compression level used for zlib compression.",
   (G_PTR*)&page_zip_level, (G_PTR*)&page_zip_level,
   0, GET_UINT, REQUIRED_ARG, 6, 0, 9, 0, 0, 0},

1740 1741 1742 1743
  {"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},

1744
  {"plugin-dir", OPT_PLUGIN_DIR,
1745
  "Server plugin directory. Used to load plugins during 'prepare' phase."
1746
  "Has no effect in the 'backup' phase (plugin directory during backup is the same as server's)",
1747 1748 1749
  &xb_plugin_dir, &xb_plugin_dir,
  0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },

1750 1751 1752 1753 1754
  {"aria_log_dir_path", OPT_ARIA_LOG_DIR_PATH,
   "Path to individual files and their sizes.",
   &aria_log_dir_path, &aria_log_dir_path,
   0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},

1755 1756 1757 1758 1759
  {"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},

1760
  {"lock-ddl-per-table", OPT_LOCK_DDL_PER_TABLE, "Lock DDL for each table "
1761
   "before backup starts to copy it and until the backup is completed.",
1762 1763 1764
   (uchar*) &opt_lock_ddl_per_table, (uchar*) &opt_lock_ddl_per_table, 0,
   GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},

1765 1766 1767 1768 1769
  {"rocksdb-datadir", OPT_ROCKSDB_DATADIR, "RocksDB data directory."
   "This option is only  used with --copy-back or --move-back option",
  &xb_rocksdb_datadir, &xb_rocksdb_datadir,
  0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },

1770
  {"rocksdb-backup", OPT_BACKUP_ROCKSDB, "Backup rocksdb data, if rocksdb plugin is installed."
1771 1772 1773 1774
   "Used only with --backup option. Can be useful for partial backups, to exclude all rocksdb data",
   &xb_backup_rocksdb, &xb_backup_rocksdb,
   0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0 },

1775
  {"check-privileges", OPT_XTRA_CHECK_PRIVILEGES, "Check database user "
1776 1777 1778
   "privileges fro the backup user",
   &opt_check_privileges, &opt_check_privileges,
   0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0 },
1779

1780 1781 1782 1783 1784 1785 1786
  {"innodb_force_recovery", OPT_INNODB_FORCE_RECOVERY,
   "(for --prepare): Crash recovery mode (ignores "
   "page corruption; for emergencies only).",
   (G_PTR*)&srv_force_recovery,
   (G_PTR*)&srv_force_recovery,
   0, GET_ULONG, OPT_ARG, 0, 0, SRV_FORCE_IGNORE_CORRUPT, 0, 0, 0},

1787 1788 1789 1790 1791 1792 1793
    {"mysqld-args", OPT_XTRA_MYSQLD_ARGS,
     "All arguments that follow this argument are considered as server "
     "options, and if some of them are not supported by mariabackup, they "
     "will be ignored.",
     (G_PTR *) &xtrabackup_mysqld_args, (G_PTR *) &xtrabackup_mysqld_args, 0,
     GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},

Oleksandr Byelkin's avatar
Oleksandr Byelkin committed
1794 1795
    {"help", '?',
     "Display this help and exit.",
1796
     (G_PTR *) &xtrabackup_help, (G_PTR *) &xtrabackup_help, 0,
Oleksandr Byelkin's avatar
Oleksandr Byelkin committed
1797 1798
     GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},

1799 1800 1801 1802 1803
  { 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);

1804

1805
static std::set<std::string> tables_for_export;
1806

1807
static void append_export_table(const char *dbname, const char *tablename,
1808
                                bool is_remote, bool skip_node_page0,
1809
                                uint32_t defer_space_id)
1810 1811 1812 1813 1814 1815 1816 1817 1818
{
  if(dbname && tablename && !is_remote)
  {
    char buf[3*FN_REFLEN];
    snprintf(buf,sizeof(buf),"%s/%s",dbname, tablename);
    // trim .ibd
    char *p=strrchr(buf, '.');
    if (p) *p=0;

1819 1820 1821 1822 1823 1824 1825 1826 1827
    std::string name=ut_get_name(0, buf);
    /* Strip partition name comment from table name, if any */
    if (ends_with(name.c_str(), "*/"))
    {
      size_t pos= name.rfind("/*");
      if (pos != std::string::npos)
         name.resize(pos);
    }
    tables_for_export.insert(name);
1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841
  }
}


#define BOOTSTRAP_FILENAME "mariabackup_prepare_for_export.sql"

static int create_bootstrap_file()
{
  FILE *f= fopen(BOOTSTRAP_FILENAME,"wb");
  if(!f)
   return -1;

  fputs("SET NAMES UTF8;\n",f);
  enumerate_ibd_files(append_export_table);
1842 1843
  for (std::set<std::string>::iterator it = tables_for_export.begin();
       it != tables_for_export.end(); it++)
1844
  {
1845
     const char *tab = it->c_str();
1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869
     fprintf(f,
     "BEGIN NOT ATOMIC "
       "DECLARE CONTINUE HANDLER FOR NOT FOUND,SQLEXCEPTION BEGIN END;"
       "FLUSH TABLES %s FOR EXPORT;"
     "END;\n"
     "UNLOCK TABLES;\n",
      tab);
  }
  fclose(f);
  return 0;
}

static int prepare_export()
{
  int err= -1;

  char cmdline[2*FN_REFLEN];
  FILE *outf;

  if (create_bootstrap_file())
    return -1;

  // Process defaults-file , it can have some --lc-language stuff,
  // which is* unfortunately* still necessary to get mysqld up
1870
  if (strncmp(orig_argv1,"--defaults-file=", 16) == 0)
1871
  {
1872
    snprintf(cmdline, sizeof cmdline,
1873
      IF_WIN("\"","") "\"%s\" --mysqld \"%s\""
1874
      " --defaults-extra-file=./backup-my.cnf --defaults-group-suffix=%s --datadir=."
1875
      " --innodb --innodb-fast-shutdown=0 --loose-partition"
1876
      " --innodb_purge_rseg_truncate_frequency=1 --innodb-buffer-pool-size=%llu"
1877
      " --console --skip-log-error --skip-log-bin --bootstrap %s< "
1878
      BOOTSTRAP_FILENAME IF_WIN("\"",""),
1879
      mariabackup_exe,
1880
      orig_argv1, (my_defaults_group_suffix?my_defaults_group_suffix:""),
1881
      xtrabackup_use_memory,
1882
      (srv_force_recovery ? "--innodb-force-recovery=1 " : ""));
1883 1884 1885
  }
  else
  {
1886
    snprintf(cmdline, sizeof cmdline,
1887
      IF_WIN("\"","") "\"%s\" --mysqld"
1888
      " --defaults-file=./backup-my.cnf --defaults-group-suffix=%s --datadir=."
1889
      " --innodb --innodb-fast-shutdown=0 --loose-partition"
1890
      " --innodb_purge_rseg_truncate_frequency=1 --innodb-buffer-pool-size=%llu"
1891
      " --console --log-error= --skip-log-bin --bootstrap %s< "
1892
      BOOTSTRAP_FILENAME IF_WIN("\"",""),
1893
      mariabackup_exe,
1894
      (my_defaults_group_suffix?my_defaults_group_suffix:""),
1895
      xtrabackup_use_memory,
1896
      (srv_force_recovery ? "--innodb-force-recovery=1 " : ""));
1897 1898 1899 1900 1901 1902 1903 1904
  }

  msg("Prepare export : executing %s\n", cmdline);
  fflush(stderr);

  outf= popen(cmdline,"r");
  if (!outf)
    goto end;
1905

1906
  char outline[FN_REFLEN];
1907
  while (fgets(outline, FN_REFLEN - 1, outf))
1908 1909 1910 1911 1912 1913 1914 1915
    fprintf(stderr,"%s",outline);

  err = pclose(outf);
end:
  unlink(BOOTSTRAP_FILENAME);
  return err;
}

1916
static const char *xb_client_default_groups[]= {
1917
    "client", "client-server", "client-mariadb", 0, 0, 0};
1918

1919 1920
static const char *backup_default_groups[]= {
    "xtrabackup", "mariabackup", "mariadb-backup", 0, 0, 0};
1921 1922 1923

static void print_version(void)
{
1924
  fprintf(stderr, "%s based on MariaDB server %s %s (%s)\n",
1925
      my_progname, MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE);
1926 1927
}

1928 1929 1930 1931 1932 1933
static void concatenate_default_groups(std::vector<const char*> &backup_load_groups, const char **default_groups)
{
  for ( ; *default_groups ; default_groups++)
    backup_load_groups.push_back(*default_groups);
}

1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952
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");

1953
  printf("Usage: %s [--defaults-file=#] [--backup | --prepare | --copy-back | --move-back] [OPTIONS]\n",my_progname);
1954 1955 1956 1957 1958
  std::vector<const char*> backup_load_default_groups;
  concatenate_default_groups(backup_load_default_groups, backup_default_groups);
  concatenate_default_groups(backup_load_default_groups, load_default_groups);
  backup_load_default_groups.push_back(nullptr);
  print_defaults("my", &backup_load_default_groups[0]);
1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980
  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
1981
xb_get_one_option(const struct my_option *opt,
1982
		  const char *argument, const char *)
1983
{
1984
  switch(opt->id) {
1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011
  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;

2012
  case OPT_INNODB_FLUSH_METHOD:
2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023
#ifdef _WIN32
    /* From: storage/innobase/handler/ha_innodb.cc:innodb_init_params */
    switch (srv_file_flush_method) {
    case SRV_ALL_O_DIRECT_FSYNC + 1 /* "async_unbuffered"="unbuffered" */:
      srv_file_flush_method= SRV_ALL_O_DIRECT_FSYNC;
      break;
    case SRV_ALL_O_DIRECT_FSYNC + 2 /* "normal"="fsync" */:
      srv_file_flush_method= SRV_FSYNC;
      break;
    }
#endif
2024 2025 2026 2027 2028
    ut_a(srv_file_flush_method
	 <= IF_WIN(SRV_ALL_O_DIRECT_FSYNC, SRV_O_DIRECT_NO_FSYNC));
    ADD_PRINT_PARAM_OPT(innodb_flush_method_names[srv_file_flush_method]);
    break;

2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045
  case OPT_INNODB_PAGE_SIZE:

    ADD_PRINT_PARAM_OPT(innobase_page_size);
    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:

2046
    ut_a(srv_checksum_algorithm <= SRV_CHECKSUM_ALGORITHM_STRICT_FULL_CRC32);
2047 2048 2049 2050

    ADD_PRINT_PARAM_OPT(innodb_checksum_algorithm_names[srv_checksum_algorithm]);
    break;

2051 2052 2053 2054
  case OPT_INNODB_COMPRESSION_LEVEL:
    ADD_PRINT_PARAM_OPT(page_zip_level);
    break;

2055 2056 2057 2058 2059
  case OPT_INNODB_BUFFER_POOL_FILENAME:

    ADD_PRINT_PARAM_OPT(innobase_buffer_pool_filename);
    break;

2060 2061 2062 2063 2064 2065 2066
  case OPT_INNODB_FORCE_RECOVERY:

    if (srv_force_recovery) {
        ADD_PRINT_PARAM_OPT(srv_force_recovery);
    }
    break;

2067 2068 2069 2070
  case OPT_ARIA_LOG_DIR_PATH:
    ADD_PRINT_PARAM_OPT(aria_log_dir_path);
    break;

2071 2072 2073 2074 2075
  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:
2076 2077
    if (!strcasecmp(argument, "mbstream") ||
        !strcasecmp(argument, "xbstream"))
2078 2079 2080
      xtrabackup_stream_fmt = XB_STREAM_FMT_XBSTREAM;
    else
    {
2081
      msg("Invalid --stream argument: %s", argument);
2082 2083 2084 2085 2086 2087 2088 2089 2090
      return 1;
    }
    xtrabackup_stream = TRUE;
    break;
  case OPT_XTRA_COMPRESS:
    if (argument == NULL)
      xtrabackup_compress_alg = "quicklz";
    else if (strcasecmp(argument, "quicklz"))
    {
2091
      msg("Invalid --compress argument: %s", argument);
2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110
      return 1;
    }
    xtrabackup_compress = 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':
2111
    opt_password = argument;
2112
    break;
2113 2114 2115
  case OPT_PROTOCOL:
    if (argument)
    {
2116 2117 2118 2119 2120 2121
      if ((opt_protocol= find_type_with_warning(argument, &sql_protocol_typelib,
                                                opt->name)) <= 0)
      {
        sf_leaking_memory= 1; /* no memory leak reports here */
        exit(1);
      }
2122 2123
    }
    break;
2124
#define MYSQL_CLIENT
2125
#include "sslopt-case.h"
2126
#undef MYSQL_CLIENT
2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141

  case '?':
    usage();
    exit(EXIT_SUCCESS);
    break;
  case 'v':
    print_version();
    exit(EXIT_SUCCESS);
    break;
  default:
    break;
  }
  return 0;
}

2142
static bool innodb_init_param()
2143
{
2144
	srv_is_being_started = TRUE;
2145 2146 2147
	/* === some variables from mysqld === */
	memset((G_PTR) &mysql_tmpdir_list, 0, sizeof(mysql_tmpdir_list));

Marko Mäkelä's avatar
Marko Mäkelä committed
2148 2149
	if (init_tmpdir(&mysql_tmpdir_list, opt_mysql_tmpdir)) {
		msg("init_tmpdir() failed");
2150
		return true;
Marko Mäkelä's avatar
Marko Mäkelä committed
2151
	}
2152
	xtrabackup_tmpdir = my_tmpdir(&mysql_tmpdir_list);
2153 2154 2155 2156 2157
	/* dummy for initialize all_charsets[] */
	get_charset_name(0);

	srv_page_size = 0;
	srv_page_size_shift = 0;
2158 2159 2160
#ifdef BTR_CUR_HASH_ADAPT
	btr_ahi_parts = 1;
#endif /* BTR_CUR_HASH_ADAPT */
2161 2162

	if (innobase_page_size != (1LL << 14)) {
2163
		size_t n_shift = get_bit_shift(size_t(innobase_page_size));
2164 2165

		if (n_shift >= 12 && n_shift <= UNIV_PAGE_SIZE_SHIFT_MAX) {
2166
			srv_page_size_shift = uint32_t(n_shift);
2167
			srv_page_size = 1U << n_shift;
2168
			msg("InnoDB: The universal page size of the "
2169
			    "database is set to %lu.", srv_page_size);
2170
		} else {
Marko Mäkelä's avatar
Marko Mäkelä committed
2171
			msg("invalid value of "
2172
			    "innobase_page_size: %lld", innobase_page_size);
2173
			goto error;
2174 2175 2176
		}
	} else {
		srv_page_size_shift = 14;
2177
		srv_page_size = 1U << 14;
2178 2179 2180 2181 2182
	}

	/* Check that values don't overflow on 32-bit systems. */
	if (sizeof(ulint) == 4) {
		if (xtrabackup_use_memory > UINT_MAX32) {
2183
			msg("mariabackup: use-memory can't be over 4GB"
2184
			    " on 32-bit systems");
2185 2186 2187
		}
	}

2188 2189
	static char default_path[2] = { FN_CURLIB, 0 };
	fil_path_to_mysql_datadir = default_path;
2190 2191 2192 2193

	/* Set InnoDB initialization parameters according to the values
	read from MySQL .cnf file */

2194
	if (xtrabackup_backup) {
2195
		msg("mariabackup: using the following InnoDB configuration:");
2196
	} else {
2197
		msg("mariabackup: using the following InnoDB configuration "
2198
		    "for recovery:");
2199 2200 2201 2202 2203 2204
	}

	/*--------------- Data files -------------------------*/

	/* The default dir for data files is the datadir of MySQL */

2205
	srv_data_home = (xtrabackup_backup && innobase_data_home_dir
2206
			 ? innobase_data_home_dir : default_path);
2207
	msg("innodb_data_home_dir = %s", srv_data_home);
2208 2209 2210 2211 2212 2213 2214 2215

	/* 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";
	}
2216
	msg("innodb_data_file_path = %s",
2217 2218
	    innobase_data_file_path);

2219 2220
	srv_sys_space.set_space_id(TRX_SYS_SPACE);
	srv_sys_space.set_path(srv_data_home);
2221 2222 2223 2224 2225 2226 2227 2228 2229
	switch (srv_checksum_algorithm) {
	case SRV_CHECKSUM_ALGORITHM_FULL_CRC32:
	case SRV_CHECKSUM_ALGORITHM_STRICT_FULL_CRC32:
		srv_sys_space.set_flags(FSP_FLAGS_FCRC32_MASK_MARKER
					| FSP_FLAGS_FCRC32_PAGE_SSIZE());
		break;
	default:
		srv_sys_space.set_flags(FSP_FLAGS_PAGE_SSIZE());
	}
2230

2231
	if (!srv_sys_space.parse_params(innobase_data_file_path, true)) {
2232 2233 2234
		goto error;
	}

2235 2236 2237
	srv_sys_space.normalize_size();
	srv_lock_table_size = 5 * (srv_buf_pool_size >> srv_page_size_shift);

2238 2239 2240 2241
	/* -------------- Log files ---------------------------*/

	/* The default dir for log files is the datadir of MySQL */

2242
	if (!(xtrabackup_backup && srv_log_group_home_dir)) {
2243 2244 2245 2246 2247
		srv_log_group_home_dir = default_path;
	}
	if (xtrabackup_prepare && xtrabackup_incremental_dir) {
		srv_log_group_home_dir = xtrabackup_incremental_dir;
	}
2248
	msg("innodb_log_group_home_dir = %s",
2249 2250 2251 2252
	    srv_log_group_home_dir);

	if (strchr(srv_log_group_home_dir, ';')) {
		msg("syntax error in innodb_log_group_home_dir, ");
2253
		goto error;
2254 2255 2256 2257 2258 2259 2260 2261
	}

	srv_adaptive_flushing = FALSE;

        /* 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) xtrabackup_use_memory;
2262
	srv_buf_pool_chunk_unit = srv_buf_pool_size;
2263

2264 2265
	srv_n_read_io_threads = (uint) innobase_read_io_threads;
	srv_n_write_io_threads = (uint) innobase_write_io_threads;
2266

2267
	srv_max_n_open_files = ULINT_UNDEFINED - 5;
2268

2269
	srv_print_verbose_log = verbose ? 2 : 1;
2270 2271 2272 2273 2274 2275 2276

	/* 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;

2277
	ut_ad(DATA_MYSQL_BINARY_CHARSET_COLL == my_charset_bin.number);
2278

2279 2280
#ifdef _WIN32
	srv_use_native_aio = TRUE;
2281 2282 2283 2284

#elif defined(LINUX_NATIVE_AIO)

	if (srv_use_native_aio) {
2285
		msg("InnoDB: Using Linux native AIO");
2286
	}
2287
#elif defined(HAVE_URING)
2288 2289 2290 2291 2292 2293
	if (!srv_use_native_aio) {
	} else if (io_uring_may_be_unsafe) {
		msg("InnoDB: Using liburing on this kernel %s may cause hangs;"
		    " see https://jira.mariadb.org/browse/MDEV-26674",
		    io_uring_may_be_unsafe);
	} else {
2294 2295
		msg("InnoDB: Using liburing");
	}
2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310
#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) {
2311
		srv_undo_dir = (char*) ".";
2312 2313
	}

2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336
	compile_time_assert(SRV_FORCE_IGNORE_CORRUPT == 1);

	/*
	 * This option can be read both from the command line, and the
	 * defaults file. The assignment should account for both cases,
	 * and for "--innobackupex". Since the command line argument is
	 * parsed after the defaults file, it takes precedence.
	 */
	if (xtrabackup_innodb_force_recovery) {
		srv_force_recovery = xtrabackup_innodb_force_recovery;
	}

	if (srv_force_recovery >= SRV_FORCE_IGNORE_CORRUPT) {
		if (!xtrabackup_prepare) {
			msg("mariabackup: The option \"innodb_force_recovery\""
			    " should only be used with \"%s\".",
			    (innobackupex_mode ? "--apply-log" : "--prepare"));
			goto error;
		} else {
			msg("innodb_force_recovery = %lu", srv_force_recovery);
		}
	}

2337 2338 2339 2340
#ifdef _WIN32
	srv_use_native_aio = TRUE;
#endif
	return false;
2341 2342

error:
2343
	msg("mariabackup: innodb_init_param(): Error occurred.\n");
2344
	return true;
2345 2346
}

2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368
static byte log_hdr_buf[log_t::START_OFFSET + SIZE_OF_FILE_CHECKPOINT];

/** Initialize an InnoDB log file header in log_hdr_buf[] */
static void log_hdr_init()
{
  memset(log_hdr_buf, 0, sizeof log_hdr_buf);
  mach_write_to_4(LOG_HEADER_FORMAT + log_hdr_buf, log_t::FORMAT_10_8);
  mach_write_to_8(LOG_HEADER_START_LSN + log_hdr_buf,
                  log_sys.next_checkpoint_lsn);
  snprintf(reinterpret_cast<char*>(LOG_HEADER_CREATOR + log_hdr_buf),
           16, "Backup %u.%u.%u",
           MYSQL_VERSION_ID / 10000, MYSQL_VERSION_ID / 100 % 100,
           MYSQL_VERSION_ID % 100);
  if (log_sys.is_encrypted())
    log_crypt_write_header(log_hdr_buf + LOG_HEADER_CREATOR_END);
  mach_write_to_4(508 + log_hdr_buf, my_crc32c(0, log_hdr_buf, 508));
  mach_write_to_8(log_hdr_buf + 0x1000, log_sys.next_checkpoint_lsn);
  mach_write_to_8(log_hdr_buf + 0x1008, recv_sys.lsn);
  mach_write_to_4(log_hdr_buf + 0x103c,
                  my_crc32c(0, log_hdr_buf + 0x1000, 60));
}

2369
static bool innodb_init()
2370
{
2371
  bool create_new_db= false;
2372

2373 2374
  srv_max_io_capacity= srv_io_capacity >= SRV_MAX_IO_CAPACITY_LIMIT / 2
    ? SRV_MAX_IO_CAPACITY_LIMIT : std::max(2 * srv_io_capacity, 2000UL);
2375

2376 2377
  /* Check if the data files exist or not. */
  dberr_t err= srv_sys_space.check_file_spec(&create_new_db, 5U << 20);
2378

Marko Mäkelä's avatar
Marko Mäkelä committed
2379 2380 2381 2382 2383
  if (create_new_db)
  {
    msg("mariadb-backup: InnoDB files do not exist");
    return true;
  }
2384

2385
  if (err == DB_SUCCESS)
Marko Mäkelä's avatar
Marko Mäkelä committed
2386
    err= srv_start(false);
2387

2388 2389 2390 2391 2392
  if (err != DB_SUCCESS)
  {
    msg("mariadb-backup: srv_start() returned %d (%s).", err, ut_strerr(err));
    return true;
  }
2393

2394 2395 2396
  ut_ad(srv_force_recovery <= SRV_FORCE_IGNORE_CORRUPT);
  ut_ad(recv_no_log_write);
  buf_flush_sync();
Marko Mäkelä's avatar
Marko Mäkelä committed
2397
  recv_sys.debug_free();
Marko Mäkelä's avatar
Marko Mäkelä committed
2398 2399 2400 2401
  ut_ad(!os_aio_pending_reads());
  ut_d(mysql_mutex_lock(&buf_pool.flush_list_mutex));
  ut_ad(!buf_pool.get_oldest_modification(0));
  ut_d(mysql_mutex_unlock(&buf_pool.flush_list_mutex));
Marko Mäkelä's avatar
Marko Mäkelä committed
2402 2403 2404 2405 2406 2407
  /* os_aio_pending_writes() may hold here if some write_io_callback()
  did not release the slot yet.  However, the page write itself must
  have completed, because the buf_pool.flush_list is empty. In debug
  builds, we wait for this to happen, hoping to get a hung process if
  this assumption does not hold. */
  ut_d(os_aio_wait_until_no_pending_writes(false));
2408 2409 2410 2411 2412 2413 2414 2415
  log_sys.close_file();

  if (xtrabackup_incremental)
    /* Reset the ib_logfile0 in --target-dir, not --incremental-dir. */
    srv_log_group_home_dir= xtrabackup_target_dir;

  bool ret;
  const std::string ib_logfile0{get_log_file_path()};
2416 2417 2418 2419
  os_file_delete_if_exists_func(ib_logfile0.c_str(), nullptr);
  os_file_t file= os_file_create_func(ib_logfile0.c_str(),
                                      OS_FILE_CREATE, OS_FILE_NORMAL,
                                      OS_DATA_FILE_NO_O_DIRECT, false, &ret);
2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439
  if (!ret)
  {
  invalid_log:
    msg("mariadb-backup: Cannot create %s", ib_logfile0.c_str());
    return true;
  }

  recv_sys.lsn= log_sys.next_checkpoint_lsn=
    log_sys.get_lsn() - SIZE_OF_FILE_CHECKPOINT;
  log_sys.set_latest_format(false); // not encrypted
  log_hdr_init();
  byte *b= &log_hdr_buf[log_t::START_OFFSET];
  b[0]= FILE_CHECKPOINT | 10;
  mach_write_to_8(b + 3, recv_sys.lsn);
  b[11]= 1;
  mach_write_to_4(b + 12, my_crc32c(0, b, 11));
  static_assert(12 + 4 == SIZE_OF_FILE_CHECKPOINT, "compatibility");

#ifdef _WIN32
  DWORD len;
2440
  ret= WriteFile(file, log_hdr_buf, sizeof log_hdr_buf,
2441 2442
                 &len, nullptr) && len == sizeof log_hdr_buf;
#else
2443
  ret= sizeof log_hdr_buf == write(file, log_hdr_buf, sizeof log_hdr_buf);
2444
#endif
2445
  if (!os_file_close_func(file) || !ret)
2446 2447
    goto invalid_log;
  return false;
2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463
}

/* ================= 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;

	fp = fopen(filename,"r");
	if(!fp) {
2464
		msg("Error: cannot open %s", filename);
2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508
		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 */

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"
2509
		 "last_lsn = " UINT64PF "\n",
2510 2511 2512
		 metadata_type,
		 metadata_from_lsn,
		 metadata_to_lsn,
2513
		 metadata_last_lsn);
2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537
}

/***********************************************************************
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) {
2538
		msg("Error: cannot open output stream for %s", XTRABACKUP_METADATA_FILENAME);
2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569
		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) {
2570
		msg("Error: cannot open %s", filepath);
2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594
		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 */
2595
	ulint page_size = ULINT_UNDEFINED, zip_size = 0;
2596
	info->space_id = UINT32_MAX;
2597 2598 2599 2600 2601 2602 2603 2604 2605 2606

	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) {
2607
				page_size = strtoul(value, NULL, 10);
2608
			} else if (strcmp(key, "zip_size") == 0) {
2609
				zip_size = strtoul(value, NULL, 10);
2610
			} else if (strcmp(key, "space_id") == 0) {
2611 2612
				info->space_id = static_cast<uint32_t>
					(strtoul(value, NULL, 10));
2613 2614 2615 2616 2617 2618
			}
		}
	}

	fclose(fp);

2619
	if (page_size == ULINT_UNDEFINED) {
2620
		msg("page_size is required in %s", filepath);
2621
		r = FALSE;
2622
	} else {
2623 2624
		info->page_size = page_size;
		info->zip_size = zip_size;
2625
	}
2626

2627
	if (info->space_id == UINT32_MAX) {
2628
		msg("mariabackup: Warning: This backup was taken with XtraBackup 2.0.1 "
2629
			"or earlier, some DDL operations between full and incremental "
2630
			"backups may be handled incorrectly");
2631 2632 2633 2634 2635 2636 2637 2638 2639
	}

	return(r);
}

/***********************************************************************
Write meta info for an incremental delta.
@return TRUE on success, FALSE on failure. */
my_bool
2640 2641
xb_write_delta_metadata(ds_ctxt *ds_meta,
                        const char *filename, const xb_delta_info_t *info)
2642 2643 2644 2645 2646 2647 2648 2649
{
	ds_file_t	*f;
	char		buf[64];
	my_bool		ret;
	size_t		len;
	MY_STAT		mystat;

	snprintf(buf, sizeof(buf),
2650 2651
		 "page_size = " ULINTPF "\n"
		 "zip_size = " ULINTPF " \n"
2652
		 "space_id = %u\n",
2653 2654
		 info->page_size,
		 info->zip_size,
2655
		 info->space_id);
2656 2657 2658 2659 2660 2661 2662
	len = strlen(buf);

	mystat.st_size = len;
	mystat.st_mtime = my_time(0);

	f = ds_open(ds_meta, filename, &mystat);
	if (f == NULL) {
2663
		msg("Error: Can't open output stream for %s",filename);
2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676
		return(FALSE);
	}

	ret = (ds_write(f, buf, len) == 0);

	if (ds_close(f)) {
		ret = FALSE;
	}

	return(ret);
}

/* ================= backup ================= */
2677
void xtrabackup_io_throttling()
2678
{
2679
  if (!xtrabackup_backup || !xtrabackup_throttle)
2680 2681
    return;

2682
  mysql_mutex_lock(&recv_sys.mutex);
2683
  if (io_ticket-- < 0)
2684 2685
    mysql_cond_wait(&wait_throttle, &recv_sys.mutex);
  mysql_mutex_unlock(&recv_sys.mutex);
2686 2687
}

Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714
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;
2715 2716
	const ulint fold = my_crc32c(0, name, strlen(name));
	HASH_SEARCH(name_hash, table, fold,
Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
2717 2718 2719 2720 2721 2722 2723 2724 2725 2726
		    xb_filter_entry_t*,
		    found, (void) 0,
		    !strcmp(found->name, name));

	if (found && result) {
		*result = found;
	}
	return (found != NULL);
}

2727
/************************************************************************
Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
2728 2729
Checks if a given table name matches any of specifications given in
regex_list or tables_hash.
2730

Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
2731
@return TRUE on match or both regex_list and tables_hash are empty.*/
2732
static my_bool
Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
2733 2734 2735
check_if_table_matches_filters(const char *name,
	const regex_list_t& regex_list,
	hash_table_t* tables_hash)
2736
{
2737
	if (regex_list.empty() && !tables_hash->array) {
Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
2738 2739
		return(FALSE);
	}
2740

Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
2741 2742 2743
	if (regex_list_check_match(regex_list, name)) {
		return(TRUE);
	}
2744

2745 2746
	return tables_hash->array &&
		find_filter_in_hashtable(name, tables_hash, NULL);
Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
2747 2748 2749 2750 2751 2752 2753 2754
}

enum skip_database_check_result {
	DATABASE_SKIP,
	DATABASE_SKIP_SOME_TABLES,
	DATABASE_DONT_SKIP,
	DATABASE_DONT_SKIP_UNLESS_EXPLICITLY_EXCLUDED,
};
2755

Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771
/************************************************************************
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;

2772 2773
	if (databases_exclude_hash.array &&
		find_filter_in_hashtable(name, &databases_exclude_hash,
Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
2774
					 &database) &&
Marko Mäkelä's avatar
Marko Mäkelä committed
2775
		(!database->has_tables || !databases_include_hash.array)) {
Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
2776 2777 2778 2779 2780
		/* Database is found and there are no tables specified,
		   skip entire db. */
		return DATABASE_SKIP;
	}

2781 2782
	if (databases_include_hash.array) {
		if (!find_filter_in_hashtable(name, &databases_include_hash,
Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
2783 2784 2785 2786 2787 2788 2789
					      &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;
2790 2791 2792
		}
	}

Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805
	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. */
)
{
2806
	if (!databases_include_hash.array && !databases_exclude_hash.array) {
Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
2807 2808 2809
		return(FALSE);
	}

2810 2811 2812 2813 2814 2815 2816 2817 2818
	const char* db_name = strrchr(path, '/');
#ifdef _WIN32
	if (const char* last = strrchr(path, '\\')) {
		if (!db_name || last > db_name) {
			db_name = last;
		}
	}
#endif

Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
2819 2820 2821 2822 2823 2824 2825
	if (db_name == NULL) {
		db_name = path;
	} else {
		++db_name;
	}

	return check_if_skip_database(db_name) == DATABASE_SKIP;
2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843
}

/************************************************************************
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;

2844 2845 2846

	dbname = NULL;
	tbname = name;
2847 2848 2849 2850 2851 2852 2853 2854 2855 2856
	for (;;) {
		ptr= strchr(tbname, '/');
#ifdef _WIN32
		if (!ptr) {
			ptr= strchr(tbname,'\\');
		}
#endif
		if (!ptr) {
			break;
		}
2857 2858 2859 2860
		dbname = tbname;
		tbname = ptr + 1;
	}

2861 2862 2863 2864
	if (strncmp(tbname, tmp_file_prefix, tmp_file_prefix_length) == 0) {
		return TRUE;
	}

Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
2865 2866
	if (regex_exclude_list.empty() &&
		regex_include_list.empty() &&
2867 2868 2869 2870
		!tables_include_hash.array &&
		!tables_exclude_hash.array &&
		!databases_include_hash.array &&
		!databases_exclude_hash.array) {
2871 2872 2873 2874 2875 2876 2877
		return(FALSE);
	}

	if (dbname == NULL) {
		return(FALSE);
	}

2878 2879 2880
	strncpy(buf, dbname, FN_REFLEN - 1);
	buf[FN_REFLEN - 1] = '\0';
	buf[tbname - 1 - dbname] = '\0';
2881

Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
2882 2883 2884 2885
	const skip_database_check_result skip_database =
			check_if_skip_database(buf);
	if (skip_database == DATABASE_SKIP) {
		return (TRUE);
2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900
	}

	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' */
Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
2901
	if (check_if_table_matches_filters(buf, regex_exclude_list,
2902
					   &tables_exclude_hash)) {
Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
2903 2904 2905
		return(TRUE);
	}
	if (check_if_table_matches_filters(buf, regex_include_list,
2906
					   &tables_include_hash)) {
2907 2908 2909 2910 2911
		return(FALSE);
	}
	if ((eptr = strstr(buf, "#P#")) != NULL) {
		*eptr = 0;

Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
2912
		if (check_if_table_matches_filters(buf, regex_exclude_list,
2913
						   &tables_exclude_hash)) {
Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
2914 2915 2916
			return (TRUE);
		}
		if (check_if_table_matches_filters(buf, regex_include_list,
2917
						   &tables_include_hash)) {
2918 2919 2920 2921
			return(FALSE);
		}
	}

Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
2922 2923 2924 2925 2926 2927 2928 2929
	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() ||
2930
		tables_include_hash.array) {
Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
2931 2932 2933 2934 2935 2936 2937

		/* Include lists are present, but qualified name
		   failed to match any.*/
		return(TRUE);
	}

	return(FALSE);
2938 2939 2940 2941 2942 2943 2944 2945 2946
}

const char*
xb_get_copy_action(const char *dflt)
{
	const char *action;

	if (xtrabackup_stream) {
		if (xtrabackup_compress) {
2947
			action = "Compressing and streaming";
2948 2949 2950 2951 2952
		} else {
			action = "Streaming";
		}
	} else {
		if (xtrabackup_compress) {
2953
			action = "Compressing";
2954 2955 2956 2957 2958 2959 2960 2961 2962
		} else {
			action = dflt;
		}
	}

	return(action);
}


2963 2964 2965 2966 2967 2968 2969 2970 2971
/** Copy innodb data file to the specified destination.

@param[in] node	file node of a tablespace
@param[in] thread_n	thread id, used in the text of diagnostic messages
@param[in] dest_name	destination file name
@param[in] write_filter	write filter to copy data, can be pass-through filter
for full backup, pages filter for incremental backup, etc.

@return FALSE on success and TRUE on error */
2972 2973 2974
static my_bool xtrabackup_copy_datafile(ds_ctxt *ds_data,
                                        ds_ctxt *ds_meta,
                                        fil_node_t *node, uint thread_n,
2975
                                        const char *dest_name,
2976 2977
                                        const xb_write_filt_t &write_filter,
                                        CorruptedPages &corrupted_pages)
2978 2979 2980 2981 2982 2983 2984 2985 2986 2987
{
	char			 dst_name[FN_REFLEN];
	ds_file_t		*dstfile = NULL;
	xb_fil_cur_t		 cursor;
	xb_fil_cur_result_t	 res;
	xb_write_filt_ctxt_t	 write_filt_ctxt;
	const char		*action;
	xb_read_filt_t		*read_filter;
	my_bool			rc = FALSE;

2988
	if (fil_is_user_tablespace_id(node->space->id)
2989 2990 2991 2992
	    && check_if_skip_table(filename_to_spacename(node->name,
							 strlen(node->name)).
				   c_str())) {
		msg(thread_n, "Skipping %s.", node->name);
2993 2994 2995
		return(FALSE);
	}

2996 2997
	memset(&write_filt_ctxt, 0, sizeof(xb_write_filt_ctxt_t));

2998
	bool was_dropped;
2999
	mysql_mutex_lock(&recv_sys.mutex);
3000
	was_dropped = (ddl_tracker.drops.find(node->space->id) != ddl_tracker.drops.end());
3001
	mysql_mutex_unlock(&recv_sys.mutex);
3002
	if (was_dropped) {
Marko Mäkelä's avatar
Marko Mäkelä committed
3003
		if (node->is_open()) {
3004
			mysql_mutex_lock(&fil_system.mutex);
Marko Mäkelä's avatar
Marko Mäkelä committed
3005
			node->close();
3006
			mysql_mutex_unlock(&fil_system.mutex);
Marko Mäkelä's avatar
Marko Mäkelä committed
3007
		}
3008 3009 3010
		goto skip;
	}

3011 3012 3013 3014 3015 3016
	if (!changed_page_bitmap) {
		read_filter = &rf_pass_through;
	}
	else {
		read_filter = &rf_bitmap;
	}
3017

3018
	res = xb_fil_cur_open(&cursor, read_filter, node, thread_n, ULLONG_MAX);
3019 3020 3021 3022 3023 3024
	if (res == XB_FIL_CUR_SKIP) {
		goto skip;
	} else if (res == XB_FIL_CUR_ERROR) {
		goto error;
	}

3025 3026 3027
	strncpy(dst_name, dest_name ? dest_name : cursor.rel_path,
		sizeof dst_name - 1);
	dst_name[sizeof dst_name - 1] = '\0';
3028

3029
	ut_a(write_filter.process != NULL);
3030

3031
	if (write_filter.init != NULL &&
3032
		!write_filter.init(ds_meta, &write_filt_ctxt, dst_name, &cursor,
3033
			opt_log_innodb_page_corruption ? &corrupted_pages : NULL)) {
3034
		msg (thread_n, "mariabackup: error: failed to initialize page write filter.");
3035 3036 3037 3038 3039
		goto error;
	}

	dstfile = ds_open(ds_data, dst_name, &cursor.statinfo);
	if (dstfile == NULL) {
3040
		msg(thread_n,"mariabackup: error: can't open the destination stream for %s", dst_name);
3041 3042 3043 3044 3045 3046
		goto error;
	}

	action = xb_get_copy_action();

	if (xtrabackup_stream) {
3047
		msg(thread_n, "%s %s", action, node->name);
3048
	} else {
3049 3050
		msg(thread_n, "%s %s to %s", action, node->name,
		    dstfile->path);
3051 3052 3053
	}

	/* The main copy loop */
3054 3055 3056 3057 3058 3059 3060 3061 3062 3063
	while (1) {
		res = xb_fil_cur_read(&cursor, corrupted_pages);
		if (res == XB_FIL_CUR_ERROR) {
		       goto error;
		}

		if (res == XB_FIL_CUR_EOF) {
			break;
		}

3064
		if (!write_filter.process(&write_filt_ctxt, dstfile)) {
3065 3066 3067
			goto error;
		}

3068
		if (res == XB_FIL_CUR_SKIP) {
Marko Mäkelä's avatar
Marko Mäkelä committed
3069
			mysql_mutex_lock(&recv_sys.mutex);
3070 3071
			fail_undo_ids.insert(
				static_cast<uint32_t>(cursor.space_id));
Marko Mäkelä's avatar
Marko Mäkelä committed
3072
			mysql_mutex_unlock(&recv_sys.mutex);
3073 3074
			break;
		}
3075 3076
	}

3077 3078
	if (write_filter.finalize
	    && !write_filter.finalize(&write_filt_ctxt, dstfile)) {
3079
		goto error;
3080 3081
	} else {
		const fil_space_t::name_type name = node->space->name();
3082

3083
		mysql_mutex_lock(&recv_sys.mutex);
3084 3085 3086
		ddl_tracker.tables_in_backup.emplace(node->space->id,
						     std::string(name.data(),
								 name.size()));
3087
		mysql_mutex_unlock(&recv_sys.mutex);
3088
	}
3089

3090
	/* close */
3091
	msg(thread_n,"        ...done");
3092 3093 3094 3095
	xb_fil_cur_close(&cursor);
	if (ds_close(dstfile)) {
		rc = TRUE;
	}
3096 3097
	if (write_filter.deinit) {
		write_filter.deinit(&write_filt_ctxt);
3098 3099 3100 3101 3102 3103 3104 3105
	}
	return(rc);

error:
	xb_fil_cur_close(&cursor);
	if (dstfile != NULL) {
		ds_close(dstfile);
	}
3106 3107
	if (write_filter.deinit) {
		write_filter.deinit(&write_filt_ctxt);;
3108
	}
3109
	msg(thread_n, "mariabackup: xtrabackup_copy_datafile() failed.");
3110 3111 3112 3113 3114 3115 3116
	return(TRUE); /*ERROR*/

skip:

	if (dstfile != NULL) {
		ds_close(dstfile);
	}
3117 3118
	if (write_filter.deinit) {
		write_filter.deinit(&write_filt_ctxt);
3119
	}
3120
	msg(thread_n,"Warning: We assume the  table was dropped during xtrabackup execution and ignore the tablespace %s", node->name);
3121 3122 3123
	return(FALSE);
}

3124 3125 3126
/** Copy redo log until the current end of the log is reached
@return	whether the operation failed */
static bool xtrabackup_copy_logfile()
3127
{
3128
  mysql_mutex_assert_owner(&recv_sys.mutex);
3129
  DBUG_EXECUTE_IF("log_checksum_mismatch", return false;);
3130

3131 3132 3133 3134
  ut_a(dst_log_file);
  ut_ad(recv_sys.is_initialised());
  const size_t sequence_offset{log_sys.is_encrypted() ? 8U + 5U : 5U};
  const size_t block_size_1{log_sys.get_block_size() - 1};
3135

3136 3137
  ut_ad(!log_sys.is_pmem());

3138 3139 3140 3141 3142
  {
    recv_sys.offset= size_t(recv_sys.lsn - log_sys.get_first_lsn()) &
      block_size_1;
    recv_sys.len= 0;
  }
3143

3144 3145 3146 3147
  for (unsigned retry_count{0};;)
  {
    recv_sys_t::parse_mtr_result r;
    size_t start_offset{recv_sys.offset};
3148

3149 3150 3151 3152 3153 3154 3155
    {
      {
        auto source_offset=
          log_sys.calc_lsn_offset(recv_sys.lsn + recv_sys.len -
                                  recv_sys.offset);
        source_offset&= ~block_size_1;
        size_t size{log_sys.buf_size - recv_sys.len};
3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170
        if (UNIV_UNLIKELY(source_offset + size > log_sys.file_size))
        {
          const size_t first{size_t(log_sys.file_size - source_offset)};
          ut_ad(first <= log_sys.buf_size);
          log_sys.log.read(source_offset, {log_sys.buf, first});
          size-= first;
          if (log_sys.START_OFFSET + size > source_offset)
            size= size_t(source_offset - log_sys.START_OFFSET);
          if (size)
            log_sys.log.read(log_sys.START_OFFSET,
                             {log_sys.buf + first, size});
          size+= first;
        }
        else
          log_sys.log.read(source_offset, {log_sys.buf, size});
3171 3172
        recv_sys.len= size;
      }
3173

3174 3175
      if (log_sys.buf[recv_sys.offset] <= 1)
        break;
3176

3177
      if (recv_sys.parse_mtr<false>(false) == recv_sys_t::OK)
3178 3179 3180 3181 3182 3183 3184 3185 3186
      {
        do
        {
          /* Set the sequence bit (the backed-up log will not wrap around) */
          byte *seq= &log_sys.buf[recv_sys.offset - sequence_offset];
          ut_ad(*seq == log_sys.get_sequence_bit(recv_sys.lsn -
                                                 sequence_offset));
          *seq= 1;
        }
3187
        while ((r= recv_sys.parse_mtr<false>(false)) == recv_sys_t::OK);
3188

3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202
        if (ds_write(dst_log_file, log_sys.buf + start_offset,
                     recv_sys.offset - start_offset))
        {
          msg("Error: write to ib_logfile0 failed");
          return true;
        }
        else
        {
          const auto ofs= recv_sys.offset & ~block_size_1;
          memmove_aligned<64>(log_sys.buf, log_sys.buf + ofs,
                              recv_sys.len - ofs);
          recv_sys.len-= ofs;
          recv_sys.offset&= block_size_1;
        }
3203

3204
        pthread_cond_broadcast(&scanned_lsn_cond);
3205

3206 3207
        if (r == recv_sys_t::GOT_EOF)
          break;
3208

3209 3210
        if (recv_sys.offset < log_sys.get_block_size())
          break;
3211

3212
        if (xtrabackup_throttle && io_ticket-- < 0)
3213
          mysql_cond_wait(&wait_throttle, &recv_sys.mutex);
3214

3215
        retry_count= 0;
3216
        continue;
3217 3218 3219 3220 3221 3222
      }
      else
      {
        recv_sys.len= recv_sys.offset & ~block_size_1;
        if (retry_count == 100)
          break;
3223

3224 3225 3226 3227 3228 3229 3230 3231
        mysql_mutex_unlock(&recv_sys.mutex);
        if (!retry_count++)
          msg("Retrying read of log at LSN=" LSN_PF, recv_sys.lsn);
        my_sleep(1000);
      }
    }
    mysql_mutex_lock(&recv_sys.mutex);
  }
3232

Marko Mäkelä's avatar
Marko Mäkelä committed
3233 3234
  if (verbose)
    msg(">> log scanned up to (" LSN_PF ")", recv_sys.lsn);
3235
  return false;
3236 3237
}

3238 3239 3240
/**
Wait until redo log copying thread processes given lsn
*/
3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256
void backup_wait_for_lsn(lsn_t lsn)
{
  mysql_mutex_lock(&recv_sys.mutex);
  for (lsn_t last_lsn{recv_sys.lsn}; last_lsn < lsn; )
  {
    timespec abstime;
    set_timespec(abstime, 5);
    if (my_cond_timedwait(&scanned_lsn_cond, &recv_sys.mutex.m_mutex,
                          &abstime) &&
        last_lsn == recv_sys.lsn)
      die("Was only able to copy log from " LSN_PF " to " LSN_PF
          ", not " LSN_PF "; try increasing innodb_log_file_size",
          log_sys.next_checkpoint_lsn, last_lsn, lsn);
    last_lsn= recv_sys.lsn;
  }
  mysql_mutex_unlock(&recv_sys.mutex);
3257 3258 3259 3260
}

extern lsn_t server_lsn_after_lock;

3261
static void log_copying_thread()
3262
{
3263
  my_thread_init();
3264
  mysql_mutex_lock(&recv_sys.mutex);
3265
  while (!xtrabackup_copy_logfile() &&
3266
         (!metadata_to_lsn || metadata_to_lsn > recv_sys.lsn))
3267 3268
  {
    timespec abstime;
3269
    set_timespec_nsec(abstime, 1000000ULL * xtrabackup_log_copy_interval);
3270
    mysql_cond_timedwait(&log_copying_stop, &recv_sys.mutex, &abstime);
3271 3272
  }
  log_copying_running= false;
3273
  mysql_mutex_unlock(&recv_sys.mutex);
3274
  my_thread_end();
3275 3276
}

3277
/** whether io_watching_thread() is active; protected by recv_sys.mutex */
3278 3279
static bool have_io_watching_thread;

3280
/* io throttle watching (rough) */
3281
static void io_watching_thread()
3282
{
3283
  my_thread_init();
3284
  /* currently, for --backup only */
3285
  ut_ad(xtrabackup_backup);
3286

3287
  mysql_mutex_lock(&recv_sys.mutex);
3288
  ut_ad(have_io_watching_thread);
3289

3290 3291 3292 3293
  while (log_copying_running && !metadata_to_lsn)
  {
    timespec abstime;
    set_timespec(abstime, 1);
3294
    mysql_cond_timedwait(&log_copying_stop, &recv_sys.mutex, &abstime);
3295 3296 3297
    io_ticket= xtrabackup_throttle;
    mysql_cond_broadcast(&wait_throttle);
  }
3298

3299 3300
  /* stop io throttle */
  xtrabackup_throttle= 0;
3301
  have_io_watching_thread= false;
3302
  mysql_cond_broadcast(&wait_throttle);
3303
  mysql_mutex_unlock(&recv_sys.mutex);
3304
  my_thread_end();
3305 3306
}

3307
#ifndef DBUG_OFF
3308 3309
char *dbug_mariabackup_get_val(const char *event,
                               const fil_space_t::name_type key)
3310
{
3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324
  char envvar[FN_REFLEN];
  strncpy(envvar, event, sizeof envvar - 1);
  envvar[(sizeof envvar) - 1] = '\0';

  if (key.size() && key.size() + strlen(envvar) < (sizeof envvar) - 2)
  {
    strcat(envvar, "_");
    strncat(envvar, key.data(), key.size());
    if (char *slash= strchr(envvar, '/'))
      *slash= '_';
  }

  char *val = getenv(envvar);
  return val && *val ? val : nullptr;
3325 3326
}

3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338
/*
In debug mode,  execute SQL statement that was passed via environment.
To use this facility, you need to

1. Add code DBUG_EXECUTE_MARIABACKUP_EVENT("my_event_name", key););
  to the code. key is usually a table name
2. Set environment variable my_event_name_$key SQL statement you want to execute
   when event occurs, in DBUG_EXECUTE_IF from above.
   In mtr , you can set environment via 'let' statement (do not use $ as the first char
   for the variable)
3. start mariabackup with --dbug=+d,debug_mariabackup_events
*/
3339
void dbug_mariabackup_event(const char *event,
3340
                                   const fil_space_t::name_type key)
3341
{
3342 3343
	char *sql = dbug_mariabackup_get_val(event, key);
	if (sql && *sql) {
3344
		msg("dbug_mariabackup_event : executing '%s'", sql);
3345 3346 3347
		xb_mysql_query(mysql_connection, sql, false, true);
	}
}
3348
#endif // DBUG_OFF
3349

3350 3351
/** Datafiles copying thread.*/
static void data_copy_thread_func(data_thread_ctxt_t *ctxt) /* thread context */
3352 3353 3354
{
	uint			num = ctxt->num;
	fil_node_t*		node;
3355
	ut_ad(ctxt->corrupted_pages);
3356 3357 3358 3359 3360 3361 3362 3363

	/*
	  Initialize mysys thread-specific memory so we can
	  use mysys functions in this thread.
	*/
	my_thread_init();

	while ((node = datafiles_iter_next(ctxt->it)) != NULL) {
3364 3365 3366
		DBUG_MARIABACKUP_EVENT("before_copy", node->space->name());
		DBUG_EXECUTE_FOR_KEY("wait_innodb_redo_before_copy",
				     node->space->name(),
3367
			backup_wait_for_lsn(get_current_lsn(mysql_connection)););
3368
		/* copy the datafile */
3369 3370
		if (xtrabackup_copy_datafile(ctxt->datasinks->m_data,
					     ctxt->datasinks->m_meta, node, num, NULL,
3371 3372
			xtrabackup_incremental ? wf_incremental : wf_write_through,
			*ctxt->corrupted_pages))
3373
			die("failed to copy datafile.");
3374

3375
		DBUG_MARIABACKUP_EVENT("after_copy", node->space->name());
3376 3377
	}

3378
	pthread_mutex_lock(ctxt->count_mutex);
3379
	(*ctxt->count)--;
3380
	pthread_mutex_unlock(ctxt->count_mutex);
3381 3382 3383 3384 3385 3386 3387 3388 3389 3390

	my_thread_end();
}

/************************************************************************
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
3391
files (including ib_logfile0). The second datasink writes to temporary
3392
files first, and then streams them in a serialized way when closed. */
3393
void Backup_datasinks::init()
3394 3395 3396 3397
{
	/* Start building out the pipelines from the terminus back */
	if (xtrabackup_stream) {
		/* All streaming goes to stdout */
3398 3399
		m_data = m_meta = m_redo = ds_create(xtrabackup_target_dir,
						     DS_TYPE_STDOUT);
3400 3401
	} else {
		/* Local filesystem */
3402 3403
		m_data = m_meta = m_redo = ds_create(xtrabackup_target_dir,
						     DS_TYPE_LOCAL);
3404 3405 3406
	}

	/* Track it for destruction */
3407
	add_datasink_to_destroy(m_data);
3408 3409 3410 3411

	/* Stream formatting */
	if (xtrabackup_stream) {
		ds_ctxt_t	*ds;
3412 3413 3414

	 ut_a(xtrabackup_stream_fmt == XB_STREAM_FMT_XBSTREAM);
	 ds = ds_create(xtrabackup_target_dir, DS_TYPE_XBSTREAM);
3415

3416
		add_datasink_to_destroy(ds);
3417

3418 3419
		ds_set_pipe(ds, m_data);
		m_data = ds;
3420 3421


3422
		m_redo = m_meta = m_data;
3423 3424
	}

3425
	/* Compression for m_data and m_redo */
3426 3427 3428 3429 3430 3431
	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);
3432 3433 3434 3435
		add_datasink_to_destroy(ds);
		ds_set_pipe(ds, m_data);
		if (m_data != m_redo) {
			m_data = ds;
3436 3437
			ds = ds_create(xtrabackup_target_dir, DS_TYPE_BUFFER);
			ds_buffer_set_size(ds, 1024 * 1024);
3438 3439 3440
			add_datasink_to_destroy(ds);
			ds_set_pipe(ds, m_redo);
			m_redo = ds;
3441
		} else {
3442
			m_redo = m_data = ds;
3443 3444 3445
		}

		ds = ds_create(xtrabackup_target_dir, DS_TYPE_COMPRESS);
3446 3447 3448 3449
		add_datasink_to_destroy(ds);
		ds_set_pipe(ds, m_data);
		if (m_data != m_redo) {
			m_data = ds;
3450
			ds = ds_create(xtrabackup_target_dir, DS_TYPE_COMPRESS);
3451 3452 3453
			add_datasink_to_destroy(ds);
			ds_set_pipe(ds, m_redo);
			m_redo = ds;
3454
		} else {
3455
			m_redo = m_data = ds;
3456 3457 3458 3459 3460 3461
		}
	}
}

#define SRV_MAX_N_PENDING_SYNC_IOS	100

3462
/** Initialize the tablespace cache subsystem. */
3463
static
3464 3465
void
xb_fil_io_init()
3466
{
3467
	fil_system.create(srv_file_per_table ? 50000 : 5000);
3468
	fil_system.freeze_space_list = 1;
3469
	fil_system.space_id_reuse_warned = true;
3470
}
3471

3472 3473 3474 3475 3476
/** Load tablespace.

@param[in] dirname directory name of the tablespace to open
@param[in] filname file name of the tablespece to open
@param[in] is_remote true if tablespace file is .isl
Marko Mäkelä's avatar
Marko Mäkelä committed
3477 3478 3479 3480
@param[in] skip_node_page0 true if we don't need to read node page 0. Otherwise
node page0 will be read, and it's size and free pages limit
will be set from page 0, what is neccessary for checking and fixing corrupted
pages.
3481 3482
@param[in] defer_space_id use the space id to create space object
when there is deferred tablespace
3483 3484 3485
*/
static void xb_load_single_table_tablespace(const char *dirname,
                                            const char *filname,
Marko Mäkelä's avatar
Marko Mäkelä committed
3486
                                            bool is_remote,
3487
                                            bool skip_node_page0,
3488
                                            uint32_t defer_space_id)
3489
{
3490
	ut_ad(srv_operation == SRV_OPERATION_BACKUP
3491
	      || srv_operation == SRV_OPERATION_RESTORE_DELTA
3492 3493
	      || srv_operation == SRV_OPERATION_RESTORE
	      || srv_operation == SRV_OPERATION_BACKUP_NO_DEFER);
3494 3495
	/* Ignore .isl files on XtraBackup recovery. All tablespaces must be
	local. */
3496
	if (is_remote && srv_operation == SRV_OPERATION_RESTORE_DELTA) {
3497 3498 3499 3500 3501
		return;
	}
	if (check_if_skip_table(filname)) {
		return;
	}
3502

3503 3504 3505 3506 3507 3508 3509 3510
	/* The name ends in .ibd or .isl;
	try opening the file */
	char*	name;
	size_t	dirlen		= dirname == NULL ? 0 : strlen(dirname);
	size_t	namelen		= strlen(filname);
	ulint	pathlen		= dirname == NULL ? namelen + 1: dirlen + namelen + 2;
	dberr_t	err;
	fil_space_t	*space;
3511
	bool	defer = false;
3512

3513
	name = static_cast<char*>(ut_malloc_nokey(pathlen));
3514

3515
	if (dirname != NULL) {
3516
		snprintf(name, pathlen, "%s/%s", dirname, filname);
3517 3518
		name[pathlen - 5] = 0;
	} else {
3519
		snprintf(name, pathlen, "%s", filname);
3520 3521
		name[pathlen - 5] = 0;
	}
3522

3523
	const fil_space_t::name_type n{name, pathlen - 5};
3524 3525 3526 3527
	Datafile *file;

	if (is_remote) {
		RemoteDatafile* rf = new RemoteDatafile();
3528
		if (!rf->open_link_file(n)) {
3529 3530 3531 3532 3533
			die("Can't open datafile %s", name);
		}
		file = rf;
	} else {
		file = new Datafile();
3534
		file->make_filepath(".", n, IBD);
3535
	}
3536

3537
	if (file->open_read_only(true) != DB_SUCCESS) {
3538
		die("Can't open datafile %s", name);
3539
	}
3540

3541
	for (int i = 0; i < 10; i++) {
3542
		file->m_defer = false;
3543
		err = file->validate_first_page();
3544 3545 3546 3547 3548 3549 3550 3551 3552 3553

		if (file->m_defer) {
			if (defer_space_id) {
				defer = true;
				file->set_space_id(defer_space_id);
				file->set_flags(FSP_FLAGS_PAGE_SSIZE());
				err = DB_SUCCESS;
				break;
			}
		} else if (err != DB_CORRUPTION) {
3554 3555 3556 3557 3558 3559
			break;
		}

		my_sleep(1000);
	}

3560
	if (!defer && file->m_defer) {
3561 3562 3563 3564
		const char *file_path = file->filepath();
		defer_space_names.insert(
			filename_to_spacename(
				file_path, strlen(file_path)));
3565 3566 3567 3568 3569
		delete file;
		ut_free(name);
		return;
	}

3570
	bool is_empty_file = file->exists() && file->is_empty_file();
3571

3572
	if (err == DB_SUCCESS && file->space_id() != SRV_TMP_SPACE_ID) {
3573
		mysql_mutex_lock(&fil_system.mutex);
3574
		space = fil_space_t::create(
3575
			file->space_id(), file->flags(),
Marko Mäkelä's avatar
Marko Mäkelä committed
3576
			FIL_TYPE_TABLESPACE, nullptr/* TODO: crypt_data */,
3577 3578
			FIL_ENCRYPTION_DEFAULT,
			file->handle() != OS_FILE_CLOSED);
3579
		ut_ad(space);
3580 3581 3582 3583 3584
		fil_node_t* node= space->add(
			file->filepath(),
			skip_node_page0 ? file->detach() : pfs_os_file_t(),
			0, false, false);
		node->deferred= defer;
3585
		if (!space->read_page0())
3586
			err = DB_CANNOT_OPEN_FILE;
3587
		mysql_mutex_unlock(&fil_system.mutex);
3588

3589 3590
		if (srv_operation == SRV_OPERATION_RESTORE_DELTA
		    || xb_close_files) {
3591
			space->close();
3592 3593 3594 3595 3596
		}
	}

	delete file;

3597
	if (err != DB_SUCCESS && xtrabackup_backup && !is_empty_file) {
Sergei Golubchik's avatar
Sergei Golubchik committed
3598
		die("Failed to validate first page of the file %s, error %d",name, (int)err);
3599
	}
Eugene Kosov's avatar
Eugene Kosov committed
3600 3601

	ut_free(name);
3602 3603
}

3604
static void xb_load_single_table_tablespace(const std::string &space_name,
3605
                                            bool skip_node_page0,
3606
                                            uint32_t defer_space_id)
3607 3608 3609 3610 3611 3612 3613 3614 3615 3616
{
  std::string name(space_name);
  bool is_remote= access((name + ".ibd").c_str(), R_OK) != 0;
  const char *extension= is_remote ? ".isl" : ".ibd";
  name.append(extension);
  char buf[FN_REFLEN];
  strncpy(buf, name.c_str(), sizeof buf - 1);
  buf[sizeof buf - 1]= '\0';
  const char *dbname= buf;
  char *p= strchr(buf, '/');
3617
  if (!p)
3618 3619 3620 3621
    die("Unexpected tablespace %s filename %s", space_name.c_str(),
        name.c_str());
  *p= 0;
  const char *tablename= p + 1;
Marko Mäkelä's avatar
Marko Mäkelä committed
3622
  xb_load_single_table_tablespace(dbname, tablename, is_remote,
3623
                                  skip_node_page0, defer_space_id);
3624 3625
}

3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 3655 3656 3657 3658 3659 3660 3661 3662 3663 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 3695 3696 3697 3698 3699 3700 3701 3702 3703 3704 3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727
#ifdef _WIN32
/**
The os_file_opendir() function opens a directory stream corresponding to the
directory named by the dirname argument. The directory stream is positioned
at the first entry. In both Unix and Windows we automatically skip the '.'
and '..' items at the start of the directory listing.
@param[in]	dirname		directory name; it must not contain a trailing
				'\' or '/'
@return directory stream, NULL if error */
os_file_dir_t os_file_opendir(const char *dirname)
{
  char path[OS_FILE_MAX_PATH + 3];

  ut_a(strlen(dirname) < OS_FILE_MAX_PATH);

  strcpy(path, dirname);
  strcpy(path + strlen(path), "\\*");

  /* Note that in Windows opening the 'directory stream' also retrieves
  the first entry in the directory. Since it is '.', that is no problem,
  as we will skip over the '.' and '..' entries anyway. */

  LPWIN32_FIND_DATA lpFindFileData= static_cast<LPWIN32_FIND_DATA>
    (ut_malloc_nokey(sizeof(WIN32_FIND_DATA)));
  os_file_dir_t dir= FindFirstFile((LPCTSTR) path, lpFindFileData);
  ut_free(lpFindFileData);

  return dir;
}
#endif

/** This function returns information of the next file in the directory. We jump
over the '.' and '..' entries in the directory.
@param[in]	dirname		directory name or path
@param[in]	dir		directory stream
@param[out]	info		buffer where the info is returned
@return 0 if ok, -1 if error, 1 if at the end of the directory */
int
os_file_readdir_next_file(
	const char*	dirname,
	os_file_dir_t	dir,
	os_file_stat_t* info)
{
#ifdef _WIN32
	BOOL		ret;
	int		status;
	WIN32_FIND_DATA find_data;

next_file:
	ret = FindNextFile(dir, &find_data);

	if (ret > 0) {

		const char* name;

		name = static_cast<const char*>(find_data.cFileName);

		ut_a(strlen(name) < OS_FILE_MAX_PATH);

		if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {

			goto next_file;
		}

		strcpy(info->name, name);

		info->size = find_data.nFileSizeHigh;
		info->size <<= 32;
		info->size |= find_data.nFileSizeLow;

		if (find_data.dwFileAttributes
		    & FILE_ATTRIBUTE_REPARSE_POINT) {

			/* TODO: test Windows symlinks */
			/* TODO: MySQL has apparently its own symlink
			implementation in Windows, dbname.sym can
			redirect a database directory:
			REFMAN "windows-symbolic-links.html" */

			info->type = OS_FILE_TYPE_LINK;

		} else if (find_data.dwFileAttributes
			   & FILE_ATTRIBUTE_DIRECTORY) {

			info->type = OS_FILE_TYPE_DIR;

		} else {

			/* It is probably safest to assume that all other
			file types are normal. Better to check them rather
			than blindly skip them. */

			info->type = OS_FILE_TYPE_FILE;
		}

		status = 0;

	} else {
		DWORD err = GetLastError();
		if (err == ERROR_NO_MORE_FILES) {
			status = 1;
		} else {
3728
			msg("FindNextFile in %s returned %lu", dirname, err);
3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759
			status = -1;
		}
	}

	return(status);
#else
	struct dirent*	ent;
	char*		full_path;
	int		ret;
	struct stat	statinfo;

next_file:

	ent = readdir(dir);

	if (ent == NULL) {

		return(1);
	}

	ut_a(strlen(ent->d_name) < OS_FILE_MAX_PATH);

	if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) {

		goto next_file;
	}

	strcpy(info->name, ent->d_name);

	full_path = static_cast<char*>(
		ut_malloc_nokey(strlen(dirname) + strlen(ent->d_name) + 10));
Marko Mäkelä's avatar
Marko Mäkelä committed
3760 3761 3762
	if (!full_path) {
		return -1;
	}
3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 3809 3810 3811 3812 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844

	sprintf(full_path, "%s/%s", dirname, ent->d_name);

	ret = stat(full_path, &statinfo);

	if (ret) {

		if (errno == ENOENT) {
			/* readdir() returned a file that does not exist,
			it must have been deleted in the meantime. Do what
			would have happened if the file was deleted before
			readdir() - ignore and go to the next entry.
			If this is the last entry then info->name will still
			contain the name of the deleted file when this
			function returns, but this is not an issue since the
			caller shouldn't be looking at info when end of
			directory is returned. */

			ut_free(full_path);

			goto next_file;
		}

		msg("stat %s: Got error %d", full_path, errno);

		ut_free(full_path);

		return(-1);
	}

	info->size = statinfo.st_size;

	if (S_ISDIR(statinfo.st_mode)) {
		info->type = OS_FILE_TYPE_DIR;
	} else if (S_ISLNK(statinfo.st_mode)) {
		info->type = OS_FILE_TYPE_LINK;
	} else if (S_ISREG(statinfo.st_mode)) {
		info->type = OS_FILE_TYPE_FILE;
	} else {
		info->type = OS_FILE_TYPE_UNKNOWN;
	}

	ut_free(full_path);
	return(0);
#endif
}

/***********************************************************************//**
A fault-tolerant function that tries to read the next file name in the
directory. We retry 100 times if os_file_readdir_next_file() returns -1. The
idea is to read as much good data as we can and jump over bad data.
@return 0 if ok, -1 if error even after the retries, 1 if at the end
of the directory */
int
fil_file_readdir_next_file(
/*=======================*/
	dberr_t*	err,	/*!< out: this is set to DB_ERROR if an error
				was encountered, otherwise not changed */
	const char*	dirname,/*!< in: directory name or path */
	os_file_dir_t	dir,	/*!< in: directory stream */
	os_file_stat_t* info)	/*!< in/out: buffer where the
				info is returned */
{
	for (ulint i = 0; i < 100; i++) {
		int	ret = os_file_readdir_next_file(dirname, dir, info);

		if (ret != -1) {

			return(ret);
		}

		ib::error() << "os_file_readdir_next_file() returned -1 in"
			" directory " << dirname
			<< ", crash recovery may have failed"
			" for some .ibd files!";

		*err = DB_ERROR;
	}

	return(-1);
}

3845 3846 3847
/** Scan the database directories under the MySQL datadir, looking for
.ibd files and determining the space id in each of them.
@return	DB_SUCCESS or error number */
3848 3849

static dberr_t enumerate_ibd_files(process_single_tablespace_func_t callback)
3850 3851 3852 3853 3854 3855 3856 3857 3858
{
	int		ret;
	char*		dbpath		= NULL;
	ulint		dbpath_len	= 100;
	os_file_dir_t	dir;
	os_file_dir_t	dbdir;
	os_file_stat_t	dbinfo;
	os_file_stat_t	fileinfo;
	dberr_t		err		= DB_SUCCESS;
3859
	size_t len;
3860 3861 3862

	/* The datadir of MySQL is always the default directory of mysqld */

3863
	dir = os_file_opendir(fil_path_to_mysql_datadir);
3864

3865 3866
	if (UNIV_UNLIKELY(dir == IF_WIN(INVALID_HANDLE_VALUE, nullptr))) {
		msg("cannot open dir %s", fil_path_to_mysql_datadir);
3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880
		return(DB_ERROR);
	}

	dbpath = static_cast<char*>(ut_malloc_nokey(dbpath_len));

	/* Scan all directories under the datadir. They are the database
	directories of MySQL. */

	ret = fil_file_readdir_next_file(&err, fil_path_to_mysql_datadir, dir,
					 &dbinfo);
	while (ret == 0) {

		/* General tablespaces are always at the first level of the
		data home dir */
3881 3882 3883 3884 3885
		if (dbinfo.type != OS_FILE_TYPE_FILE) {
			const bool is_isl = ends_with(dbinfo.name, ".isl");
			if (is_isl || ends_with(dbinfo.name,".ibd")) {
				(*callback)(nullptr, dbinfo.name, is_isl,
					    false, 0);
3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908
			}
		}

		if (dbinfo.type == OS_FILE_TYPE_FILE
		    || dbinfo.type == OS_FILE_TYPE_UNKNOWN) {

			goto next_datadir_item;
		}

		/* We found a symlink or a directory; try opening it to see
		if a symlink is a directory */

		len = strlen(fil_path_to_mysql_datadir)
			+ strlen (dbinfo.name) + 2;
		if (len > dbpath_len) {
			dbpath_len = len;

			if (dbpath) {
				ut_free(dbpath);
			}

			dbpath = static_cast<char*>(ut_malloc_nokey(dbpath_len));
		}
3909 3910
		snprintf(dbpath, dbpath_len,
			 "%s/%s", fil_path_to_mysql_datadir, dbinfo.name);
3911 3912 3913 3914 3915 3916

		if (check_if_skip_database_by_path(dbpath)) {
			fprintf(stderr, "Skipping db: %s\n", dbpath);
			goto next_datadir_item;
		}

3917
		dbdir = os_file_opendir(dbpath);
3918

3919
		if (UNIV_UNLIKELY(dbdir != IF_WIN(INVALID_HANDLE_VALUE,NULL))){
3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934
			/* We found a database directory; loop through it,
			looking for possible .ibd files in it */

			for (ret = fil_file_readdir_next_file(&err, dbpath,
							      dbdir,
							      &fileinfo);
			     ret == 0;
			     ret = fil_file_readdir_next_file(&err, dbpath,
							      dbdir,
							      &fileinfo)) {
				if (fileinfo.type == OS_FILE_TYPE_DIR) {
					continue;
				}

				/* We found a symlink or a file */
3935
				if (strlen(fileinfo.name) > 4) {
3936
					bool is_isl= false;
Marko Mäkelä's avatar
Marko Mäkelä committed
3937
					if (ends_with(fileinfo.name, ".ibd") || ((is_isl = ends_with(fileinfo.name, ".isl"))))
3938
						(*callback)(dbinfo.name, fileinfo.name, is_isl, false, 0);
3939 3940 3941
				}
			}

3942
			if (os_file_closedir_failed(dbdir)) {
3943
				fprintf(stderr, "InnoDB: Warning: could not"
3944
				 " close database directory %s\n",
3945 3946 3947 3948 3949 3950
					dbpath);

				err = DB_ERROR;
			}

		} else {
3951
			msg("Can't open dir %s", dbpath);
3952 3953 3954 3955 3956 3957 3958 3959 3960 3961 3962 3963 3964
			err = DB_ERROR;
			break;

		}

next_datadir_item:
		ret = fil_file_readdir_next_file(&err,
						 fil_path_to_mysql_datadir,
						 dir, &dbinfo);
	}

	ut_free(dbpath);

3965
	if (os_file_closedir_failed(dir)) {
3966
		fprintf(stderr,
3967
			"InnoDB: Error: could not close MariaDB datadir\n");
3968
		return(DB_ERROR);
3969 3970
	}

3971 3972 3973
	return(err);
}

3974 3975 3976 3977 3978
/** Close all undo tablespaces while applying incremental delta */
static void xb_close_undo_tablespaces()
{
  if (srv_undo_space_id_start == 0)
    return;
Marko Mäkelä's avatar
Marko Mäkelä committed
3979
  for (uint32_t space_id= srv_undo_space_id_start;
3980 3981 3982 3983 3984 3985 3986 3987 3988
       space_id < srv_undo_space_id_start + srv_undo_tablespaces_open;
       space_id++)
  {
     fil_space_t *space= fil_space_get(space_id);
     ut_ad(space);
     space->close();
  }
}

3989 3990 3991 3992 3993 3994 3995 3996 3997 3998 3999 4000
/****************************************************************************
Populates the tablespace memory cache by scanning for and opening data files.
@returns DB_SUCCESS or error code.*/
static
dberr_t
xb_load_tablespaces()
{
	bool	create_new_db;
	dberr_t	err;
	ulint   sum_of_new_sizes;

	ut_ad(srv_operation == SRV_OPERATION_BACKUP
4001
	      || srv_operation == SRV_OPERATION_RESTORE_DELTA);
4002 4003 4004 4005 4006

	err = srv_sys_space.check_file_spec(&create_new_db, 0);

	/* create_new_db must not be true. */
	if (err != DB_SUCCESS || create_new_db) {
4007
		msg("Could not find data files at the specified datadir");
4008 4009 4010
		return(DB_ERROR);
	}

4011
	for (int i= 0; i < 10; i++) {
4012
		err = srv_sys_space.open_or_create(false, false, &sum_of_new_sizes);
4013 4014 4015 4016 4017 4018
		if (err == DB_PAGE_CORRUPTED || err == DB_CORRUPTION) {
			my_sleep(1000);
		}
		else
		 break;
	}
4019 4020

	if (err != DB_SUCCESS) {
4021
		msg("Could not open data files.\n");
4022 4023 4024 4025
		return(err);
	}

	/* Add separate undo tablespaces to fil_system */
4026
	err = srv_undo_tablespaces_init(false, nullptr);
4027

4028 4029 4030 4031
	if (err != DB_SUCCESS) {
		return(err);
	}

4032
	/* It is important to call xb_load_single_table_tablespaces() after
4033 4034 4035
	srv_undo_tablespaces_init(), because fil_is_user_tablespace_id() *
	relies on srv_undo_tablespaces_open to be properly initialized */

4036
	msg("mariabackup: Generating a list of tablespaces");
4037

4038
	err = enumerate_ibd_files(xb_load_single_table_tablespace);
4039 4040 4041 4042
	if (err != DB_SUCCESS) {
		return(err);
	}

4043 4044 4045 4046
	if (srv_operation == SRV_OPERATION_RESTORE_DELTA) {
		xb_close_undo_tablespaces();
	}

4047
	DBUG_MARIABACKUP_EVENT("after_load_tablespaces", {});
4048 4049 4050
	return(DB_SUCCESS);
}

4051 4052
/** Destroy the tablespace memory cache. */
static void xb_data_files_close()
4053
{
4054
  fil_space_t::close_all();
4055
  buf_dblwr.close();
4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 4073
}

/***********************************************************************
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 *>
4074
		(malloc(sizeof(xb_filter_entry_t) + namelen + 1));
4075 4076 4077 4078 4079 4080 4081 4082 4083 4084 4085 4086 4087 4088 4089
	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 */
4090
	hash_table_t*	hash)	/*!< in/out: hash to insert into */
4091
{
4092
	xb_filter_entry_t* entry = xb_new_filter_entry(name);
4093

4094 4095
	if (UNIV_UNLIKELY(!hash->array)) {
		hash->create(1000);
4096
	}
4097 4098
	const ulint fold = my_crc32c(0, entry->name, strlen(entry->name));
	HASH_INSERT(xb_filter_entry_t, name_hash, hash, fold, entry);
4099 4100 4101 4102 4103 4104 4105 4106 4107 4108 4109 4110 4111 4112 4113 4114 4115 4116
	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) {
4117
		die("name `%s` is too long.", name);
4118 4119
	}
	p = strpbrk(name, "/\\~");
Monty's avatar
Monty committed
4120
	if (p && (uint) (p - name) < NAME_LEN) {
4121
		die("name `%s` is not valid.", name);
4122 4123 4124 4125 4126 4127 4128 4129 4130 4131
	}
}

/***********************************************************************
Register new filter entry which can be either database
or table name.  */
static
void
xb_register_filter_entry(
/*=====================*/
Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
4132
	const char*	name,	/*!< in: name */
4133 4134
	hash_table_t* databases_hash,
	hash_table_t* tables_hash
Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
4135
	)
4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 4150
{
	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;

4151
		if (databases_hash && databases_hash->array) {
4152
			const ulint fold = my_crc32c(0, dbname, p - name);
4153
			HASH_SEARCH(name_hash, databases_hash,
4154
					fold,
4155 4156 4157 4158 4159
					xb_filter_entry_t*,
					db_entry, (void) 0,
					!strcmp(db_entry->name, dbname));
		}
		if (!db_entry) {
Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
4160
			db_entry = xb_add_filter(dbname, databases_hash);
4161 4162
		}
		db_entry->has_tables = TRUE;
Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
4163
		xb_add_filter(name, tables_hash);
4164 4165 4166
	} else {
		xb_validate_name(name, namelen);

Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
4167
		xb_add_filter(name, databases_hash);
4168 4169 4170
	}
}

Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
4171 4172 4173 4174 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190
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);
}

4191 4192 4193 4194 4195
void register_ignore_db_dirs_filter(const char *name)
{
  xb_add_filter(name, &databases_exclude_hash);
}

4196 4197 4198 4199 4200 4201 4202 4203 4204
/***********************************************************************
Register new table for the filter.  */
static
void
xb_register_table(
/*==============*/
	const char* name)	/*!< in: name of table */
{
	if (strchr(name, '.') == NULL) {
4205
		die("`%s` is not fully qualified name.", name);
4206 4207
	}

Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
4208
	xb_register_include_filter_entry(name);
4209 4210 4211 4212
}

static
void
Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
4213 4214 4215 4216
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 */
4217 4218 4219 4220
{
	char			errbuf[100];
	int			ret;

Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
4221 4222
	regex_t compiled_regex;
	ret = regcomp(&compiled_regex, regex, REG_EXTENDED);
4223 4224

	if (ret != 0) {
Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
4225
		regerror(ret, &compiled_regex, errbuf, sizeof(errbuf));
4226
		msg("mariabackup: error: %s regcomp(%s): %s",
Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
4227
			error_context, regex, errbuf);
4228 4229 4230
		exit(EXIT_FAILURE);
	}

Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 4253
	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);
4254 4255 4256 4257
}

typedef void (*insert_entry_func_t)(const char*);

4258 4259 4260 4261 4262 4263
/* Scan string and load filter entries from it.
@param[in] list string representing a list
@param[in] delimiters delimiters of entries
@param[in] ins callback to add entry */
void xb_load_list_string(char *list, const char *delimiters,
                         insert_entry_func_t ins)
4264
{
4265 4266
  char *p;
  char *saveptr;
4267

4268 4269 4270
  p= strtok_r(list, delimiters, &saveptr);
  while (p)
  {
4271

4272
    ins(p);
4273

4274 4275
    p= strtok_r(NULL, delimiters, &saveptr);
  }
4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 4291 4292
}

/***********************************************************************
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) {
4293
		die("Can't open %s",
4294 4295 4296 4297 4298 4299 4300
		    filename);
	}
	while (fgets(name_buf, sizeof(name_buf), fp) != NULL) {
		char*	p = strchr(name_buf, '\n');
		if (p) {
			*p = '\0';
		} else {
4301
			die("`%s...` name is too long", name_buf);
4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 4312 4313 4314 4315 4316
		}

		ins(name_buf);
	}

	fclose(fp);
}


static
void
xb_filters_init()
{
	if (xtrabackup_databases) {
		xb_load_list_string(xtrabackup_databases, " \t",
Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
4317
				    xb_register_include_filter_entry);
4318 4319 4320 4321
	}

	if (xtrabackup_databases_file) {
		xb_load_list_file(xtrabackup_databases_file,
Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
4322 4323 4324 4325 4326 4327
				  xb_register_include_filter_entry);
	}

	if (xtrabackup_databases_exclude) {
		xb_load_list_string(xtrabackup_databases_exclude, " \t",
				    xb_register_exclude_filter_entry);
4328 4329 4330 4331
	}

	if (xtrabackup_tables) {
		xb_load_list_string(xtrabackup_tables, ",",
Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
4332
				    xb_register_include_regex);
4333 4334 4335 4336 4337
	}

	if (xtrabackup_tables_file) {
		xb_load_list_file(xtrabackup_tables_file, xb_register_table);
	}
Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
4338 4339 4340 4341 4342

	if (xtrabackup_tables_exclude) {
		xb_load_list_string(xtrabackup_tables_exclude, ",",
				    xb_register_exclude_regex);
	}
4343 4344 4345 4346 4347 4348 4349 4350 4351
}

static
void
xb_filter_hash_free(hash_table_t* hash)
{
	ulint	i;

	/* free the hash elements */
4352
	for (i = 0; i < hash->n_cells; i++) {
4353 4354 4355 4356 4357 4358 4359 4360 4361 4362
		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));
4363 4364
			const ulint fold = my_crc32c(0, prev_table->name,
						     strlen(prev_table->name));
4365
			HASH_DELETE(xb_filter_entry_t, name_hash, hash,
4366
				    fold, prev_table);
4367
			free(prev_table);
4368 4369 4370
		}
	}

4371
	hash->free();
4372 4373
}

Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
4374 4375 4376 4377 4378 4379 4380 4381
static void xb_regex_list_free(regex_list_t* list)
{
	while (list->size() > 0) {
		xb_regfree(&list->front());
		list->pop_front();
	}
}

4382 4383 4384 4385 4386 4387
/************************************************************************
Destroy table filters for partial backup. */
static
void
xb_filters_free()
{
Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
4388 4389 4390
	xb_regex_list_free(&regex_include_list);
	xb_regex_list_free(&regex_exclude_list);

4391 4392
	if (tables_include_hash.array) {
		xb_filter_hash_free(&tables_include_hash);
4393 4394
	}

4395 4396
	if (tables_exclude_hash.array) {
		xb_filter_hash_free(&tables_exclude_hash);
4397 4398
	}

4399 4400
	if (databases_include_hash.array) {
		xb_filter_hash_free(&databases_include_hash);
Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
4401 4402
	}

4403 4404
	if (databases_exclude_hash.array) {
		xb_filter_hash_free(&databases_exclude_hash);
4405 4406 4407
	}
}

4408 4409
#ifdef RLIMIT_NOFILE
/**
4410
Set the open files limit. Based on set_max_open_files().
4411
@param max_file_limit  requested open files limit
4412 4413
@return the resulting open files limit. May be less or more than the requested
value.  */
4414
static ulong xb_set_max_open_files(rlim_t max_file_limit)
4415 4416
{
	struct rlimit rlimit;
4417
	rlim_t old_cur;
4418 4419 4420 4421 4422 4423

	if (getrlimit(RLIMIT_NOFILE, &rlimit)) {

		goto end;
	}

4424
	old_cur = rlimit.rlim_cur;
4425 4426 4427 4428 4429 4430 4431 4432 4433 4434 4435 4436 4437 4438 4439

	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)) {
4440 4441
		/* Use original value */
		max_file_limit = static_cast<ulong>(old_cur);
4442 4443 4444 4445 4446 4447 4448 4449 4450
	} else {

		rlimit.rlim_cur = 0;	/* Safety if next call fails */

		(void) getrlimit(RLIMIT_NOFILE, &rlimit);

		if (rlimit.rlim_cur) {

			/* If call didn't fail */
4451
			max_file_limit = rlimit.rlim_cur;
4452 4453 4454 4455
		}
	}

end:
4456 4457
	return static_cast<ulong>(max_file_limit);
}
4458
#else
Vladislav Vaintroub's avatar
Vladislav Vaintroub committed
4459
# define xb_set_max_open_files(x) 0UL
4460 4461
#endif

4462
static void stop_backup_threads()
4463
{
4464 4465 4466
  mysql_cond_broadcast(&log_copying_stop);

  if (log_copying_running || have_io_watching_thread)
4467
  {
Marko Mäkelä's avatar
Marko Mäkelä committed
4468
    mysql_mutex_unlock(&recv_sys.mutex);
4469 4470
    fputs("mariabackup: Stopping log copying thread", stderr);
    fflush(stderr);
Marko Mäkelä's avatar
Marko Mäkelä committed
4471
    mysql_mutex_lock(&recv_sys.mutex);
4472
    while (log_copying_running || have_io_watching_thread)
4473
    {
4474
      mysql_cond_broadcast(&log_copying_stop);
Marko Mäkelä's avatar
Marko Mäkelä committed
4475
      mysql_mutex_unlock(&recv_sys.mutex);
4476 4477
      putc('.', stderr);
      fflush(stderr);
4478
      std::this_thread::sleep_for(std::chrono::milliseconds(200));
Marko Mäkelä's avatar
Marko Mäkelä committed
4479
      mysql_mutex_lock(&recv_sys.mutex);
4480 4481 4482
    }
    putc('\n', stderr);
  }
4483

4484
  mysql_cond_destroy(&log_copying_stop);
4485 4486
}

4487 4488
/** Implement the core of --backup
@return	whether the operation succeeded */
4489
bool Backup_datasinks::backup_low()
4490
{
4491
	mysql_mutex_lock(&recv_sys.mutex);
4492 4493
	ut_ad(!metadata_to_lsn);

4494 4495
	/* read the latest checkpoint lsn */
	{
4496 4497 4498 4499
		const lsn_t lsn = recv_sys.lsn;
		if (recv_sys.find_checkpoint() == DB_SUCCESS
		    && log_sys.is_latest()) {
			metadata_to_lsn = log_sys.next_checkpoint_lsn;
Marko Mäkelä's avatar
Marko Mäkelä committed
4500
			msg("mariabackup: The latest check point"
4501
			    " (for incremental): '" LSN_PF "'",
4502 4503
			    metadata_to_lsn);
		} else {
4504
			msg("Error: recv_sys.find_checkpoint() failed.");
4505
		}
4506

4507
		recv_sys.lsn = lsn;
4508
		stop_backup_threads();
4509 4510
	}

4511
	if (metadata_to_lsn && xtrabackup_copy_logfile()) {
4512
		mysql_mutex_unlock(&recv_sys.mutex);
4513
		ds_close(dst_log_file);
4514
		dst_log_file = NULL;
4515 4516
		return false;
	}
4517

4518
	mysql_mutex_unlock(&recv_sys.mutex);
4519

4520
	if (ds_close(dst_log_file) || !metadata_to_lsn) {
4521 4522 4523 4524 4525 4526
		dst_log_file = NULL;
		return false;
	}

	dst_log_file = NULL;

4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543
	std::vector<uint32_t> failed_ids;
	std::set_difference(
		fail_undo_ids.begin(), fail_undo_ids.end(),
		undo_trunc_ids.begin(), undo_trunc_ids.end(),
		std::inserter(failed_ids, failed_ids.begin()));

	for (uint32_t id : failed_ids) {
		msg("mariabackup: Failed to read undo log "
		    "tablespace space id %d and there is no undo "
		    "tablespace truncation redo record.",
		    id);
	}

	if (failed_ids.size() > 0) {
		return false;
	}

4544 4545 4546
	if (!xtrabackup_incremental) {
		safe_strcpy(metadata_type, sizeof(metadata_type),
			    "full-backuped");
4547 4548
		metadata_from_lsn = 0;
	} else {
4549 4550
		safe_strcpy(metadata_type, sizeof(metadata_type),
			    "incremental");
4551 4552
		metadata_from_lsn = incremental_lsn;
	}
4553
	metadata_last_lsn = recv_sys.lsn;
4554

4555
	if (!xtrabackup_stream_metadata(m_meta)) {
4556
		msg("Error: failed to stream metadata.");
4557 4558 4559 4560 4561 4562 4563 4564
		return false;
	}
	if (xtrabackup_extra_lsndir) {
		char	filename[FN_REFLEN];

		sprintf(filename, "%s/%s", xtrabackup_extra_lsndir,
			XTRABACKUP_METADATA_FILENAME);
		if (!xtrabackup_write_metadata(filename)) {
4565 4566
			msg("Error: failed to write metadata "
			    "to '%s'.", filename);
4567 4568
			return false;
		}
4569 4570
		sprintf(filename, "%s/%s", xtrabackup_extra_lsndir,
			XTRABACKUP_INFO);
4571 4572
		if (!write_xtrabackup_info(m_data,
		                           mysql_connection, filename, false, false)) {
4573 4574
			msg("Error: failed to write info "
			 "to '%s'.", filename);
4575 4576
			return false;
		}
4577 4578 4579 4580 4581
	}

	return true;
}

4582 4583
/** Implement --backup
@return	whether the operation succeeded */
4584
static bool xtrabackup_backup_func()
4585 4586 4587 4588
{
	MY_STAT			 stat_info;
	uint			 i;
	uint			 count;
4589
	pthread_mutex_t		 count_mutex;
4590
	CorruptedPages corrupted_pages;
4591
	data_thread_ctxt_t 	*data_threads;
4592
	Backup_datasinks backup_datasinks;
4593
	pthread_cond_init(&scanned_lsn_cond, NULL);
4594 4595

#ifdef USE_POSIX_FADVISE
4596
	msg("uses posix_fadvise().");
4597 4598 4599 4600 4601 4602
#endif

	/* cd to datadir */

	if (my_setwd(mysql_real_data_home,MYF(MY_WME)))
	{
4603
		msg("my_setwd() failed , %s", mysql_real_data_home);
4604
		return(false);
4605
	}
4606
	msg("cd to %s", mysql_real_data_home);
4607
	xb_plugin_backup_init(mysql_connection);
4608 4609
	msg("open files limit requested %lu, set to %lu",
	    xb_open_files_limit,
4610 4611 4612 4613 4614 4615
	    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;

Marko Mäkelä's avatar
Marko Mäkelä committed
4616
	srv_n_purge_threads = 1;
4617 4618
	srv_read_only_mode = TRUE;

4619
	srv_operation = SRV_OPERATION_BACKUP;
4620
	log_file_op = backup_file_op;
4621
	undo_space_trunc = backup_undo_trunc;
4622
	first_page_init = backup_first_page_op;
4623
	metadata_to_lsn = 0;
4624 4625

	/* initialize components */
4626 4627
        if(innodb_init_param()) {
fail:
4628
		if (log_copying_running) {
4629
			mysql_mutex_lock(&recv_sys.mutex);
4630
			metadata_to_lsn = 1;
4631
			stop_backup_threads();
4632
			mysql_mutex_unlock(&recv_sys.mutex);
4633 4634
		}

4635
		log_file_op = NULL;
4636
		undo_space_trunc = NULL;
4637
		first_page_init = NULL;
4638 4639 4640 4641
		if (dst_log_file) {
			ds_close(dst_log_file);
			dst_log_file = NULL;
		}
4642
		if (fil_system.is_initialised()) {
4643 4644
			innodb_shutdown();
		}
4645 4646
		return(false);
	}
4647 4648 4649 4650 4651 4652 4653 4654 4655 4656 4657 4658 4659 4660 4661 4662 4663

	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 */
        }
4664
	srv_thread_pool_init();
4665 4666
	/* Reset the system variables in the recovery module. */
	trx_pool_init();
4667
	recv_sys.create();
4668 4669 4670 4671

	xb_filters_init();

	xb_fil_io_init();
4672

4673 4674 4675 4676
	if (os_aio_init()) {
		msg("Error: cannot initialize AIO subsystem");
		goto fail;
	}
4677

4678 4679 4680
	if (!log_sys.create()) {
		goto fail;
	}
4681
	/* get current checkpoint_lsn */
Marko Mäkelä's avatar
Marko Mäkelä committed
4682 4683
	{
		mysql_mutex_lock(&recv_sys.mutex);
4684

Marko Mäkelä's avatar
Marko Mäkelä committed
4685
		dberr_t err = recv_sys.find_checkpoint();
4686

Marko Mäkelä's avatar
Marko Mäkelä committed
4687 4688 4689 4690 4691 4692 4693 4694 4695
		if (err != DB_SUCCESS) {
			msg("Error: cannot read redo log header");
		} else if (!log_sys.is_latest()) {
			msg("Error: cannot process redo log before "
			    "MariaDB 10.8");
			err = DB_ERROR;
		} else {
			recv_needed_recovery = true;
		}
4696
		mysql_mutex_unlock(&recv_sys.mutex);
4697

Marko Mäkelä's avatar
Marko Mäkelä committed
4698 4699 4700
		if (err != DB_SUCCESS) {
			goto fail;
		}
4701 4702
	}

4703 4704 4705 4706 4707 4708
	/* 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("Error: cannot mkdir %d: %s\n",
		    my_errno, xtrabackup_extra_lsndir);
4709
		goto fail;
4710
	}
4711

4712 4713 4714 4715 4716
	/* 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("Error: cannot mkdir %d: %s\n",
		    my_errno, xtrabackup_target_dir);
4717
		goto fail;
4718
	}
4719

4720
	backup_datasinks.init();
4721 4722

	if (!select_history()) {
4723
		goto fail;
4724 4725 4726 4727
	}

	/* open the log file */
	memset(&stat_info, 0, sizeof(MY_STAT));
4728
	dst_log_file = ds_open(backup_datasinks.m_redo, LOG_FILE_NAME, &stat_info);
4729
	if (dst_log_file == NULL) {
4730 4731
		msg("Error: failed to open the target stream for '%s'.",
		    LOG_FILE_NAME);
4732
		goto fail;
4733 4734 4735
	}

	/* label it */
4736 4737
	recv_sys.file_checkpoint = log_sys.next_checkpoint_lsn;
	log_hdr_init();
4738
	/* Write log header*/
4739
	if (ds_write(dst_log_file, log_hdr_buf, 12288)) {
4740
		msg("error: write to logfile failed");
4741
		goto fail;
4742
	}
4743
	log_copying_running = true;
4744 4745 4746

	mysql_cond_init(0, &log_copying_stop, nullptr);

4747
	/* start io throttle */
4748
	if (xtrabackup_throttle) {
4749
		io_ticket = xtrabackup_throttle;
4750 4751
		have_io_watching_thread = true;
		mysql_cond_init(0, &wait_throttle, nullptr);
4752
		std::thread(io_watching_thread).detach();
4753 4754
	}

4755
	/* Populate fil_system with tablespaces to copy */
4756
	if (dberr_t err = xb_load_tablespaces()) {
4757 4758
		msg("merror: xb_load_tablespaces() failed with"
		    " error %s.", ut_strerr(err));
4759
		log_copying_running = false;
4760
		goto fail;
4761 4762
	}

4763
	/* copy log file by current position */
4764

4765
	mysql_mutex_lock(&recv_sys.mutex);
4766
	recv_sys.lsn = log_sys.next_checkpoint_lsn;
4767 4768 4769

	const bool log_copy_failed = xtrabackup_copy_logfile();

4770
	mysql_mutex_unlock(&recv_sys.mutex);
4771

4772 4773
	if (log_copy_failed) {
		log_copying_running = false;
4774
		goto fail;
4775
	}
4776

4777
	DBUG_MARIABACKUP_EVENT("before_innodb_log_copy_thread_started", {});
4778

4779
	std::thread(log_copying_thread).detach();
4780 4781 4782

	/* FLUSH CHANGED_PAGE_BITMAPS call */
	if (!flush_changed_page_bitmaps()) {
4783
		goto fail;
4784 4785 4786 4787 4788
	}

	ut_a(xtrabackup_parallel > 0);

	if (xtrabackup_parallel > 1) {
4789
		msg("mariabackup: Starting %u threads for parallel data "
4790
		    "files transfer", xtrabackup_parallel);
4791 4792
	}

4793 4794
	if (opt_lock_ddl_per_table) {
		mdl_lock_all();
4795 4796 4797

		DBUG_EXECUTE_IF("check_mdl_lock_works",
			dbug_start_query_thread("ALTER TABLE test.t ADD COLUMN mdl_lock_column int",
4798
				"Waiting for table metadata lock", 0, 0););
4799 4800
	}

4801
	datafiles_iter_t it;
4802 4803 4804

	/* Create data copying threads */
	data_threads = (data_thread_ctxt_t *)
4805
		malloc(sizeof(data_thread_ctxt_t) * xtrabackup_parallel);
4806
	count = xtrabackup_parallel;
4807
	pthread_mutex_init(&count_mutex, NULL);
4808 4809

	for (i = 0; i < (uint) xtrabackup_parallel; i++) {
4810
		data_threads[i].it = &it;
4811 4812
		data_threads[i].num = i+1;
		data_threads[i].count = &count;
4813
		data_threads[i].count_mutex = &count_mutex;
4814
		data_threads[i].corrupted_pages = &corrupted_pages;
4815
		data_threads[i].datasinks= &backup_datasinks;
4816
		std::thread(data_copy_thread_func, data_threads + i).detach();
4817 4818 4819 4820
	}

	/* Wait for threads to exit */
	while (1) {
4821
		std::this_thread::sleep_for(std::chrono::seconds(1));
4822 4823 4824 4825
		pthread_mutex_lock(&count_mutex);
		bool stop = count == 0;
		pthread_mutex_unlock(&count_mutex);
		if (stop) {
4826 4827 4828 4829
			break;
		}
	}

4830 4831
	pthread_mutex_destroy(&count_mutex);
	free(data_threads);
4832

4833 4834 4835 4836
	DBUG_ASSERT(backup_datasinks.m_data);
	DBUG_ASSERT(backup_datasinks.m_meta);
	bool ok = backup_start(backup_datasinks.m_data,
	                       backup_datasinks.m_meta, corrupted_pages);
4837

4838
	if (ok) {
4839
		ok = backup_datasinks.backup_low();
4840

4841
		backup_release();
4842

4843
		DBUG_EXECUTE_IF("check_mdl_lock_works",
4844
				pthread_join(dbug_alter_thread, nullptr););
4845

4846
		if (ok) {
4847
			backup_finish(backup_datasinks.m_data);
4848 4849 4850
		}
	}

4851
	if (opt_log_innodb_page_corruption)
4852 4853
		ok = corrupted_pages.print_to_file(backup_datasinks.m_data,
						   MB_CORRUPTED_PAGES_FILE);
4854

4855
	if (!ok) {
4856
		goto fail;
4857 4858
	}

4859 4860 4861
	if (changed_page_bitmap) {
		xb_page_bitmap_deinit(changed_page_bitmap);
	}
4862
	backup_datasinks.destroy();
4863

4864 4865
	msg("Redo log (from LSN " LSN_PF " to " LSN_PF ") was copied.",
	    log_sys.next_checkpoint_lsn, recv_sys.lsn);
4866 4867 4868 4869
	xb_filters_free();

	xb_data_files_close();

4870
	/* Make sure that the latest checkpoint was included */
4871
	if (metadata_to_lsn > recv_sys.lsn) {
4872 4873
		msg("Error: failed to copy enough redo log ("
		    "LSN=" LSN_PF "; checkpoint LSN=" LSN_PF ").",
4874
		    recv_sys.lsn, metadata_to_lsn);
4875
		goto fail;
4876
	}
4877 4878

	innodb_shutdown();
4879
	log_file_op = NULL;
4880
	undo_space_trunc = NULL;
4881
	first_page_init = NULL;
4882
	pthread_cond_destroy(&scanned_lsn_cond);
4883 4884
	if (!corrupted_pages.empty()) {
		ut_ad(opt_log_innodb_page_corruption);
4885 4886 4887
		msg("Error: corrupted innodb pages are found and logged to "
			MB_CORRUPTED_PAGES_FILE " file");
	}
4888
	return(true);
4889 4890
}

4891 4892 4893 4894 4895 4896 4897 4898 4899 4900 4901 4902 4903 4904 4905 4906 4907 4908 4909

/**
This function handles DDL changes at the end of backup, under protection of
FTWRL.  This ensures consistent backup in presence of DDL.

- New tables, that were created during backup, are now copied into backup.
  Also, tablespaces with optimized (no redo loggin DDL) are re-copied into 
  backup. This tablespaces will get the extension ".new" in the backup

- Tables that were renamed during backup, are marked as renamed
  For these, file <old_name>.ren will be created.
  The content of the file is the new tablespace name.

- Tables that were deleted during backup, are marked as deleted
  For these , an empty file <name>.del will be created

  It is the responsibility of the prepare phase to deal with .new, .ren, and .del
  files.
*/
4910
void CorruptedPages::backup_fix_ddl(ds_ctxt *ds_data, ds_ctxt *ds_meta)
4911 4912 4913
{
	std::set<std::string> dropped_tables;
	std::map<std::string, std::string> renamed_tables;
4914
	space_id_to_name_t new_tables;
4915

4916
	/* Disable further DDL on backed up tables (only needed for --no-lock).*/
4917
	mysql_mutex_lock(&recv_sys.mutex);
4918
	log_file_op = backup_file_op_fail;
4919
	mysql_mutex_unlock(&recv_sys.mutex);
4920

4921
	DBUG_MARIABACKUP_EVENT("backup_fix_ddl", {});
4922

4923 4924 4925 4926 4927
	for (space_id_to_name_t::iterator iter = ddl_tracker.tables_in_backup.begin();
		iter != ddl_tracker.tables_in_backup.end();
		iter++) {

		const std::string name = iter->second;
4928
		uint32_t id = iter->first;
4929 4930 4931

		if (ddl_tracker.drops.find(id) != ddl_tracker.drops.end()) {
			dropped_tables.insert(name);
4932
			drop_space(id);
4933 4934 4935 4936 4937 4938 4939 4940 4941 4942
			continue;
		}

		if (ddl_tracker.id_to_name.find(id) == ddl_tracker.id_to_name.end()) {
			continue;
		}

		/* tablespace was affected by DDL. */
		const std::string new_name = ddl_tracker.id_to_name[id];
		if (new_name != name) {
4943
			renamed_tables[name] = new_name;
4944
			if (opt_log_innodb_page_corruption)
4945
				rename_space(id, new_name);
4946 4947 4948 4949 4950 4951 4952 4953
		}
	}

	/* Find tables that were created during backup (and not removed).*/
	for(space_id_to_name_t::iterator iter = ddl_tracker.id_to_name.begin();
		iter != ddl_tracker.id_to_name.end();
		iter++) {

4954
		uint32_t id = iter->first;
4955 4956 4957 4958 4959 4960 4961
		std::string name = iter->second;

		if (ddl_tracker.tables_in_backup.find(id) != ddl_tracker.tables_in_backup.end()) {
			/* already processed above */
			continue;
		}

4962 4963 4964
		if (ddl_tracker.drops.find(id) == ddl_tracker.drops.end()
		    && ddl_tracker.deferred_tables.find(id)
			== ddl_tracker.deferred_tables.end()) {
4965
			dropped_tables.erase(name);
4966
			new_tables[id] = name;
4967
			if (opt_log_innodb_page_corruption)
4968
				drop_space(id);
4969 4970 4971 4972 4973 4974 4975 4976
		}
	}

	// Mark tablespaces for rename
	for (std::map<std::string, std::string>::iterator iter = renamed_tables.begin();
		iter != renamed_tables.end(); ++iter) {
		const std::string old_name = iter->first;
		std::string new_name = iter->second;
4977 4978
		DBUG_ASSERT(ds_data);
		ds_data->backup_file_printf((old_name + ".ren").c_str(), "%s", new_name.c_str());
4979 4980 4981 4982 4983 4984 4985
	}

	// Mark tablespaces for drop
	for (std::set<std::string>::iterator iter = dropped_tables.begin();
		iter != dropped_tables.end();
		iter++) {
		const std::string name(*iter);
4986
		ds_data->backup_file_printf((name + ".del").c_str(), "%s", "");
4987 4988 4989 4990 4991
	}

	//  Load and copy new tables.
	//  Close all datanodes first, reload only new tables.
	std::vector<fil_node_t *> all_nodes;
4992 4993
	datafiles_iter_t it;
	while (fil_node_t *node = datafiles_iter_next(&it)) {
4994 4995 4996 4997 4998 4999
		all_nodes.push_back(node);
	}
	for (size_t i = 0; i < all_nodes.size(); i++) {
		fil_node_t *n = all_nodes[i];
		if (n->space->id == 0)
			continue;
Marko Mäkelä's avatar
Marko Mäkelä committed
5000
		if (n->is_open()) {
5001
			mysql_mutex_lock(&fil_system.mutex);
Marko Mäkelä's avatar
Marko Mäkelä committed
5002
			n->close();
5003
			mysql_mutex_unlock(&fil_system.mutex);
Marko Mäkelä's avatar
Marko Mäkelä committed
5004
		}
5005 5006 5007
		fil_space_free(n->space->id, false);
	}

5008
	DBUG_EXECUTE_IF("check_mdl_lock_works", DBUG_ASSERT(new_tables.size() == 0););
5009

5010 5011 5012 5013 5014 5015 5016 5017 5018 5019 5020 5021 5022 5023 5024 5025 5026 5027 5028 5029 5030 5031 5032 5033 5034 5035 5036 5037 5038 5039 5040 5041 5042 5043 5044
	srv_operation = SRV_OPERATION_BACKUP_NO_DEFER;

	/* Mariabackup detected the FILE_MODIFY or FILE_RENAME
	for the deferred tablespace. So it needs to read the
	tablespace again if innodb doesn't have page0 initialization
	redo log for it */
	for (space_id_to_name_t::iterator iter =
			ddl_tracker.deferred_tables.begin();
	     iter != ddl_tracker.deferred_tables.end();
	     iter++) {
		if (check_if_skip_table(iter->second.c_str())) {
			continue;
		}

		if (first_page_init_ids.find(iter->first)
				!= first_page_init_ids.end()) {
			new_tables[iter->first] = iter->second.c_str();
			continue;
		}

		xb_load_single_table_tablespace(iter->second, false);
	}

	/* Mariabackup doesn't detect any FILE_OP for the deferred
	tablespace. There is a possiblity that page0 could've
	been corrupted persistently in the disk */
	for (auto space_name: defer_space_names) {
		if (!check_if_skip_table(space_name.c_str())) {
			xb_load_single_table_tablespace(
					space_name, false);
		}
	}

	srv_operation = SRV_OPERATION_BACKUP;

5045 5046 5047 5048 5049
	for (const auto &t : new_tables) {
		if (!check_if_skip_table(t.second.c_str())) {
			xb_load_single_table_tablespace(t.second, false,
							t.first);
		}
5050 5051
	}

5052
	datafiles_iter_t it2;
5053

5054
	while (fil_node_t *node = datafiles_iter_next(&it2)) {
5055
		if (!fil_is_user_tablespace_id(node->space->id))
5056
			continue;
5057 5058
		std::string dest_name= filename_to_spacename(
			node->name, strlen(node->name));
5059
		dest_name.append(".new");
5060

5061
		xtrabackup_copy_datafile(ds_data, ds_meta,
Marko Mäkelä's avatar
Marko Mäkelä committed
5062 5063
					 node, 0, dest_name.c_str(),
					 wf_write_through, *this);
5064 5065 5066
	}
}

5067 5068 5069 5070 5071 5072 5073 5074 5075 5076 5077 5078 5079 5080 5081 5082 5083 5084 5085 5086 5087 5088 5089 5090 5091 5092 5093 5094 5095 5096 5097
/* ================= prepare ================= */

/***********************************************************************
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.

5098
@return true on success, false on error.  */
5099
static
5100
bool
5101 5102 5103
xb_space_create_file(
/*==================*/
	const char*	path,		/*!<in: path to tablespace */
5104 5105
	uint32_t	space_id,	/*!<in: space id */
	uint32_t	flags,		/*!<in: tablespace flags */
Marko Mäkelä's avatar
Marko Mäkelä committed
5106
	pfs_os_file_t*	file)		/*!<out: file handle */
5107
{
5108
	bool		ret;
5109

5110 5111
	*file = os_file_create_simple_no_error_handling(
		0, path, OS_FILE_CREATE, OS_FILE_READ_WRITE, false, &ret);
5112
	if (!ret) {
5113
		msg("Can't create file %s", path);
5114 5115 5116 5117
		return ret;
	}

	ret = os_file_set_size(path, *file,
5118 5119
			       FIL_IBD_FILE_INITIAL_SIZE
			       << srv_page_size_shift);
5120
	if (!ret) {
5121
		msg("mariabackup: cannot set size for file %s", path);
5122 5123 5124 5125 5126 5127 5128 5129
		os_file_close(*file);
		os_file_delete(0, path);
		return ret;
	}

	return TRUE;
}

5130 5131
static fil_space_t* fil_space_get_by_name(const char* name)
{
5132 5133 5134 5135 5136 5137 5138 5139 5140
  mysql_mutex_assert_owner(&fil_system.mutex);
  for (fil_space_t &space : fil_system.space_list)
    if (space.chain.start)
      if (const char *str= strstr(space.chain.start->name, name))
        if (!strcmp(str + strlen(name), ".ibd") &&
            (str == space.chain.start->name ||
             IF_WIN(str[-1] == '\\' ||,) str[-1] == '/'))
          return &space;
  return nullptr;
5141 5142
}

5143 5144 5145 5146 5147 5148 5149 5150
/***********************************************************************
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
Marko Mäkelä's avatar
Marko Mäkelä committed
5151
pfs_os_file_t
5152 5153 5154
xb_delta_open_matching_space(
	const char*	dbname,		/* in: path to destination database dir */
	const char*	name,		/* in: name of delta file (without .delta) */
5155
	const xb_delta_info_t& info,
5156 5157
	char*		real_name,	/* out: full path of destination file */
	size_t		real_name_len,	/* out: buffer size for real_name */
5158
	bool* 		success)	/* out: indicates error. true = success */
5159 5160 5161 5162
{
	char			dest_dir[FN_REFLEN];
	char			dest_space_name[FN_REFLEN];
	fil_space_t*		fil_space;
Marko Mäkelä's avatar
Marko Mäkelä committed
5163
	pfs_os_file_t		file;
5164 5165 5166
	xb_filter_entry_t*	table;

	ut_a(dbname != NULL ||
5167
	     !fil_is_user_tablespace_id(info.space_id) ||
5168
	     info.space_id == UINT32_MAX);
5169

5170
	*success = false;
5171 5172 5173 5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 5184 5185 5186 5187 5188

	if (dbname) {
		snprintf(dest_dir, FN_REFLEN, "%s/%s",
			xtrabackup_target_dir, dbname);
		snprintf(dest_space_name, FN_REFLEN, "%s/%s", dbname, name);
	} else {
		snprintf(dest_dir, FN_REFLEN, "%s", xtrabackup_target_dir);
		snprintf(dest_space_name, FN_REFLEN, "%s", name);
	}

	snprintf(real_name, real_name_len,
		 "%s/%s",
		 xtrabackup_target_dir, dest_space_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)) {
5189
		msg("mariabackup: error: cannot create dir %s", dest_dir);
5190 5191 5192
		return file;
	}

5193 5194 5195 5196 5197 5198 5199 5200 5201 5202 5203 5204 5205 5206 5207 5208
	if (!info.space_id && fil_system.sys_space) {
		fil_node_t *node
			= UT_LIST_GET_FIRST(fil_system.sys_space->chain);
		for (; node; node = UT_LIST_GET_NEXT(chain, node)) {
			if (!strcmp(node->name, real_name)) {
				break;
			}
		}
		if (node && node->handle != OS_FILE_CLOSED) {
			*success = true;
			return node->handle;
		}
		msg("mariabackup: Cannot find file %s\n", real_name);
		return OS_FILE_CLOSED;
	}

5209
	mysql_mutex_lock(&recv_sys.mutex);
5210 5211 5212 5213 5214 5215 5216 5217 5218
	if (!fil_is_user_tablespace_id(info.space_id)) {
found:
		/* open the file and return its handle */

		file = os_file_create_simple_no_error_handling(
			0, real_name,
			OS_FILE_OPEN, OS_FILE_READ_WRITE, false, success);

		if (!*success) {
Marko Mäkelä's avatar
Marko Mäkelä committed
5219
			msg("mariabackup: Cannot open file %s\n", real_name);
5220 5221
		}
exit:
5222
		mysql_mutex_unlock(&recv_sys.mutex);
5223
		return file;
5224 5225
	}

5226
	const size_t len = strlen(dest_space_name);
5227 5228
	/* remember space name for further reference */
	table = static_cast<xb_filter_entry_t *>
5229
		(malloc(sizeof(xb_filter_entry_t) +
5230
			len + 1));
5231 5232

	table->name = ((char*)table) + sizeof(xb_filter_entry_t);
5233 5234
	memcpy(table->name, dest_space_name, len + 1);
	const ulint fold = my_crc32c(0, dest_space_name, len);
5235
	HASH_INSERT(xb_filter_entry_t, name_hash, &inc_dir_tables_hash,
5236
		    fold, table);
5237

5238
	mysql_mutex_lock(&fil_system.mutex);
5239
	fil_space = fil_space_get_by_name(dest_space_name);
5240
	mysql_mutex_unlock(&fil_system.mutex);
5241 5242

	if (fil_space != NULL) {
5243
		if (fil_space->id == info.space_id
5244
		    || info.space_id == UINT32_MAX) {
5245 5246 5247 5248 5249 5250
			/* we found matching space */
			goto found;
		} else {

			char	tmpname[FN_REFLEN];

5251
			snprintf(tmpname, FN_REFLEN, "%s/xtrabackup_tmp_#%u",
5252 5253
				 dbname, fil_space->id);

5254
			msg("mariabackup: Renaming %s to %s.ibd",
5255
				fil_space->chain.start->name, tmpname);
5256

5257
			if (fil_space->rename(tmpname, false) != DB_SUCCESS) {
5258
				msg("mariabackup: Cannot rename %s to %s",
5259
					fil_space->chain.start->name, tmpname);
5260 5261 5262 5263 5264
				goto exit;
			}
		}
	}

5265
	if (info.space_id == UINT32_MAX)
5266
	{
5267
		die("Can't handle DDL operation on tablespace "
5268 5269
		    "%s\n", dest_space_name);
	}
5270
	mysql_mutex_lock(&fil_system.mutex);
5271
	fil_space = fil_space_get_by_id(info.space_id);
5272
	mysql_mutex_unlock(&fil_system.mutex);
5273 5274 5275
	if (fil_space != NULL) {
		char	tmpname[FN_REFLEN];

5276
		snprintf(tmpname, sizeof tmpname, "%s.ibd", dest_space_name);
5277

5278
		msg("mariabackup: Renaming %s to %s",
5279
		    fil_space->chain.start->name, tmpname);
5280

5281
		if (fil_space->rename(tmpname, false) != DB_SUCCESS) {
5282
			msg("mariabackup: Cannot rename %s to %s",
5283
			    fil_space->chain.start->name, tmpname);
5284 5285 5286 5287 5288 5289 5290
			goto exit;
		}

		goto found;
	}

	/* No matching space found. create the new one.  */
5291
	const uint32_t flags = info.zip_size
5292
		? get_bit_shift(info.page_size
5293 5294 5295 5296
				>> (UNIV_ZIP_SIZE_SHIFT_MIN - 1))
		<< FSP_FLAGS_POS_ZIP_SSIZE
		| FSP_FLAGS_MASK_POST_ANTELOPE
		| FSP_FLAGS_MASK_ATOMIC_BLOBS
5297
		| (srv_page_size == UNIV_PAGE_SIZE_ORIG
5298
		   ? 0
5299
		   : get_bit_shift(srv_page_size
5300 5301 5302
				   >> (UNIV_ZIP_SIZE_SHIFT_MIN - 1))
		   << FSP_FLAGS_POS_PAGE_SSIZE)
		: FSP_FLAGS_PAGE_SSIZE();
5303 5304
	ut_ad(fil_space_t::zip_size(flags) == info.zip_size);
	ut_ad(fil_space_t::physical_size(flags) == info.page_size);
5305

5306 5307 5308 5309 5310 5311
	mysql_mutex_lock(&fil_system.mutex);
	fil_space_t* space = fil_space_t::create(info.space_id, flags,
						 FIL_TYPE_TABLESPACE, 0,
						 FIL_ENCRYPTION_DEFAULT, true);
	mysql_mutex_unlock(&fil_system.mutex);
	if (space) {
5312 5313 5314
		*success = xb_space_create_file(real_name, info.space_id,
						flags, &file);
	} else {
5315
		msg("Can't create tablespace %s\n", dest_space_name);
5316 5317 5318 5319 5320 5321 5322 5323 5324 5325 5326 5327 5328 5329 5330 5331 5332
	}

	goto exit;
}

/************************************************************************
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*/)
{
Marko Mäkelä's avatar
Marko Mäkelä committed
5333 5334
	pfs_os_file_t	src_file;
	pfs_os_file_t	dst_file;
5335 5336 5337 5338
	char	src_path[FN_REFLEN];
	char	dst_path[FN_REFLEN];
	char	meta_path[FN_REFLEN];
	char	space_name[FN_REFLEN];
5339
	bool	success;
5340 5341 5342 5343 5344

	ibool	last_buffer = FALSE;
	ulint	page_in_buffer;
	ulint	incremental_buffers = 0;

5345
	xb_delta_info_t info(srv_page_size, 0, SRV_TMP_SPACE_ID);
5346 5347
	ulint		page_size;
	ulint		page_size_shift;
5348
	byte*		incremental_buffer = NULL;
5349 5350 5351 5352 5353 5354 5355 5356 5357 5358 5359 5360 5361 5362 5363 5364 5365 5366

	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';

5367 5368
	strncpy(space_name, filename, FN_REFLEN - 1);
	space_name[FN_REFLEN - 1] = '\0';
5369 5370 5371 5372 5373 5374 5375 5376 5377 5378
	space_name[strlen(space_name) -  6] = 0;

	if (!get_meta_path(src_path, meta_path)) {
		goto error;
	}

	if (!xb_read_delta_metadata(meta_path, &info)) {
		goto error;
	}

5379
	page_size = info.page_size;
5380
	page_size_shift = get_bit_shift(page_size);
5381
	msg("page size for %s is %zu bytes",
5382 5383 5384
	    src_path, page_size);
	if (page_size_shift < 10 ||
	    page_size_shift > UNIV_PAGE_SIZE_SHIFT_MAX) {
5385 5386
		msg("error: invalid value of page_size "
		    "(%zu bytes) read from %s", page_size, meta_path);
5387 5388 5389
		goto error;
	}

5390 5391 5392
	src_file = os_file_create_simple_no_error_handling(
		0, src_path,
		OS_FILE_OPEN, OS_FILE_READ_WRITE, false, &success);
5393 5394
	if (!success) {
		os_file_get_last_error(TRUE);
5395
		msg("error: can't open %s", src_path);
5396 5397 5398 5399 5400 5401
		goto error;
	}

	posix_fadvise(src_file, 0, 0, POSIX_FADV_SEQUENTIAL);

	dst_file = xb_delta_open_matching_space(
5402
			dbname, space_name, info,
5403 5404
			dst_path, sizeof(dst_path), &success);
	if (!success) {
5405
		msg("error: can't open %s", dst_path);
5406 5407 5408 5409 5410 5411 5412
		goto error;
	}

	posix_fadvise(dst_file, 0, 0, POSIX_FADV_DONTNEED);

	/* allocate buffer for incremental backup (4096 pages) */
	incremental_buffer = static_cast<byte *>
5413
		(aligned_malloc(page_size / 4 * page_size, page_size));
5414

5415
	msg("Applying %s to %s...", src_path, dst_path);
5416 5417 5418 5419 5420 5421 5422 5423

	while (!last_buffer) {
		ulint cluster_header;

		/* read to buffer */
		/* first block of block cluster */
		offset = ((incremental_buffers * (page_size / 4))
			 << page_size_shift);
Marko Mäkelä's avatar
Marko Mäkelä committed
5424
		if (os_file_read(IORequestRead, src_file,
5425 5426
				 incremental_buffer, offset, page_size,
				 nullptr)
Marko Mäkelä's avatar
Marko Mäkelä committed
5427
		    != DB_SUCCESS) {
5428 5429 5430 5431 5432 5433 5434 5435 5436 5437 5438
			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:
5439 5440
				msg("error: %s seems not "
				    ".delta file.", src_path);
5441 5442 5443
				goto error;
		}

5444 5445 5446
		/* FIXME: If the .delta modifies FSP_SIZE on page 0,
		extend the file to that size. */

5447 5448 5449 5450 5451 5452 5453 5454 5455 5456
		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 */
Marko Mäkelä's avatar
Marko Mäkelä committed
5457 5458
		if (os_file_read(IORequestRead, src_file,
				 incremental_buffer,
5459
				 offset, page_in_buffer * page_size, nullptr)
Marko Mäkelä's avatar
Marko Mäkelä committed
5460
		    != DB_SUCCESS) {
5461 5462 5463 5464 5465 5466 5467 5468 5469 5470 5471 5472 5473 5474 5475
			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;

5476 5477 5478 5479 5480 5481
			uchar *buf = incremental_buffer + page_in_buffer * page_size;
			const os_offset_t off = os_offset_t(offset_on_page)*page_size;

			if (off == 0) {
				/* Read tablespace size from page 0,
				and extend the file to specified size.*/
Marko Mäkelä's avatar
Marko Mäkelä committed
5482 5483 5484 5485 5486 5487 5488
				os_offset_t n_pages = mach_read_from_4(
					buf + FSP_HEADER_OFFSET + FSP_SIZE);
				if (mach_read_from_4(buf
						     + FIL_PAGE_SPACE_ID)) {
					if (!os_file_set_size(
						    dst_path, dst_file,
						    n_pages * page_size))
5489
						goto error;
Marko Mäkelä's avatar
Marko Mäkelä committed
5490
				} else if (fil_space_t* space
5491
					   = fil_system.sys_space) {
Marko Mäkelä's avatar
Marko Mäkelä committed
5492 5493 5494 5495 5496 5497 5498 5499 5500
					/* The system tablespace can
					consist of multiple files. The
					first one has full tablespace
					size in page 0, but only the last
					file should be extended. */
					fil_node_t* n = UT_LIST_GET_FIRST(
						space->chain);
					bool fail = !strcmp(n->name, dst_path)
						&& !fil_space_extend(
5501
							space, uint32_t(n_pages));
Marko Mäkelä's avatar
Marko Mäkelä committed
5502
					if (fail) goto error;
5503
				}
5504 5505
			}

Marko Mäkelä's avatar
Marko Mäkelä committed
5506 5507 5508
			if (os_file_write(IORequestWrite,
					  dst_path, dst_file, buf, off,
					  page_size) != DB_SUCCESS) {
5509 5510 5511 5512
				goto error;
			}
		}

5513 5514 5515 5516 5517 5518 5519
		/* Free file system buffer cache after the batch was written. */
#ifdef __linux__
		os_file_flush_func(dst_file);
#endif
		posix_fadvise(dst_file, 0, 0, POSIX_FADV_DONTNEED);


5520 5521 5522
		incremental_buffers++;
	}

5523
	aligned_free(incremental_buffer);
5524
	if (src_file != OS_FILE_CLOSED) {
5525
		os_file_close(src_file);
5526 5527
		os_file_delete(0,src_path);
	}
5528
	if (dst_file != OS_FILE_CLOSED && info.space_id)
5529 5530 5531 5532
		os_file_close(dst_file);
	return TRUE;

error:
5533
	aligned_free(incremental_buffer);
5534
	if (src_file != OS_FILE_CLOSED)
5535
		os_file_close(src_file);
5536
	if (dst_file != OS_FILE_CLOSED && info.space_id)
5537
		os_file_close(dst_file);
5538
	msg("Error: xtrabackup_apply_delta(): "
5539 5540 5541 5542
	    "failed to apply %s to %s.\n", src_path, dst_path);
	return FALSE;
}

5543 5544 5545 5546 5547 5548 5549 5550 5551 5552 5553 5554 5555

std::string change_extension(std::string filename, std::string new_ext) {
	DBUG_ASSERT(new_ext.size() == 3);
	std::string new_name(filename);
	new_name.resize(new_name.size() - new_ext.size());
	new_name.append(new_ext);
	return new_name;
}


static void rename_file(const char *from,const char *to) {
	msg("Renaming %s to %s\n", from, to);
	if (my_rename(from, to, MY_WME)) {
5556
		die("Can't rename %s to %s errno %d", from, to, errno);
5557 5558 5559 5560 5561 5562
	}
}

static void rename_file(const std::string& from, const std::string &to) {
	rename_file(from.c_str(), to.c_str());
}
5563 5564 5565 5566 5567 5568 5569 5570 5571 5572 5573
/************************************************************************
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 */

5574 5575 5576
/** Rename, and replace destination file, if exists */
static void rename_force(const char *from, const char *to) {
	if (access(to, R_OK) == 0) {
5577
		msg("Removing %s", to);
5578 5579 5580 5581 5582 5583 5584 5585 5586
		if (my_delete(to, MYF(MY_WME))) {
			msg("Can't remove %s, errno %d", to, errno);
			exit(EXIT_FAILURE);
		}
	}
	rename_file(from,to);
}


5587 5588 5589 5590 5591 5592 5593 5594 5595 5596 5597 5598 5599 5600 5601 5602 5603 5604 5605
/** During prepare phase, rename ".new" files, that were created in
backup_fix_ddl() and backup_optimized_ddl_op(), to ".ibd". In the case of
incremental backup, i.e. of arg argument is set, move ".new" files to
destination directory and rename them to ".ibd", remove existing ".ibd.delta"
and ".idb.meta" files in incremental directory to avoid applying delta to
".ibd" file.

@param[in] data_home_dir	path to datadir
@param[in] db_name	database name
@param[in] file_name	file name with suffix
@param[in] arg	destination path, used in incremental backup to notify, that
*.new file must be moved to destibation directory

@return true */
static ibool prepare_handle_new_files(const char *data_home_dir,
                                      const char *db_name,
                                      const char *file_name, void *arg)
{
	const char *dest_dir = static_cast<const char *>(arg);
5606
	std::string src_path = std::string(data_home_dir) + '/' + std::string(db_name) + '/';
5607 5608 5609
	/* Copy "*.new" files from incremental to base dir for incremental backup */
	std::string dest_path=
		dest_dir ? std::string(dest_dir) + '/' + std::string(db_name) +
5610 5611 5612 5613 5614 5615 5616 5617 5618 5619 5620 5621 5622
			'/' : src_path;

	/*
	  A CREATE DATABASE could have happened during the base mariabackup run.
	  In case if the current table file (e.g. `t1.new`) is from such
	  a new database, the database directory may not exist yet in
	  the base backup directory. Let's make sure to check if the directory
	  exists (and create if needed).
	*/
	if (!directory_exists(dest_path.c_str(), true/*create if not exists*/))
		return FALSE;
	src_path+= file_name;
	dest_path+= file_name;
5623 5624 5625

	size_t index = dest_path.find(".new");
	DBUG_ASSERT(index != std::string::npos);
5626
	dest_path.replace(index, strlen(".ibd"), ".ibd");
5627
	rename_force(src_path.c_str(),dest_path.c_str());
5628 5629 5630 5631 5632 5633 5634 5635 5636 5637 5638 5639 5640 5641 5642 5643 5644 5645 5646 5647 5648 5649 5650 5651 5652 5653

	if (dest_dir) {
		/* remove delta and meta files to avoid delta applying for new file */
		index = src_path.find(".new");
		DBUG_ASSERT(index != std::string::npos);
		src_path.replace(index, std::string::npos, ".ibd.delta");
		if (access(src_path.c_str(), R_OK) == 0) {
			msg("Removing %s", src_path.c_str());
			if (my_delete(src_path.c_str(), MYF(MY_WME)))
				die("Can't remove %s, errno %d", src_path.c_str(), errno);
		}
		src_path.replace(index, std::string::npos, ".ibd.meta");
		if (access(src_path.c_str(), R_OK) == 0) {
			msg("Removing %s", src_path.c_str());
			if (my_delete(src_path.c_str(), MYF(MY_WME)))
				die("Can't remove %s, errno %d", src_path.c_str(), errno);
		}

		/* add table name to the container to avoid it's deletion at the end of
		 prepare */
		std::string table_name = std::string(db_name) + '/'
			+ std::string(file_name, file_name + strlen(file_name) - strlen(".new"));
		xb_filter_entry_t *table = static_cast<xb_filter_entry_t *>
			(malloc(sizeof(xb_filter_entry_t) + table_name.size() + 1));
		table->name = ((char*)table) + sizeof(xb_filter_entry_t);
		strcpy(table->name, table_name.c_str());
5654 5655
		const ulint fold = my_crc32c(0, table->name,
					     table_name.size());
Marko Mäkelä's avatar
Marko Mäkelä committed
5656
		HASH_INSERT(xb_filter_entry_t, name_hash, &inc_dir_tables_hash,
5657
			    fold, table);
5658 5659
	}

5660 5661 5662
	return TRUE;
}

5663 5664 5665 5666 5667 5668 5669 5670 5671 5672 5673 5674 5675 5676 5677 5678 5679
/************************************************************************
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" */
5680 5681 5682
	const size_t len = strlen(name) - 4;
	name[len] = '\0';
	const ulint fold = my_crc32c(0, name, len);
5683

5684
	HASH_SEARCH(name_hash, &inc_dir_tables_hash, fold,
5685 5686 5687 5688 5689 5690 5691 5692 5693 5694 5695 5696 5697
		    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);
}

5698
/** Function enumerates files in datadir (provided by path) which are matched
5699
by provided suffix. For each entry callback is called.
5700 5701 5702 5703 5704 5705

@param[in] path	datadir path
@param[in] suffix	suffix to match against
@param[in] func	callback
@param[in] func_arg	arguments for the above callback

5706
@return FALSE if callback for some entry returned FALSE */
5707 5708 5709
static ibool xb_process_datadir(const char *path, const char *suffix,
                                handle_datadir_entry_func_t func,
                                void *func_arg = NULL)
5710 5711
{
	ulint		ret;
5712
	char		dbpath[OS_FILE_MAX_PATH+2];
5713 5714 5715 5716 5717 5718 5719 5720 5721 5722 5723 5724 5725 5726 5727
	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 */
5728 5729 5730
	dbdir = os_file_opendir(path);
	if (UNIV_UNLIKELY(dbdir != IF_WIN(INVALID_HANDLE_VALUE, nullptr))) {
		ret = fil_file_readdir_next_file(&err, path, dbdir, &fileinfo);
5731 5732 5733 5734 5735 5736 5737 5738 5739 5740 5741
		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,
5742
					    fileinfo.name, func_arg))
5743
				{
5744
					os_file_closedir(dbdir);
5745 5746 5747 5748 5749 5750 5751 5752 5753 5754 5755
					return(FALSE);
				}
			}
next_file_item_1:
			ret = fil_file_readdir_next_file(&err,
							path, dbdir,
							&fileinfo);
		}

		os_file_closedir(dbdir);
	} else {
5756
		msg("Can't open dir %s", path);
5757 5758 5759
	}

	/* single table tablespaces */
5760
	dir = os_file_opendir(path);
5761

5762
	if (UNIV_UNLIKELY(dbdir == IF_WIN(INVALID_HANDLE_VALUE, nullptr))) {
5763
		msg("Can't open dir %s", path);
5764
		return TRUE;
5765 5766
	}

5767
	ret = fil_file_readdir_next_file(&err, path, dir, &dbinfo);
5768 5769 5770 5771 5772 5773 5774
	while (ret == 0) {
		if (dbinfo.type == OS_FILE_TYPE_FILE
		    || dbinfo.type == OS_FILE_TYPE_UNKNOWN) {

		        goto next_datadir_item;
		}

Monty's avatar
Monty committed
5775 5776 5777 5778 5779
		snprintf(dbpath, sizeof(dbpath), "%.*s/%.*s",
                         OS_FILE_MAX_PATH/2-1,
                         path,
                         OS_FILE_MAX_PATH/2-1,
                         dbinfo.name);
5780

5781
		dbdir = os_file_opendir(dbpath);
5782

5783
		if (dbdir != IF_WIN(INVALID_HANDLE_VALUE, nullptr)) {
5784 5785 5786 5787 5788 5789 5790 5791 5792 5793 5794 5795 5796 5797 5798 5799 5800 5801 5802
			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,
5803
						    fileinfo.name, func_arg))
5804
					{
5805
						os_file_closedir(dbdir);
FaramosCZ's avatar
FaramosCZ committed
5806
						os_file_closedir(dir);
5807 5808 5809 5810 5811 5812 5813 5814 5815 5816 5817 5818 5819 5820 5821 5822 5823 5824 5825 5826 5827 5828 5829 5830 5831 5832 5833 5834 5835 5836
						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",
5837
		xtrabackup_apply_delta);
5838 5839 5840 5841 5842 5843 5844
}


static
void
innodb_free_param()
{
5845
	srv_sys_space.shutdown();
5846 5847 5848 5849
	free_tmpdir(&mysql_tmpdir_list);
}


5850 5851 5852 5853 5854 5855 5856 5857 5858 5859 5860
/** Check if file exists*/
static bool file_exists(std::string name)
{
	return access(name.c_str(), R_OK) == 0 ;
}

/** Read file content into STL string */
static std::string read_file_as_string(const std::string file) {
	char content[FN_REFLEN];
	FILE *f = fopen(file.c_str(), "r");
	if (!f) {
5861
		msg("Can not open %s", file.c_str());
5862 5863 5864 5865 5866 5867 5868 5869 5870 5871 5872
	}
	size_t len = fread(content, 1, FN_REFLEN, f);
	fclose(f);
	return std::string(content, len);
}

/** Delete file- Provide verbose diagnostics and exit, if operation fails. */
static void delete_file(const std::string& file, bool if_exists = false) {
	if (if_exists && !file_exists(file))
		return;
	if (my_delete(file.c_str(), MYF(MY_WME))) {
5873
		die("Can't remove %s, errno %d", file.c_str(), errno);
5874 5875 5876 5877 5878 5879 5880 5881 5882 5883 5884 5885 5886 5887 5888 5889 5890 5891 5892 5893 5894 5895 5896 5897 5898 5899
	}
}

/**
Rename tablespace during prepare.
Backup in its end phase may generate some .ren files, recording
tablespaces that should be renamed in --prepare.
*/
static void rename_table_in_prepare(const std::string &datadir, const std::string& from , const std::string& to,
	const char *extension=0) {
	if (!extension) {
		static const char *extensions_nonincremental[] = { ".ibd", 0 };
		static const char *extensions_incremental[] = { ".ibd.delta", ".ibd.meta", 0 };
		const char **extensions = xtrabackup_incremental_dir ?
			extensions_incremental : extensions_nonincremental;
		for (size_t i = 0; extensions[i]; i++) {
			rename_table_in_prepare(datadir, from, to, extensions[i]);
		}
		return;
	}
	std::string src = std::string(datadir) + "/" + from + extension;
	std::string dest = std::string(datadir) + "/" + to + extension;
	std::string ren2, tmp;
	if (file_exists(dest)) {
		ren2= std::string(datadir) + "/" + to + ".ren";
		if (!file_exists(ren2)) {
5900
			die("ERROR : File %s was not found, but expected during rename processing\n", ren2.c_str());
5901 5902 5903 5904 5905 5906 5907 5908 5909 5910 5911 5912 5913 5914 5915 5916 5917 5918 5919 5920 5921 5922 5923 5924 5925 5926 5927 5928 5929 5930 5931 5932 5933 5934 5935 5936 5937 5938 5939 5940 5941 5942 5943 5944
		}
		tmp = to + "#";
		rename_table_in_prepare(datadir, to, tmp);
	}
	rename_file(src, dest);
	if (ren2.size()) {
		// Make sure the temp. renamed file is processed.
		std::string to2 = read_file_as_string(ren2);
		rename_table_in_prepare(datadir, tmp, to2);
		delete_file(ren2);
	}
}

static ibool prepare_handle_ren_files(const char *datadir, const char *db, const char *filename, void *) {

	std::string ren_file = std::string(datadir) + "/" + db + "/" + filename;
	if (!file_exists(ren_file))
		return TRUE;

	std::string to = read_file_as_string(ren_file);
	std::string source_space_name = std::string(db) + "/" + filename;
	source_space_name.resize(source_space_name.size() - 4); // remove extension

	rename_table_in_prepare(datadir, source_space_name.c_str(), to.c_str());
	delete_file(ren_file);
	return TRUE;
}

/* Remove tablespaces during backup, based on */
static ibool prepare_handle_del_files(const char *datadir, const char *db, const char *filename, void *) {
	std::string del_file = std::string(datadir) + "/" + db + "/" + filename;
	std::string path(del_file);
	path.resize(path.size() - 4); // remove extension;
	if (xtrabackup_incremental) {
		delete_file(path + ".ibd.delta", true);
		delete_file(path + ".ibd.meta", true);
	}
	else {
		delete_file(path + ".ibd", true);
	}
	delete_file(del_file);
	return TRUE;
}

5945 5946
/** Implement --prepare
@return	whether the operation succeeded */
Marko Mäkelä's avatar
Marko Mäkelä committed
5947
static bool xtrabackup_prepare_func(char** argv)
5948
{
5949
	CorruptedPages corrupted_pages;
5950 5951 5952 5953 5954 5955
	char			 metadata_path[FN_REFLEN];

	/* cd to target-dir */

	if (my_setwd(xtrabackup_real_target_dir,MYF(MY_WME)))
	{
5956
		msg("can't my_setwd %s", xtrabackup_real_target_dir);
5957
		return(false);
5958
	}
5959
	msg("cd to %s", xtrabackup_real_target_dir);
5960

5961 5962
	fil_path_to_mysql_datadir = ".";

5963
	ut_ad(xtrabackup_incremental == xtrabackup_incremental_dir);
Marko Mäkelä's avatar
Marko Mäkelä committed
5964 5965
	if (xtrabackup_incremental)
		inc_dir_tables_hash.create(1000);
5966

5967
	msg("open files limit requested %u, set to %lu",
5968 5969 5970
	    (uint) xb_open_files_limit,
	    xb_set_max_open_files(xb_open_files_limit));

5971 5972 5973 5974 5975 5976 5977 5978
	/* Fix DDL for prepare. Process .del,.ren, and .new files.
	The order in which files are processed, is important
	(see MDEV-18185, MDEV-18201)
	*/
	xb_process_datadir(xtrabackup_incremental_dir ? xtrabackup_incremental_dir : ".",
		".del", prepare_handle_del_files);
	xb_process_datadir(xtrabackup_incremental_dir? xtrabackup_incremental_dir:".",
		".ren", prepare_handle_ren_files);
5979 5980 5981
	if (xtrabackup_incremental_dir) {
		xb_process_datadir(xtrabackup_incremental_dir, ".new.meta", prepare_handle_new_files);
		xb_process_datadir(xtrabackup_incremental_dir, ".new.delta", prepare_handle_new_files);
5982 5983
		xb_process_datadir(xtrabackup_incremental_dir, ".new",
				prepare_handle_new_files, (void *)".");
5984 5985 5986 5987 5988
	}
	else {
		xb_process_datadir(".", ".new", prepare_handle_new_files);
	}

5989
	int argc; for (argc = 0; argv[argc]; argc++) {}
5990
	xb_plugin_prepare_init(argc, argv, xtrabackup_incremental_dir);
5991

5992 5993 5994
	xtrabackup_target_dir= mysql_data_home_buff;
	xtrabackup_target_dir[0]=FN_CURLIB;		// all paths are relative from here
	xtrabackup_target_dir[1]=0;
5995 5996
	const lsn_t target_lsn = xtrabackup_incremental
		? incremental_to_lsn : metadata_to_lsn;
5997 5998

	/*
5999
	  read metadata of target
6000 6001 6002 6003 6004
	*/
	sprintf(metadata_path, "%s/%s", xtrabackup_target_dir,
		XTRABACKUP_METADATA_FILENAME);

	if (!xtrabackup_read_metadata(metadata_path)) {
6005
		msg("Error: failed to read metadata from '%s'\n",
6006
		    metadata_path);
6007
		return(false);
6008 6009
	}

6010
	if (!strcmp(metadata_type, "full-backuped")) {
6011
		if (xtrabackup_incremental) {
6012 6013
			msg("error: applying incremental backup "
			    "needs a prepared target.");
6014 6015
			return(false);
		}
6016
		msg("This target seems to be not prepared yet.");
6017
	} else if (!strcmp(metadata_type, "log-applied")) {
6018
		msg("This target seems to be already prepared.");
6019
	} else {
6020
		msg("This target does not have correct metadata.");
6021
		return(false);
6022
	}
6023

6024 6025 6026
	bool ok = !xtrabackup_incremental
		|| metadata_to_lsn == incremental_lsn;
	if (!ok) {
6027 6028 6029
		msg("error: This incremental backup seems "
		    "not to be proper for the target. Check 'to_lsn' of the target and "
		    "'from_lsn' of the incremental.");
6030
		return(false);
6031 6032 6033
	}

	srv_max_n_threads = 1000;
Marko Mäkelä's avatar
Marko Mäkelä committed
6034
	srv_n_purge_threads = 1;
6035

6036 6037
	xb_filters_init();

6038
	srv_log_group_home_dir = NULL;
6039

6040
	if (xtrabackup_incremental) {
6041 6042
		srv_operation = SRV_OPERATION_RESTORE_DELTA;

6043
		if (innodb_init_param()) {
6044 6045 6046
error:
			ok = false;
			goto cleanup;
6047
		}
6048

6049
		recv_sys.create();
6050 6051 6052
		if (!log_sys.create()) {
			goto error;
		}
6053
		recv_sys.recovery_on = true;
6054

6055 6056
		xb_fil_io_init();
		if (dberr_t err = xb_load_tablespaces()) {
6057
			msg("mariabackup: error: xb_data_files_init() failed "
6058
			    "with error %s\n", ut_strerr(err));
6059
			goto error;
6060
		}
6061

6062 6063
		ok = fil_system.sys_space->open(false)
			&& xtrabackup_apply_deltas();
6064

6065 6066
		xb_data_files_close();

6067 6068 6069 6070 6071 6072
		if (ok) {
			/* Cleanup datadir from tablespaces deleted
			between full and incremental backups */

			xb_process_datadir("./", ".ibd", rm_if_not_found);
		}
6073

6074
		xb_filter_hash_free(&inc_dir_tables_hash);
6075

6076
		fil_system.close();
6077
		innodb_free_param();
6078
		log_sys.close();
6079
		if (!ok) goto cleanup;
6080
	}
6081

6082 6083
	srv_operation = xtrabackup_export
		? SRV_OPERATION_RESTORE_EXPORT : SRV_OPERATION_RESTORE;
6084

6085
	if (innodb_init_param()) {
6086
		goto error;
6087 6088
	}

6089 6090
	fil_system.freeze_space_list = 0;

6091 6092 6093 6094
	msg("Starting InnoDB instance for recovery.");

	msg("mariabackup: Using %lld bytes for buffer pool "
	    "(set by --use-memory parameter)", xtrabackup_use_memory);
6095

6096 6097 6098 6099 6100 6101
	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;
	}

6102
        if (innodb_init()) {
6103
		goto error;
6104 6105
	}

6106 6107
	ut_ad(!fil_system.freeze_space_list);

6108 6109 6110 6111 6112 6113 6114 6115 6116 6117 6118 6119 6120 6121 6122 6123 6124 6125 6126 6127 6128 6129
        corrupted_pages.read_from_file(MB_CORRUPTED_PAGES_FILE);
        if (xtrabackup_incremental)
        {
          char inc_filename[FN_REFLEN];
          sprintf(inc_filename, "%s/%s", xtrabackup_incremental_dir,
                  MB_CORRUPTED_PAGES_FILE);
          corrupted_pages.read_from_file(inc_filename);
        }
        if (!corrupted_pages.empty())
          corrupted_pages.zero_out_free_pages();
        if (corrupted_pages.empty())
        {
          if (!xtrabackup_incremental && unlink(MB_CORRUPTED_PAGES_FILE) &&
              errno != ENOENT)
          {
            char errbuf[MYSYS_STRERROR_SIZE];
            my_strerror(errbuf, sizeof(errbuf), errno);
            die("Error: unlink %s failed: %s", MB_CORRUPTED_PAGES_FILE,
                errbuf);
          }
        }
        else
6130
          corrupted_pages.print_to_file(NULL, MB_CORRUPTED_PAGES_FILE);
6131

6132
	if (ok) {
Marko Mäkelä's avatar
Marko Mäkelä committed
6133
		msg("Last binlog file %s, position %lld",
6134 6135
		    trx_sys.recovered_binlog_filename,
		    longlong(trx_sys.recovered_binlog_offset));
6136 6137 6138
	}

	/* Check whether the log is applied enough or not. */
6139
	if (recv_sys.lsn && recv_sys.lsn < target_lsn) {
6140
		msg("mariabackup: error: "
6141
		    "The log was only applied up to LSN " LSN_PF
6142
		    ", instead of " LSN_PF, recv_sys.lsn, target_lsn);
6143
		ok = false;
6144
	}
6145
#ifdef WITH_WSREP
6146
	else if (ok) xb_write_galera_info(xtrabackup_incremental);
6147
#endif
6148

Marko Mäkelä's avatar
Marko Mäkelä committed
6149
        innodb_shutdown();
6150 6151

        innodb_free_param();
6152 6153

	/* output to metadata file */
6154
	if (ok) {
6155 6156
		char	filename[FN_REFLEN];

6157 6158
		safe_strcpy(metadata_type, sizeof(metadata_type),
			    "log-applied");
6159 6160 6161 6162 6163 6164 6165 6166 6167 6168 6169

		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)) {

6170
			msg("mariabackup: Error: failed to write metadata "
6171
			    "to '%s'", filename);
6172 6173
			ok = false;
		} else if (xtrabackup_extra_lsndir) {
6174 6175
			sprintf(filename, "%s/%s", xtrabackup_extra_lsndir, XTRABACKUP_METADATA_FILENAME);
			if (!xtrabackup_write_metadata(filename)) {
6176
				msg("mariabackup: Error: failed to write "
6177
				    "metadata to '%s'", filename);
6178
				ok = false;
6179 6180 6181 6182
			}
		}
	}

6183
	if (ok) ok = apply_log_finish();
6184

6185 6186 6187
	if (ok && xtrabackup_export)
		ok= (prepare_export() == 0);

6188
cleanup:
6189
	xb_filters_free();
6190
        return ok && !ib::error::was_logged() && corrupted_pages.empty();
6191 6192 6193 6194 6195 6196 6197 6198 6199 6200 6201 6202 6203 6204 6205 6206 6207 6208 6209 6210 6211
}

/**************************************************************************
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);
}

6212 6213 6214 6215 6216 6217 6218 6219 6220 6221 6222 6223 6224 6225 6226 6227 6228 6229 6230 6231 6232 6233 6234 6235 6236 6237 6238 6239 6240 6241 6242 6243 6244 6245 6246 6247 6248 6249
static const char*
normalize_privilege_target_name(const char* name)
{
	if (strcmp(name, "*") == 0) {
		return "\\*";
	}
	else {
		/* should have no regex special characters. */
		ut_ad(strpbrk(name, ".()[]*+?") == 0);
	}
	return name;
}

/******************************************************************//**
Check if specific privilege is granted.
Uses regexp magic to check if requested privilege is granted for given
database.table or database.* or *.*
or if user has 'ALL PRIVILEGES' granted.
@return true if requested privilege is granted, false otherwise. */
static bool
has_privilege(const std::list<std::string> &granted,
	const char* required,
	const char* db_name,
	const char* table_name)
{
	char buffer[1000];
	regex_t priv_re;
	regmatch_t tables_regmatch[1];
	bool result = false;

	db_name = normalize_privilege_target_name(db_name);
	table_name = normalize_privilege_target_name(table_name);

	int written = snprintf(buffer, sizeof(buffer),
		"GRANT .*(%s)|(ALL PRIVILEGES).* ON (\\*|`%s`)\\.(\\*|`%s`)",
		required, db_name, table_name);
	if (written < 0 || written == sizeof(buffer)
		|| regcomp(&priv_re, buffer, REG_EXTENDED)) {
6250
		die("regcomp() failed for '%s'", buffer);
6251 6252 6253 6254 6255 6256 6257 6258 6259 6260 6261 6262 6263 6264 6265 6266 6267 6268 6269 6270 6271 6272 6273 6274 6275 6276 6277 6278 6279 6280 6281 6282 6283 6284 6285 6286 6287 6288 6289 6290 6291
	}

	typedef std::list<std::string>::const_iterator string_iter;
	for (string_iter i = granted.begin(), e = granted.end(); i != e; ++i) {
		int res = regexec(&priv_re, i->c_str(),
			1, tables_regmatch, 0);

		if (res != REG_NOMATCH) {
			result = true;
			break;
		}
	}

	xb_regfree(&priv_re);
	return result;
}

enum {
	PRIVILEGE_OK = 0,
	PRIVILEGE_WARNING = 1,
	PRIVILEGE_ERROR = 2,
};

/******************************************************************//**
Check if specific privilege is granted.
Prints error message if required privilege is missing.
@return PRIVILEGE_OK if requested privilege is granted, error otherwise. */
static
int check_privilege(
	const std::list<std::string> &granted_priv, /* in: list of
							granted privileges*/
	const char* required,		/* in: required privilege name */
	const char* target_database,	/* in: required privilege target
						database name */
	const char* target_table,	/* in: required privilege target
						table name */
	int error = PRIVILEGE_ERROR)	/* in: return value if privilege
						is not granted */
{
	if (!has_privilege(granted_priv,
		required, target_database, target_table)) {
6292
		msg("%s: missing required privilege %s on %s.%s",
6293 6294 6295 6296 6297 6298 6299 6300
			(error == PRIVILEGE_ERROR ? "Error" : "Warning"),
			required, target_database, target_table);
		return error;
	}
	return PRIVILEGE_OK;
}


6301
/**
6302 6303 6304 6305
Check DB user privileges according to the intended actions.

Fetches DB user privileges, determines intended actions based on
command-line arguments and prints missing privileges.
6306 6307
@return whether all the necessary privileges are granted */
static bool check_all_privileges()
6308 6309 6310
{
	if (!mysql_connection) {
		/* Not connected, no queries is going to be executed. */
6311
		return true;
6312 6313 6314 6315 6316
	}

	/* Fetch effective privileges. */
	std::list<std::string> granted_privileges;
	MYSQL_RES* result = xb_mysql_query(mysql_connection, "SHOW GRANTS",
6317 6318
					   true);
	while (MYSQL_ROW row = mysql_fetch_row(result)) {
6319 6320 6321 6322 6323 6324 6325 6326 6327 6328 6329 6330 6331 6332
		granted_privileges.push_back(*row);
	}
	mysql_free_result(result);

	int check_result = PRIVILEGE_OK;

	/* FLUSH TABLES WITH READ LOCK */
	if (!opt_no_lock)
	{
		check_result |= check_privilege(
			granted_privileges,
			"RELOAD", "*", "*");
		check_result |= check_privilege(
			granted_privileges,
6333
			"PROCESS", "*", "*");
6334 6335 6336
	}

	/* KILL ... */
6337
	if (!opt_no_lock && (opt_kill_long_queries_timeout || opt_kill_long_query_type)) {
6338 6339
		check_result |= check_privilege(
			granted_privileges,
6340 6341 6342 6343 6344 6345 6346 6347 6348 6349
			"CONNECTION ADMIN", "*", "*",
			PRIVILEGE_WARNING);
	}

	/* START SLAVE SQL_THREAD */
	/* STOP SLAVE SQL_THREAD */
	if (opt_safe_slave_backup) {
		check_result |= check_privilege(
			granted_privileges,
			"REPLICATION SLAVE ADMIN", "*", "*",
6350 6351 6352 6353 6354 6355
			PRIVILEGE_WARNING);
	}

	/* SHOW MASTER STATUS */
	/* SHOW SLAVE STATUS */
	if (opt_galera_info || opt_slave_info
6356
		|| opt_safe_slave_backup) {
6357
		check_result |= check_privilege(granted_privileges,
6358
			"SLAVE MONITOR", "*", "*",
6359 6360 6361 6362
			PRIVILEGE_WARNING);
	}

	if (check_result & PRIVILEGE_ERROR) {
6363 6364 6365 6366 6367 6368 6369
		msg("Current privileges, as reported by 'SHOW GRANTS': ");
		int n=1;
		for (std::list<std::string>::const_iterator it = granted_privileges.begin();
			it != granted_privileges.end();
			it++,n++) {
				msg("  %d.%s", n, it->c_str());
		}
Marko Mäkelä's avatar
Marko Mäkelä committed
6370
		return false;
6371
	}
Marko Mäkelä's avatar
Marko Mäkelä committed
6372 6373

	return true;
6374 6375
}

6376 6377 6378 6379 6380 6381 6382 6383 6384 6385 6386 6387 6388
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 "
6389
			"cannot be consistent with the backup data.");
6390 6391 6392
		return(false);
	}

6393 6394 6395 6396 6397 6398 6399 6400 6401 6402 6403 6404 6405
	if (xtrabackup_backup && opt_rsync)
	{
		if (xtrabackup_stream_fmt)
		{
			msg("Error: --rsync doesn't work with --stream\n");
			return(false);
		}
		bool have_rsync = IF_WIN(false, (system("rsync --version > /dev/null 2>&1") == 0));
		if (!have_rsync)
		{
			msg("Error: rsync executable not found, cannot run backup with --rsync\n");
			return false;
		}
6406 6407 6408 6409 6410 6411 6412 6413 6414 6415 6416 6417 6418 6419 6420 6421 6422 6423 6424 6425 6426 6427 6428 6429 6430 6431 6432 6433 6434 6435 6436 6437 6438 6439
	}

	n_mixed_options = 0;

	if (opt_decompress) {
		mixed_options[n_mixed_options++] = "--decompress";
	}

	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);
		}
Marko Mäkelä's avatar
Marko Mäkelä committed
6440

6441 6442
		if (opt_check_privileges && !check_all_privileges()) {
			return(false);
6443
		}
Marko Mäkelä's avatar
Marko Mäkelä committed
6444
		history_start_time = time(NULL);
6445 6446 6447 6448 6449
	}

	return(true);
}

6450 6451 6452 6453 6454

extern void init_signals(void);

#include <sql_locale.h>

6455

6456 6457 6458
void setup_error_messages()
{
  my_default_lc_messages = &my_locale_en_US;
6459 6460
	if (init_errmessage())
	  die("could not initialize error messages");
6461 6462
}

6463 6464 6465 6466 6467 6468 6469 6470 6471 6472 6473 6474 6475 6476 6477
/** Handle mariabackup options. The options are handled with the following
order:

1) Load server groups and process server options, ignore unknown options
2) Load client groups and process client options, ignore unknown options
3) Load backup groups and process client-server options, exit on unknown option
4) Process --mysqld-args options, ignore unknown options

@param[in] argc arguments count
@param[in] argv arguments array
@param[out] argv_server server options including loaded from server groups
@param[out] argv_client client options including loaded from client groups
@param[out] argv_backup backup options including loaded from backup groups */
void handle_options(int argc, char **argv, char ***argv_server,
                    char ***argv_client, char ***argv_backup)
6478
{
6479
	/* Setup some variables for Innodb.*/
6480
	srv_operation = SRV_OPERATION_RESTORE;
6481

6482
	files_charset_info = &my_charset_utf8mb3_general_ci;
6483

6484 6485 6486 6487

	setup_error_messages();
	sys_var_init();
	plugin_mutex_init();
6488
	mysql_prlock_init(key_rwlock_LOCK_system_variables_hash, &LOCK_system_variables_hash);
6489 6490 6491 6492 6493 6494 6495 6496
	opt_stack_trace = 1;
	test_flags |=  TEST_SIGINT;
	init_signals();
#ifndef _WIN32
	/* Exit process on SIGINT. */
	my_sigset(SIGINT, SIG_DFL);
#endif

6497
	sf_leaking_memory = 1; /* don't report memory leaks on early exist */
6498

6499 6500 6501 6502 6503 6504 6505 6506
	int i;
	int ho_error;

	char*	target_dir = NULL;
	bool	prepare = false;

	char	conf_file[FN_REFLEN];

6507 6508 6509 6510
        // array_elements() will not work for load_defaults, as it is defined
        // as external symbol, so let's use dynamic array to have ability to
        // add new server default groups
        std::vector<const char *> server_default_groups;
6511

6512 6513 6514
        for (const char **default_group= load_default_groups; *default_group;
             ++default_group)
          server_default_groups.push_back(*default_group);
6515

6516 6517 6518 6519
        std::vector<char *> mysqld_args;
        std::vector<char *> mariabackup_args;
        mysqld_args.push_back(argv[0]);
        mariabackup_args.push_back(argv[0]);
6520

6521 6522 6523 6524 6525 6526 6527 6528 6529 6530 6531 6532
        /* scan options for group and config file to load defaults from */
        for (i= 1; i < argc; i++)
        {
          char *optend= strcend(argv[i], '=');
          if (mysqld_args.size() > 1 ||
              strncmp(argv[i], "--mysqld-args", optend - argv[i]) == 0)
          {
            mysqld_args.push_back(argv[i]);
            continue;
          }
          else
            mariabackup_args.push_back(argv[i]);
6533

6534 6535 6536 6537 6538 6539 6540 6541 6542 6543 6544 6545 6546 6547 6548 6549 6550 6551
          if (strncmp(argv[i], "--defaults-group", optend - argv[i]) == 0)
          {
            defaults_group= optend + 1;
            server_default_groups.push_back(defaults_group);
          }
          else 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));
          }
          else if (!strncmp(argv[i], "--prepare", optend - argv[i]))
          {
            prepare= true;
          }
          else if (!strncmp(argv[i], "--apply-log", optend - argv[i]))
          {
            prepare= true;
          }
6552
          else if (!strncmp(argv[i], "--incremental-dir", optend - argv[i]) &&
6553 6554 6555 6556
                   *optend)
          {
            target_dir= optend + 1;
          }
6557 6558 6559 6560 6561 6562
          else if (!strncmp(argv[i], "--target-dir", optend - argv[i]) &&
                   *optend && !target_dir)
          {
            target_dir= optend + 1;
          }
          else if (!*optend && argv[i][0] != '-' && !target_dir)
6563 6564 6565 6566
          {
            target_dir= argv[i];
          }
        }
6567

6568
        server_default_groups.push_back(NULL);
6569 6570 6571 6572 6573
	snprintf(conf_file, sizeof(conf_file), "my");

	if (prepare && target_dir) {
		snprintf(conf_file, sizeof(conf_file),
			 "%s/backup-my.cnf", target_dir);
6574 6575 6576 6577 6578 6579 6580 6581
			if (!strncmp(argv[1], "--defaults-file=", 16)) {
				/* Remove defaults-file*/
				for (int i = 2; ; i++) {
					if ((argv[i-1]= argv[i]) == 0)
						break;
				}
				argc--;
			}
6582
	}
6583

6584
        mariabackup_args.push_back(nullptr);
6585
        *argv_client= *argv_server= *argv_backup= &mariabackup_args[0];
6586
        int argc_backup= static_cast<int>(mariabackup_args.size() - 1);
6587 6588 6589 6590 6591 6592 6593 6594
        int argc_client= argc_backup;
        int argc_server= argc_backup;

        /* 1) Load server groups and process server options, ignore unknown
         options */

        load_defaults_or_exit(conf_file, &server_default_groups[0],
                              &argc_server, argv_server);
6595

6596 6597 6598 6599
	int n;
	for (n = 0; (*argv_server)[n]; n++) {};
	argc_server = n;

6600 6601 6602 6603 6604 6605 6606 6607 6608 6609 6610 6611 6612 6613 6614 6615 6616 6617 6618 6619
	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 &&
6620 6621
			!strncmp(argv[i], "--defaults-file", optend - argv[i])) {
			die("--defaults-file must be specified first on the command line");
6622
		}
6623 6624 6625 6626
		if (optend - argv[i] == 21 &&
			!strncmp(argv[i], "--defaults-extra-file",
				optend - argv[i])) {
			die("--defaults-extra-file must be specified first on the command line");
6627 6628 6629 6630 6631 6632 6633 6634
		}
	}

	if (argc_server > 0
	    && (ho_error=handle_options(&argc_server, argv_server,
					xb_server_options, xb_get_one_option)))
		exit(ho_error);

6635 6636 6637
        /* 2) Load client groups and process client options, ignore unknown
         options */

6638 6639
	load_defaults_or_exit(conf_file, xb_client_default_groups,
			      &argc_client, argv_client);
6640

6641 6642 6643
	for (n = 0; (*argv_client)[n]; n++) {};
 	argc_client = n;

6644
	if (innobackupex_mode && argc_client > 0) {
6645 6646 6647 6648 6649 6650 6651 6652 6653 6654
		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);

6655 6656 6657 6658 6659 6660 6661 6662 6663 6664 6665 6666 6667 6668 6669 6670 6671 6672 6673 6674 6675 6676 6677 6678 6679 6680 6681 6682 6683 6684 6685 6686 6687 6688
        /* 3) Load backup groups and process client-server options, exit on
         unknown option */

        load_defaults_or_exit(conf_file, backup_default_groups, &argc_backup,
                              argv_backup);
        for (n= 0; (*argv_backup)[n]; n++)
        {
        };
        argc_backup= n;

        my_handle_options_init_variables = FALSE;

        if (argc_backup > 0 &&
            (ho_error= handle_options(&argc_backup, argv_backup,
                                      xb_server_options, xb_get_one_option)))
          exit(ho_error);

        /* Add back the program name handle_options removes */
        ++argc_backup;
        --(*argv_backup);

        if (innobackupex_mode && argc_backup > 0 &&
            !ibx_handle_options(&argc_backup, argv_backup))
          exit(EXIT_FAILURE);

        my_getopt_skip_unknown = FALSE;

        if (argc_backup > 0 &&
            (ho_error= handle_options(&argc_backup, argv_backup,
                                      xb_client_options, xb_get_one_option)))
          exit(ho_error);

        if (opt_password)
        {
6689 6690 6691 6692
          char *argument= (char*) opt_password;
          char *start= (char*) opt_password;
          opt_password= my_strdup(PSI_NOT_INSTRUMENTED, opt_password,
                                  MYF(MY_FAE));
6693 6694 6695 6696 6697 6698 6699 6700 6701 6702 6703 6704 6705 6706 6707 6708 6709 6710 6711 6712 6713
          while (*argument)
            *argument++= 'x'; // Destroy argument
          if (*start)
            start[1]= 0;
        }

        /* 4) Process --mysqld-args options, ignore unknown options */

        my_getopt_skip_unknown = TRUE;

        int argc_mysqld = static_cast<int>(mysqld_args.size());
        if (argc_mysqld > 1)
        {
          char **argv_mysqld= &mysqld_args[0];
          if ((ho_error= handle_options(&argc_mysqld, &argv_mysqld,
                                        xb_server_options, xb_get_one_option)))
            exit(ho_error);
        }

        my_handle_options_init_variables = TRUE;

6714 6715 6716
	/* 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) */
6717 6718
	for (int i = 0 ; i < argc_backup ; i++) {
		const char * const opt = (*argv_backup)[i];
6719 6720 6721 6722 6723

		if (strncmp(opt, "--", 2) &&
		    !(strlen(opt) == 2 && opt[0] == '-')) {
			bool server_option = true;

6724 6725
			for (int j = 0; j < argc_backup; j++) {
				if (opt == (*argv_backup)[j]) {
6726 6727 6728 6729 6730 6731
					server_option = false;
					break;
				}
			}

			if (!server_option) {
6732
				msg("mariabackup: Error:"
6733
				    " unknown argument: '%s'", opt);
6734 6735 6736 6737 6738 6739
				exit(EXIT_FAILURE);
			}
		}
	}
}

6740
static int main_low(char** argv);
6741
static int get_exepath(char *buf, size_t size, const char *argv0);
6742

6743
/* ================= main =================== */
6744 6745
int main(int argc, char **argv)
{
6746 6747 6748
  char **server_defaults;
  char **client_defaults;
  char **backup_defaults;
6749

6750 6751
	my_getopt_prefix_matching= 0;

6752 6753 6754 6755 6756
	if (get_exepath(mariabackup_exe,FN_REFLEN, argv[0]))
    strncpy(mariabackup_exe,argv[0], FN_REFLEN-1);


	if (argc > 1 )
6757
	{
6758 6759 6760 6761 6762 6763
		/* In "prepare export", we need  to start mysqld 
		Since it is not always be installed on the machine,
		we start "mariabackup --mysqld", which acts as mysqld
		*/
		if (strcmp(argv[1], "--mysqld") == 0)
		{
6764
			srv_operation= SRV_OPERATION_EXPORT_RESTORED;
6765 6766 6767 6768 6769 6770 6771 6772 6773 6774 6775 6776
			extern int mysqld_main(int argc, char **argv);
			argc--;
			argv++;
			argv[0]+=2;
			return mysqld_main(argc, argv);
		}
		if(strcmp(argv[1], "--innobackupex") == 0)
		{
			argv++;
			argc--;
			innobackupex_mode = true;
		}
6777
	}
6778 6779 6780
  
	if (argc > 1)
		strncpy(orig_argv1,argv[1],sizeof(orig_argv1) -1);
6781

6782
	init_signals();
6783 6784 6785 6786 6787 6788 6789 6790
	MY_INIT(argv[0]);

	xb_regex_init();

	capture_tool_command(argc, argv);

	if (mysql_server_init(-1, NULL, NULL))
	{
6791
		die("mysql_server_init() failed");
6792 6793
	}

6794
	system_charset_info = &my_charset_utf8mb3_general_ci;
6795 6796
	key_map_full.set_all();

6797
	logger.init_base();
6798
	logger.set_handlers(LOG_NONE, LOG_NONE);
6799 6800 6801
	mysql_mutex_init(key_LOCK_error_log, &LOCK_error_log,
			 MY_MUTEX_INIT_FAST);

6802 6803
        handle_options(argc, argv, &server_defaults, &client_defaults,
                       &backup_defaults);
6804

6805 6806 6807 6808 6809 6810
#ifndef DBUG_OFF
	if (dbug_option) {
		DBUG_SET_INITIAL(dbug_option);
		DBUG_SET(dbug_option);
	}
#endif
6811 6812
	/* Main functions for library */
	init_thr_timer(5);
6813 6814 6815

	int status = main_low(server_defaults);

6816
	end_thr_timer();
6817 6818 6819 6820 6821 6822 6823
	backup_cleanup();

	if (innobackupex_mode) {
		ibx_cleanup();
	}

	free_defaults(server_defaults);
6824 6825
        free_defaults(client_defaults);
        free_defaults(backup_defaults);
6826 6827 6828 6829 6830 6831 6832 6833

#ifndef DBUG_OFF
	if (dbug_option) {
		DBUG_END();
	}
#endif

	logger.cleanup_base();
6834 6835
	cleanup_errmsgs();
	free_error_messages();
6836
	mysql_mutex_destroy(&LOCK_error_log);
6837

6838
	if (status == EXIT_SUCCESS) {
6839
		msg("completed OK!");
6840
	}
6841

6842 6843
	return status;
}
6844

6845 6846
static int main_low(char** argv)
{
6847 6848
	if (innobackupex_mode) {
		if (!ibx_init()) {
6849
			return(EXIT_FAILURE);
6850 6851 6852
		}
	}

6853 6854
	if (!xtrabackup_print_param && !xtrabackup_prepare
	    && !strcmp(mysql_data_home, "./")) {
6855 6856
		if (!xtrabackup_print_param)
			usage();
6857
		msg("mariabackup: Error: Please set parameter 'datadir'");
6858
		return(EXIT_FAILURE);
6859 6860 6861 6862
	}

	/* Expand target-dir, incremental-basedir, etc. */

6863
	char cwd[FN_REFLEN];
6864 6865 6866 6867 6868
	my_getwd(cwd, sizeof(cwd), MYF(0));

	my_load_path(xtrabackup_real_target_dir,
		     xtrabackup_target_dir, cwd);
	unpack_dirname(xtrabackup_real_target_dir,
6869
		       xtrabackup_real_target_dir);
6870 6871 6872 6873 6874 6875
	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,
6876
			       xtrabackup_real_incremental_basedir);
6877 6878 6879 6880 6881 6882 6883 6884
		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,
6885
			       xtrabackup_real_incremental_dir);
6886 6887 6888 6889 6890 6891 6892
		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,
6893
			       xtrabackup_real_extra_lsndir);
6894 6895 6896 6897 6898 6899
		xtrabackup_extra_lsndir = xtrabackup_real_extra_lsndir;
	}

	/* get default temporary directory */
	if (!opt_mysql_tmpdir || !opt_mysql_tmpdir[0]) {
		opt_mysql_tmpdir = getenv("TMPDIR");
6900
#if defined(_WIN32)
6901 6902 6903 6904 6905 6906 6907 6908 6909 6910 6911 6912 6913 6914 6915 6916 6917 6918 6919 6920 6921 6922 6923 6924 6925 6926
		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) {
6927
			msg("mariabackup: value '%s' may be wrong format for "
6928
			    "incremental option.", xtrabackup_incremental);
6929
			return(EXIT_FAILURE);
6930 6931 6932 6933 6934 6935 6936
		}
	} 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)) {
6937
			msg("mariabackup: error: failed to read metadata from "
6938
			    "%s", filename);
6939
			return(EXIT_FAILURE);
6940 6941 6942 6943 6944 6945 6946 6947 6948 6949
		}

		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)) {
6950
			msg("mariabackup: error: failed to read metadata from "
6951
			    "%s", filename);
6952
			return(EXIT_FAILURE);
6953 6954 6955 6956 6957 6958 6959 6960 6961 6962 6963 6964 6965 6966 6967
		}

		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;
	}

6968
	if (xtrabackup_stream && !xtrabackup_backup) {
6969
		msg("Warning: --stream parameter is ignored, it only works together with --backup.");
6970 6971
	}

6972
	if (!xb_init()) {
6973
		return(EXIT_FAILURE);
6974 6975 6976 6977 6978
	}

	/* --print-param */
	if (xtrabackup_print_param) {
		printf("%s", print_param_str.str().c_str());
6979
		return(EXIT_SUCCESS);
6980 6981 6982 6983
	}

	print_version();
	if (xtrabackup_incremental) {
6984
		msg("incremental backup from " LSN_PF " is enabled.",
6985 6986 6987
		    incremental_lsn);
	}

6988
	if (xtrabackup_export && !srv_file_per_table) {
6989
		msg("mariabackup: auto-enabling --innodb-file-per-table due to "
6990
		    "the --export option");
6991
		srv_file_per_table = TRUE;
6992 6993 6994 6995 6996 6997 6998 6999 7000 7001 7002 7003 7004
	}

	/* cannot execute both for now */
	{
		int num = 0;

		if (xtrabackup_backup) 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();
7005
			return(EXIT_FAILURE);
7006 7007 7008
		}
	}

7009 7010 7011 7012 7013 7014 7015 7016 7017 7018 7019 7020 7021 7022
	ut_ad(!field_ref_zero);
	if (auto b = aligned_malloc(UNIV_PAGE_SIZE_MAX, 4096)) {
		field_ref_zero = static_cast<byte*>(
			memset_aligned<4096>(b, 0, UNIV_PAGE_SIZE_MAX));
	} else {
		msg("Can't allocate memory for field_ref_zero");
		return EXIT_FAILURE;
	}

	auto _ = make_scope_exit([]() {
		aligned_free(const_cast<byte*>(field_ref_zero));
		field_ref_zero = nullptr;
		});

7023
	/* --backup */
7024 7025 7026
	if (xtrabackup_backup && !xtrabackup_backup_func()) {
		return(EXIT_FAILURE);
	}
7027 7028

	/* --prepare */
7029 7030 7031
	if (xtrabackup_prepare
	    && !xtrabackup_prepare_func(argv)) {
		return(EXIT_FAILURE);
7032
	}
7033 7034 7035

	if (xtrabackup_copy_back || xtrabackup_move_back) {
		if (!check_if_param_set("datadir")) {
7036
			mysql_data_home = get_default_datadir();
7037 7038
		}
		if (!copy_back())
7039
			return(EXIT_FAILURE);
7040 7041 7042
	}

	if (xtrabackup_decrypt_decompress && !decrypt_decompress()) {
7043
		return(EXIT_FAILURE);
7044 7045
	}

7046
	return(EXIT_SUCCESS);
7047
}
7048 7049 7050 7051 7052


static int get_exepath(char *buf, size_t size, const char *argv0)
{
#ifdef _WIN32
7053
  DWORD ret = GetModuleFileNameA(NULL, buf, (DWORD)size);
7054 7055 7056 7057 7058 7059
  if (ret > 0)
    return 0;
#elif defined(__linux__)
  ssize_t ret = readlink("/proc/self/exe", buf, size-1);
  if(ret > 0)
    return 0;
7060 7061 7062 7063 7064 7065
#elif defined(__APPLE__)
  size_t ret = proc_pidpath(getpid(), buf, static_cast<uint32_t>(size));
  if (ret > 0) {
    buf[ret] = 0;
    return 0;
  }
7066 7067 7068 7069 7070
#elif defined(__FreeBSD__)
  int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
  if (sysctl(mib, 4, buf, &size, NULL, 0) == 0) {
    return 0;
  }
7071 7072 7073 7074
#endif

  return my_realpath(buf, argv0, 0);
}
7075 7076 7077 7078 7079 7080 7081 7082 7083


#if defined (__SANITIZE_ADDRESS__) && defined (__linux__)
/* Avoid LeakSanitizer's false positives. */
const char* __asan_default_options()
{
  return "detect_leaks=0";
}
#endif