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

Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
Copyright (c) 2008, Google Inc.
Copyright (c) 2009, Percona Inc.
Copyright (c) 2013, 2020, MariaDB Corporation.

Portions of this file contain modifications contributed and copyrighted by
Google, Inc. Those modifications are gratefully acknowledged and are described
briefly in the InnoDB documentation. The contributions by Google are
incorporated with their permission, and subject to the conditions contained in
the file COPYING.Google.

Portions of this file contain modifications contributed and copyrighted
by Percona Inc.. Those modifications are
gratefully acknowledged and are described briefly in the InnoDB
documentation. The contributions by Percona Inc. are incorporated with
their permission, and subject to the conditions contained in the file
COPYING.Percona.

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; version 2 of the License.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA

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

/********************************************************************//**
@file srv/srv0start.cc
Starts the InnoDB database server

Created 2/16/1996 Heikki Tuuri
*************************************************************************/

#include "my_global.h"

#include "mysqld.h"
#include "mysql/psi/mysql_stage.h"
#include "mysql/psi/psi.h"

#include "row0ftsort.h"
#include "ut0mem.h"
#include "mem0mem.h"
#include "data0data.h"
#include "data0type.h"
#include "dict0dict.h"
#include "buf0buf.h"
#include "buf0dblwr.h"
#include "buf0dump.h"
#include "os0file.h"
#include "os0thread.h"
#include "fil0fil.h"
#include "fil0crypt.h"
#include "fsp0fsp.h"
#include "rem0rec.h"
#include "mtr0mtr.h"
#include "log0crypt.h"
#include "log0recv.h"
#include "page0page.h"
#include "page0cur.h"
#include "trx0trx.h"
#include "trx0sys.h"
#include "btr0btr.h"
#include "btr0cur.h"
#include "rem0rec.h"
#include "ibuf0ibuf.h"
#include "srv0start.h"
#include "srv0srv.h"
#include "btr0defragment.h"
#include "mysql/service_wsrep.h" /* wsrep_recovery */
#include "trx0rseg.h"
#include "buf0flu.h"
#include "buf0rea.h"
#include "dict0boot.h"
#include "dict0load.h"
#include "dict0stats_bg.h"
#include "que0que.h"
#include "lock0lock.h"
#include "trx0roll.h"
#include "trx0purge.h"
#include "lock0lock.h"
#include "pars0pars.h"
#include "btr0sea.h"
#include "rem0cmp.h"
#include "dict0crea.h"
#include "row0ins.h"
#include "row0sel.h"
#include "row0upd.h"
#include "row0row.h"
#include "row0mysql.h"
#include "btr0pcur.h"
#include "os0event.h"
#include "zlib.h"
#include "ut0crc32.h"

/** We are prepared for a situation that we have this many threads waiting for
a semaphore inside InnoDB. srv_start() sets the value. */
ulint srv_max_n_threads;

/** Log sequence number at shutdown */
lsn_t	srv_shutdown_lsn;

/** TRUE if a raw partition is in use */
ibool	srv_start_raw_disk_in_use;

/** Number of IO threads to use */
ulint	srv_n_file_io_threads;

/** UNDO tablespaces starts with space id. */
ulint	srv_undo_space_id_start;

/** TRUE if the server is being started, before rolling back any
incomplete transactions */
bool	srv_startup_is_before_trx_rollback_phase;
/** TRUE if the server is being started */
bool	srv_is_being_started;
/** TRUE if SYS_TABLESPACES is available for lookups */
bool	srv_sys_tablespaces_open;
/** TRUE if the server was successfully started */
bool	srv_was_started;
/** The original value of srv_log_file_size (innodb_log_file_size) */
static ulonglong	srv_log_file_size_requested;
/** whether srv_start() has been called */
static bool		srv_start_has_been_called;

/** Whether any undo log records can be generated */
UNIV_INTERN bool	srv_undo_sources;

#ifdef UNIV_DEBUG
/** InnoDB system tablespace to set during recovery */
UNIV_INTERN uint	srv_sys_space_size_debug;
/** whether redo log file have been created at startup */
UNIV_INTERN bool	srv_log_file_created;
#endif /* UNIV_DEBUG */

/** whether some background threads that create redo log have been started */
static bool srv_started_redo;

/** At a shutdown this value climbs from SRV_SHUTDOWN_NONE to
SRV_SHUTDOWN_CLEANUP and then to SRV_SHUTDOWN_LAST_PHASE, and so on */
enum srv_shutdown_t	srv_shutdown_state = SRV_SHUTDOWN_NONE;

/** Name of srv_monitor_file */
static char*	srv_monitor_file_name;
std::unique_ptr<tpool::timer> srv_master_timer;

/** */
#define SRV_MAX_N_PENDING_SYNC_IOS	100

#ifdef UNIV_PFS_THREAD
/* Keys to register InnoDB threads with performance schema */
mysql_pfs_key_t	thread_pool_thread_key;
#endif /* UNIV_PFS_THREAD */

#ifdef HAVE_PSI_STAGE_INTERFACE
/** Array of all InnoDB stage events for monitoring activities via
performance schema. */
static PSI_stage_info*	srv_stages[] =
{
	&srv_stage_alter_table_end,
	&srv_stage_alter_table_insert,
	&srv_stage_alter_table_log_index,
	&srv_stage_alter_table_log_table,
	&srv_stage_alter_table_merge_sort,
	&srv_stage_alter_table_read_pk_internal_sort,
	&srv_stage_buffer_pool_load,
};
#endif /* HAVE_PSI_STAGE_INTERFACE */

/*********************************************************************//**
Check if a file can be opened in read-write mode.
@return true if it doesn't exist or can be opened in rw mode. */
static
bool
srv_file_check_mode(
/*================*/
	const char*	name)		/*!< in: filename to check */
{
	os_file_stat_t	stat;

	memset(&stat, 0x0, sizeof(stat));

	dberr_t		err = os_file_get_status(
		name, &stat, true, srv_read_only_mode);

	if (err == DB_FAIL) {
		ib::error() << "os_file_get_status() failed on '" << name
			<< "'. Can't determine file permissions.";
		return(false);

	} else if (err == DB_SUCCESS) {

		/* Note: stat.rw_perm is only valid of files */

		if (stat.type == OS_FILE_TYPE_FILE) {

			if (!stat.rw_perm) {
				const char*	mode = srv_read_only_mode
					? "read" : "read-write";
				ib::error() << name << " can't be opened in "
					<< mode << " mode.";
				return(false);
			}
		} else {
			/* Not a regular file, bail out. */
			ib::error() << "'" << name << "' not a regular file.";

			return(false);
		}
	} else {

		/* This is OK. If the file create fails on RO media, there
		is nothing we can do. */

		ut_a(err == DB_NOT_FOUND);
	}

	return(true);
}

/** Initial number of the redo log file */
static const char INIT_LOG_FILE0[]= "101";

/** Creates log file.
@param[in]  create_new_db   whether the database is being initialized
@param[in]  lsn		    FIL_PAGE_FILE_FLUSH_LSN value
@param[out] logfile0        name of the log file
@return DB_SUCCESS or error code */
static dberr_t create_log_file(bool create_new_db, lsn_t lsn,
                               std::string& logfile0)
{
	if (srv_read_only_mode) {
		ib::error() << "Cannot create log file in read-only mode";
		return DB_READ_ONLY;
	}

	/* Crashing after deleting the first file should be
	recoverable. The buffer pool was clean, and we can simply
	create log file from the scratch. */
	DBUG_EXECUTE_IF("innodb_log_abort_6", delete_log_file("0");
			return DB_ERROR;);

	for (size_t i = 0; i < 102; i++) {
		delete_log_file(std::to_string(i).c_str());
	}

	DBUG_PRINT("ib_log", ("After innodb_log_abort_6"));
	DBUG_ASSERT(!buf_pool.any_io_pending());

	DBUG_EXECUTE_IF("innodb_log_abort_7", return DB_ERROR;);
	DBUG_PRINT("ib_log", ("After innodb_log_abort_7"));

	logfile0 = get_log_file_path(LOG_FILE_NAME_PREFIX)
			   .append(INIT_LOG_FILE0);

	bool ret;
	pfs_os_file_t file = os_file_create(
		innodb_log_file_key, logfile0.c_str(),
		OS_FILE_CREATE|OS_FILE_ON_ERROR_NO_EXIT, OS_FILE_NORMAL,
		OS_LOG_FILE, srv_read_only_mode, &ret);

	if (!ret) {
		ib::error() << "Cannot create " << logfile0;
		return DB_ERROR;
	}

	ib::info() << "Setting log file " << logfile0 << " size to "
		   << srv_log_file_size << " bytes";

	ret = os_file_set_size(logfile0.c_str(), file, srv_log_file_size);
	if (!ret) {
		os_file_close(file);
		ib::error() << "Cannot set log file " << logfile0
			    << " size to " << srv_log_file_size << " bytes";
		return DB_ERROR;
	}

	ret = os_file_close(file);
	ut_a(ret);

	DBUG_EXECUTE_IF("innodb_log_abort_8", return(DB_ERROR););
	DBUG_PRINT("ib_log", ("After innodb_log_abort_8"));

	/* We did not create the first log file initially as LOG_FILE_NAME, so
	that crash recovery cannot find it until it has been completed and
        renamed. */

	log_sys.log.create();
	if (!log_set_capacity(srv_log_file_size_requested)) {
		return DB_ERROR;
	}

	log_sys.log.open_file(logfile0);
	if (!fil_system.sys_space->open(create_new_db)) {
		return DB_ERROR;
	}

	/* Create a log checkpoint. */
	mysql_mutex_lock(&log_sys.mutex);
	if (log_sys.is_encrypted() && !log_crypt_init()) {
		return DB_ERROR;
	}
	ut_d(recv_no_log_write = false);
	lsn = ut_uint64_align_up(lsn, OS_FILE_LOG_BLOCK_SIZE);
	log_sys.set_lsn(lsn + LOG_BLOCK_HDR_SIZE);
	log_sys.log.set_lsn(lsn);
	log_sys.log.set_lsn_offset(LOG_FILE_HDR_SIZE);

	log_sys.buf_next_to_write = 0;
	log_sys.write_lsn = lsn;

	log_sys.next_checkpoint_no = 0;
	log_sys.last_checkpoint_lsn = 0;

	memset(log_sys.buf, 0, srv_log_buffer_size);
	log_block_init(log_sys.buf, lsn);
	log_block_set_first_rec_group(log_sys.buf, LOG_BLOCK_HDR_SIZE);
	memset(log_sys.flush_buf, 0, srv_log_buffer_size);

	log_sys.buf_free = LOG_BLOCK_HDR_SIZE;

	log_sys.log.write_header_durable(lsn);

	mysql_mutex_unlock(&log_sys.mutex);

	log_make_checkpoint();
	log_write_up_to(LSN_MAX, true);

	return DB_SUCCESS;
}

/** Rename the first redo log file.
@param[in]	lsn		FIL_PAGE_FILE_FLUSH_LSN value
@param[in,out]	logfile0	name of the first log file
@return	error code
@retval	DB_SUCCESS	on successful operation */
MY_ATTRIBUTE((warn_unused_result, nonnull))
static dberr_t create_log_file_rename(lsn_t lsn, std::string &logfile0)
{
  ut_ad(!srv_log_file_created);
  ut_d(srv_log_file_created= true);

  DBUG_EXECUTE_IF("innodb_log_abort_9", return (DB_ERROR););
  DBUG_PRINT("ib_log", ("After innodb_log_abort_9"));

  /* Rename the first log file, now that a log checkpoint has been created. */
  auto new_name = get_log_file_path();

  ib::info() << "Renaming log file " << logfile0 << " to " << new_name;

  mysql_mutex_lock(&log_sys.mutex);
  ut_ad(logfile0.size() == 2 + new_name.size());
  logfile0= new_name;
  dberr_t err= log_sys.log.rename(std::move(new_name));

  mysql_mutex_unlock(&log_sys.mutex);

  DBUG_EXECUTE_IF("innodb_log_abort_10", err= DB_ERROR;);

  if (err == DB_SUCCESS)
    ib::info() << "New log file created, LSN=" << lsn;

  return err;
}

/** Create an undo tablespace file
@param[in] name  file name
@return DB_SUCCESS or error code */
static dberr_t srv_undo_tablespace_create(const char* name)
{
	pfs_os_file_t	fh;
	bool		ret;
	dberr_t		err = DB_SUCCESS;

	os_file_create_subdirs_if_needed(name);

	fh = os_file_create(
		innodb_data_file_key,
		name,
		srv_read_only_mode ? OS_FILE_OPEN : OS_FILE_CREATE,
		OS_FILE_NORMAL, OS_DATA_FILE, srv_read_only_mode, &ret);

	if (!ret) {
		if (os_file_get_last_error(false) != OS_FILE_ALREADY_EXISTS
#ifdef UNIV_AIX
			/* AIX 5.1 after security patch ML7 may have
			errno set to 0 here, which causes our function
			to return 100; work around that AIX problem */
		    && os_file_get_last_error(false) != 100
#endif /* UNIV_AIX */
		) {
			ib::error() << "Can't create UNDO tablespace "
				<< name;
		}
		err = DB_ERROR;
	} else if (srv_read_only_mode) {
		ib::info() << name << " opened in read-only mode";
	} else {
		/* We created the data file and now write it full of zeros */

		ib::info() << "Data file " << name << " did not exist: new to"
			" be created";

		ib::info() << "Setting file " << name << " size to "
			<< (SRV_UNDO_TABLESPACE_SIZE_IN_PAGES >> (20 - srv_page_size_shift)) << " MB";

		ib::info() << "Database physically writes the file full: "
			<< "wait...";

		if (!os_file_set_size(name, fh, os_offset_t
				      {SRV_UNDO_TABLESPACE_SIZE_IN_PAGES}
				      << srv_page_size_shift)) {
			ib::error() << "Unable to allocate " << name;
			err = DB_ERROR;
		}

		os_file_close(fh);
	}

	return(err);
}

/* Validate the number of undo opened undo tablespace and user given
undo tablespace
@return DB_SUCCESS if it is valid */
static dberr_t srv_validate_undo_tablespaces()
{
  /* If the user says that there are fewer than what we find we
  tolerate that discrepancy but not the inverse. Because there could
  be unused undo tablespaces for future use. */

  if (srv_undo_tablespaces > srv_undo_tablespaces_open)
  {
    ib::error() << "Expected to open innodb_undo_tablespaces="
                << srv_undo_tablespaces
                << " but was able to find only "
		<< srv_undo_tablespaces_open;

    return DB_ERROR;
  }
  else if (srv_undo_tablespaces_open > 0)
  {
    ib::info() << "Opened " << srv_undo_tablespaces_open
               << " undo tablespaces";

    if (srv_undo_tablespaces == 0)
      ib::warn() << "innodb_undo_tablespaces=0 disables"
                 " dedicated undo log tablespaces";
  }
  return DB_SUCCESS;
}

/** @return the number of active undo tablespaces (except system tablespace) */
static ulint trx_rseg_get_n_undo_tablespaces()
{
  std::set<uint32_t> space_ids;
  mtr_t mtr;
  mtr.start();

  if (const buf_block_t *sys_header= trx_sysf_get(&mtr, false))
    for (ulint rseg_id= 0; rseg_id < TRX_SYS_N_RSEGS; rseg_id++)
      if (trx_sysf_rseg_get_page_no(sys_header, rseg_id) != FIL_NULL)
        if (uint32_t space= trx_sysf_rseg_get_space(sys_header, rseg_id))
          space_ids.insert(space);
  mtr.commit();
  return space_ids.size();
}

/** Open an undo tablespace.
@param[in]	create	whether undo tablespaces are being created
@param[in]	name	tablespace file name
@param[in]	i	undo tablespace count
@return undo tablespace identifier
@retval 0 on failure */
static ulint srv_undo_tablespace_open(bool create, const char* name, ulint i)
{
  bool success;
  char undo_name[sizeof "innodb_undo000"];
  ulint space_id= 0;
  ulint fsp_flags= 0;

  if (create)
  {
    space_id= srv_undo_space_id_start + i;
    snprintf(undo_name, sizeof(undo_name),
             "innodb_undo%03u", static_cast<unsigned>(space_id));
    switch (srv_checksum_algorithm) {
    case SRV_CHECKSUM_ALGORITHM_FULL_CRC32:
    case SRV_CHECKSUM_ALGORITHM_STRICT_FULL_CRC32:
      fsp_flags= FSP_FLAGS_FCRC32_MASK_MARKER | FSP_FLAGS_FCRC32_PAGE_SSIZE();
      break;
    default:
      fsp_flags= FSP_FLAGS_PAGE_SSIZE();
    }
  }

  pfs_os_file_t fh= os_file_create(innodb_data_file_key, name, OS_FILE_OPEN |
                                   OS_FILE_ON_ERROR_NO_EXIT |
                                   OS_FILE_ON_ERROR_SILENT,
                                   OS_FILE_AIO, OS_DATA_FILE,
                                   srv_read_only_mode, &success);

  if (!success)
    return 0;

  os_offset_t size= os_file_get_size(fh);
  ut_a(size != os_offset_t(-1));

  if (!create)
  {
    page_t *page= static_cast<byte*>(aligned_malloc(srv_page_size,
                                                    srv_page_size));
    dberr_t err= os_file_read(IORequestRead, fh, page, 0, srv_page_size);
    if (err != DB_SUCCESS)
    {
err_exit:
      ib::error() << "Unable to read first page of file " << name;
      aligned_free(page);
      return err;
    }

    uint32_t id= mach_read_from_4(FIL_PAGE_SPACE_ID + page);
    if (id == 0 || id >= SRV_SPACE_ID_UPPER_BOUND ||
        memcmp_aligned<2>(FIL_PAGE_SPACE_ID + page,
                          FSP_HEADER_OFFSET + FSP_SPACE_ID + page, 4))
    {
      ib::error() << "Inconsistent tablespace ID in file " << name;
      err= DB_CORRUPTION;
      goto err_exit;
    }

    fsp_flags= mach_read_from_4(FSP_HEADER_OFFSET + FSP_SPACE_FLAGS + page);
    if (buf_page_is_corrupted(false, page, fsp_flags))
    {
      ib::error() << "Checksum mismatch in the first page of file " << name;
      err= DB_CORRUPTION;
      goto err_exit;
    }

    space_id= id;
    snprintf(undo_name, sizeof undo_name, "innodb_undo%03u", id);
    aligned_free(page);
  }

  /* Load the tablespace into InnoDB's internal data structures. */

  /* We set the biggest space id to the undo tablespace
  because InnoDB hasn't opened any other tablespace apart
  from the system tablespace. */

  fil_set_max_space_id_if_bigger(space_id);

  fil_space_t *space= fil_space_t::create(undo_name, space_id, fsp_flags,
					  FIL_TYPE_TABLESPACE, NULL);
  ut_a(fil_validate());
  ut_a(space);

  fil_node_t *file= space->add(name, fh, 0, false, true);
  mutex_enter(&fil_system.mutex);

  if (create)
  {
    space->set_sizes(SRV_UNDO_TABLESPACE_SIZE_IN_PAGES);
    space->size= file->size= uint32_t(size >> srv_page_size_shift);
  }
  else if (!file->read_page0())
  {
    os_file_close(file->handle);
    file->handle= OS_FILE_CLOSED;
    ut_a(fil_system.n_open > 0);
    fil_system.n_open--;
  }

  mutex_exit(&fil_system.mutex);
  return space_id;
}

/** Check if undo tablespaces and redo log files exist before creating a
new system tablespace
@retval DB_SUCCESS  if all undo and redo logs are not found
@retval DB_ERROR    if any undo and redo logs are found */
static
dberr_t
srv_check_undo_redo_logs_exists()
{
	bool		ret;
	pfs_os_file_t	fh;
	char	name[OS_FILE_MAX_PATH];

	/* Check if any undo tablespaces exist */
	for (ulint i = 1; i <= srv_undo_tablespaces; ++i) {

		snprintf(
			name, sizeof(name),
			"%s%cundo%03zu",
			srv_undo_dir, OS_PATH_SEPARATOR,
			i);

		fh = os_file_create(
			innodb_data_file_key, name,
			OS_FILE_OPEN_RETRY
			| OS_FILE_ON_ERROR_NO_EXIT
			| OS_FILE_ON_ERROR_SILENT,
			OS_FILE_NORMAL,
			OS_DATA_FILE,
			srv_read_only_mode,
			&ret);

		if (ret) {
			os_file_close(fh);
			ib::error()
				<< "undo tablespace '" << name << "' exists."
				" Creating system tablespace with existing undo"
				" tablespaces is not supported. Please delete"
				" all undo tablespaces before creating new"
				" system tablespace.";
			return(DB_ERROR);
		}
	}

	/* Check if redo log file exists */
	auto logfilename = get_log_file_path();

	fh = os_file_create(innodb_log_file_key, logfilename.c_str(),
			    OS_FILE_OPEN_RETRY | OS_FILE_ON_ERROR_NO_EXIT
				    | OS_FILE_ON_ERROR_SILENT,
			    OS_FILE_NORMAL, OS_LOG_FILE, srv_read_only_mode,
			    &ret);

	if (ret) {
		os_file_close(fh);
		ib::error() << "redo log file '" << logfilename
			    << "' exists. Creating system tablespace with"
			       " existing redo log file is not recommended."
			       " Please delete redo log file before"
			       " creating new system tablespace.";
		return DB_ERROR;
	}

	return(DB_SUCCESS);
}

static dberr_t srv_all_undo_tablespaces_open(bool create_new_db, ulint n_undo)
{
  /* Open all the undo tablespaces that are currently in use. If we
  fail to open any of these it is a fatal error. The tablespace ids
  should be contiguous. It is a fatal error because they are required
  for recovery and are referenced by the UNDO logs (a.k.a RBS). */

  ulint prev_id= create_new_db ? srv_undo_space_id_start - 1 : 0;

  for (ulint i= 0; i < n_undo; ++i)
  {
    char name[OS_FILE_MAX_PATH];
    snprintf(name, sizeof name, "%s%cundo%03zu", srv_undo_dir,
             OS_PATH_SEPARATOR, i + 1);
    ulint space_id= srv_undo_tablespace_open(create_new_db, name, i);
    if (!space_id)
    {
      if (!create_new_db)
        break;
      ib::error() << "Unable to open create tablespace '" << name << "'.";
      return DB_ERROR;
    }

    /* Should be no gaps in undo tablespace ids. */
    ut_a(!i || prev_id + 1 == space_id);

    prev_id= space_id;

    /* Note the first undo tablespace id in case of
    no active undo tablespace. */
    if (0 == srv_undo_tablespaces_open++)
      srv_undo_space_id_start= space_id;
  }

  /* Open any extra unused undo tablespaces. These must be contiguous.
  We stop at the first failure. These are undo tablespaces that are
  not in use and therefore not required by recovery. We only check
  that there are no gaps. */

  for (ulint i= prev_id + 1; i < srv_undo_space_id_start + TRX_SYS_N_RSEGS;
       ++i)
  {
     char name[OS_FILE_MAX_PATH];
     snprintf(name, sizeof(name),
              "%s%cundo%03zu", srv_undo_dir, OS_PATH_SEPARATOR, i);
     if (!srv_undo_tablespace_open(create_new_db, name, i))
       break;
     ++srv_undo_tablespaces_open;
  }

  return srv_validate_undo_tablespaces();
}

/** Open the configured number of dedicated undo tablespaces.
@param[in]	create_new_db	whether the database is being initialized
@return DB_SUCCESS or error code */
dberr_t
srv_undo_tablespaces_init(bool create_new_db)
{
  srv_undo_tablespaces_open= 0;

  ut_a(srv_undo_tablespaces <= TRX_SYS_N_RSEGS);
  ut_a(!create_new_db || srv_operation == SRV_OPERATION_NORMAL);

  if (srv_undo_tablespaces == 1)
    srv_undo_tablespaces= 0;

  /* Create the undo spaces only if we are creating a new
  instance. We don't allow creating of new undo tablespaces
  in an existing instance (yet). */
  if (create_new_db)
  {
    srv_undo_space_id_start= 1;
    DBUG_EXECUTE_IF("innodb_undo_upgrade", srv_undo_space_id_start= 3;);

    for (ulint i= 0; i < srv_undo_tablespaces; ++i)
    {
      char name[OS_FILE_MAX_PATH];
      snprintf(name, sizeof name, "%s%cundo%03zu",
               srv_undo_dir, OS_PATH_SEPARATOR, i + 1);
      if (dberr_t err= srv_undo_tablespace_create(name))
      {
        ib::error() << "Could not create undo tablespace '" << name << "'.";
        return err;
      }
    }
  }

  /* Get the tablespace ids of all the undo segments excluding
  the system tablespace (0). If we are creating a new instance then
  we build the undo_tablespace_ids ourselves since they don't
  already exist. */
  srv_undo_tablespaces_active= srv_undo_tablespaces;

  ulint n_undo= (create_new_db || srv_operation == SRV_OPERATION_BACKUP ||
                 srv_operation == SRV_OPERATION_RESTORE_DELTA)
    ? srv_undo_tablespaces : TRX_SYS_N_RSEGS;

  if (dberr_t err= srv_all_undo_tablespaces_open(create_new_db, n_undo))
    return err;

  /* Initialize srv_undo_space_id_start=0 when there are no
  dedicated undo tablespaces. */
  if (srv_undo_tablespaces_open == 0)
    srv_undo_space_id_start= 0;

  if (create_new_db)
  {
    mtr_t mtr;
    for (ulint i= 0; i < srv_undo_tablespaces; ++i)
    {
       mtr.start();
       fsp_header_init(fil_space_get(srv_undo_space_id_start + i),
                       SRV_UNDO_TABLESPACE_SIZE_IN_PAGES, &mtr);
       mtr.commit();
    }
  }

  return DB_SUCCESS;
}

/** Create the temporary file tablespace.
@param[in]	create_new_db	whether we are creating a new database
@return DB_SUCCESS or error code. */
static
dberr_t
srv_open_tmp_tablespace(bool create_new_db)
{
	ulint	sum_of_new_sizes;

	/* Will try to remove if there is existing file left-over by last
	unclean shutdown */
	srv_tmp_space.set_sanity_check_status(true);
	srv_tmp_space.delete_files();
	srv_tmp_space.set_ignore_read_only(true);

	ib::info() << "Creating shared tablespace for temporary tables";

	bool	create_new_temp_space;

	srv_tmp_space.set_space_id(SRV_TMP_SPACE_ID);

	dberr_t	err = srv_tmp_space.check_file_spec(
		&create_new_temp_space, 12 * 1024 * 1024);

	if (err == DB_FAIL) {
		ib::error() << "The innodb_temporary"
			" data file must be writable!";
		err = DB_ERROR;
	} else if (err != DB_SUCCESS) {
		ib::error() << "Could not create the shared innodb_temporary.";
	} else if ((err = srv_tmp_space.open_or_create(
			    true, create_new_db, &sum_of_new_sizes, NULL))
		   != DB_SUCCESS) {
		ib::error() << "Unable to create the shared innodb_temporary";
	} else if (fil_system.temp_space->open(true)) {
		/* Initialize the header page */
		mtr_t mtr;
		mtr.start();
		mtr.set_log_mode(MTR_LOG_NO_REDO);
		fsp_header_init(fil_system.temp_space,
				srv_tmp_space.get_sum_of_sizes(),
				&mtr);
		mtr.commit();
	} else {
		/* This file was just opened in the code above! */
		ib::error() << "The innodb_temporary"
			" data file cannot be re-opened"
			" after check_file_spec() succeeded!";
		err = DB_ERROR;
	}

	return(err);
}

/** Shutdown background threads, except the page cleaner. */
static void srv_shutdown_threads()
{
	ut_ad(!srv_undo_sources);
	srv_shutdown_state = SRV_SHUTDOWN_EXIT_THREADS;

	lock_sys.timeout_timer.reset();
	srv_master_timer.reset();

	if (purge_sys.enabled()) {
		srv_purge_shutdown();
	}

	if (srv_n_fil_crypt_threads) {
		fil_crypt_set_thread_cnt(0);
	}
}

#ifdef UNIV_DEBUG
# define srv_init_abort(_db_err)	\
	srv_init_abort_low(create_new_db, __FILE__, __LINE__, _db_err)
#else
# define srv_init_abort(_db_err)	\
	srv_init_abort_low(create_new_db, _db_err)
#endif /* UNIV_DEBUG */

/** Innobase start-up aborted. Perform cleanup actions.
@param[in]	create_new_db	TRUE if new db is  being created
@param[in]	file		File name
@param[in]	line		Line number
@param[in]	err		Reason for aborting InnoDB startup
@return DB_SUCCESS or error code. */
MY_ATTRIBUTE((warn_unused_result, nonnull))
static
dberr_t
srv_init_abort_low(
	bool		create_new_db,
#ifdef UNIV_DEBUG
	const char*	file,
	unsigned	line,
#endif /* UNIV_DEBUG */
	dberr_t		err)
{
	ut_ad(srv_is_being_started);

	if (create_new_db) {
		ib::error() << "Database creation was aborted"
#ifdef UNIV_DEBUG
			" at " << innobase_basename(file) << "[" << line << "]"
#endif /* UNIV_DEBUG */
			" with error " << err << ". You may need"
			" to delete the ibdata1 file before trying to start"
			" up again.";
	} else {
		ib::error() << "Plugin initialization aborted"
#ifdef UNIV_DEBUG
			" at " << innobase_basename(file) << "[" << line << "]"
#endif /* UNIV_DEBUG */
			" with error " << err;
	}

	srv_shutdown_bg_undo_sources();
	srv_shutdown_threads();
	return(err);
}

/** Prepare to delete the redo log file. Flush the dirty pages from all the
buffer pools.  Flush the redo log buffer to the redo log file.
@param[in]	old_exists	old redo log file exists
@return lsn upto which data pages have been flushed. */
static lsn_t srv_prepare_to_delete_redo_log_file(bool old_exists)
{
	DBUG_ENTER("srv_prepare_to_delete_redo_log_file");

	lsn_t	flushed_lsn;
	ulint	count = 0;

	if (log_sys.log.subformat != 2) {
		srv_log_file_size = 0;
	}

	for (;;) {
		/* Clean the buffer pool. */
		buf_flush_sync();

		DBUG_EXECUTE_IF("innodb_log_abort_1", DBUG_RETURN(0););
		DBUG_PRINT("ib_log", ("After innodb_log_abort_1"));

		mysql_mutex_lock(&log_sys.mutex);

		fil_names_clear(log_sys.get_lsn(), false);

		flushed_lsn = log_sys.get_lsn();

		{
			ib::info	info;
			if (srv_log_file_size == 0
			    || (log_sys.log.format & ~log_t::FORMAT_ENCRYPTED)
			    != log_t::FORMAT_10_5) {
				info << "Upgrading redo log: ";
			} else if (!old_exists
				   || srv_log_file_size
				   != srv_log_file_size_requested) {
				if (srv_encrypt_log
				    == (my_bool)log_sys.is_encrypted()) {
					info << (srv_encrypt_log
						 ? "Resizing encrypted"
						 : "Resizing");
				} else if (srv_encrypt_log) {
					info << "Encrypting and resizing";
				} else {
					info << "Removing encryption"
						" and resizing";
				}

				info << " redo log from " << srv_log_file_size
				     << " to ";
			} else if (srv_encrypt_log) {
				info << "Encrypting redo log: ";
			} else {
				info << "Removing redo log encryption: ";
			}

			info << srv_log_file_size_requested
			     << " bytes; LSN=" << flushed_lsn;
		}

		mysql_mutex_unlock(&log_sys.mutex);

		if (flushed_lsn != log_sys.get_flushed_lsn()) {
			log_write_up_to(flushed_lsn, false);
			log_sys.log.flush();
		}

		ut_ad(flushed_lsn == log_get_lsn());

		/* Check if the buffer pools are clean.  If not
		retry till it is clean. */
		if (ulint pending_io = buf_pool.io_pending()) {
			count++;
			/* Print a message every 60 seconds if we
			are waiting to clean the buffer pools */
			if (srv_print_verbose_log && count > 600) {
				ib::info() << "Waiting for "
					<< pending_io << " buffer "
					<< "page I/Os to complete";
				count = 0;
			}

			os_thread_sleep(100000);
			continue;
		}

		break;
	}

	DBUG_RETURN(flushed_lsn);
}

/** Tries to locate LOG_FILE_NAME and check it's size, etc
@param[out]	log_file_found	returns true here if correct file was found
@return	dberr_t with DB_SUCCESS or some error */
static dberr_t find_and_check_log_file(bool &log_file_found)
{
  log_file_found= false;

  auto logfile0= get_log_file_path();
  os_file_stat_t stat_info;
  const dberr_t err= os_file_get_status(logfile0.c_str(), &stat_info, false,
                                        srv_read_only_mode);

  auto is_operation_restore= []() -> bool {
    return srv_operation == SRV_OPERATION_RESTORE ||
           srv_operation == SRV_OPERATION_RESTORE_EXPORT;
  };

  if (err == DB_NOT_FOUND)
  {
    if (is_operation_restore())
      return DB_NOT_FOUND;

    return DB_SUCCESS;
  }

  if (stat_info.type != OS_FILE_TYPE_FILE)
    return DB_SUCCESS;

  if (!srv_file_check_mode(logfile0.c_str()))
    return DB_ERROR;

  const os_offset_t size= stat_info.size;
  ut_a(size != (os_offset_t) -1);

  if (size % OS_FILE_LOG_BLOCK_SIZE)
  {
    ib::error() << "Log file " << logfile0 << " size " << size
                << " is not a multiple of " << OS_FILE_LOG_BLOCK_SIZE
                << " bytes";
    return DB_ERROR;
  }

  if (size == 0 && is_operation_restore())
  {
    /* Tolerate an empty LOG_FILE_NAME from a previous run of
    mariabackup --prepare. */
    return DB_NOT_FOUND;
  }
  /* The first log file must consist of at least the following 512-byte pages:
  header, checkpoint page 1, empty, checkpoint page 2, redo log page(s).

  Mariabackup --prepare would create an empty LOG_FILE_NAME. Tolerate it. */
  if (size != 0 && size <= OS_FILE_LOG_BLOCK_SIZE * 4)
  {
    ib::error() << "Log file " << logfile0 << " size " << size
                << " is too small";
    return DB_ERROR;
  }
  srv_log_file_size= size;

  log_file_found= true;
  return DB_SUCCESS;
}

/** Start InnoDB.
@param[in]	create_new_db	whether to create a new database
@return DB_SUCCESS or error code */
dberr_t srv_start(bool create_new_db)
{
	lsn_t		flushed_lsn;
	dberr_t		err		= DB_SUCCESS;
	bool		srv_log_file_found = true;
	mtr_t		mtr;

	ut_ad(srv_operation == SRV_OPERATION_NORMAL
	      || srv_operation == SRV_OPERATION_RESTORE
	      || srv_operation == SRV_OPERATION_RESTORE_EXPORT);

	if (srv_force_recovery == SRV_FORCE_NO_LOG_REDO) {
		srv_read_only_mode = true;
	}

	high_level_read_only = srv_read_only_mode
		|| srv_force_recovery > SRV_FORCE_NO_IBUF_MERGE
		|| srv_sys_space.created_new_raw();

	srv_started_redo = false;

	compile_time_assert(sizeof(ulint) == sizeof(void*));

#ifdef UNIV_DEBUG
	ib::info() << "!!!!!!!! UNIV_DEBUG switched on !!!!!!!!!";
#endif

#ifdef UNIV_IBUF_DEBUG
	ib::info() << "!!!!!!!! UNIV_IBUF_DEBUG switched on !!!!!!!!!";
#endif

	ib::info() << MUTEX_TYPE;

	ib::info() << "Compressed tables use zlib " ZLIB_VERSION
#ifdef UNIV_ZIP_DEBUG
	      " with validation"
#endif /* UNIV_ZIP_DEBUG */
	      ;
#ifdef UNIV_ZIP_COPY
	ib::info() << "and extra copying";
#endif /* UNIV_ZIP_COPY */

	/* Since InnoDB does not currently clean up all its internal data
	structures in MySQL Embedded Server Library server_end(), we
	print an error message if someone tries to start up InnoDB a
	second time during the process lifetime. */

	if (srv_start_has_been_called) {
		ib::error() << "Startup called second time"
			" during the process lifetime."
			" In the MySQL Embedded Server Library"
			" you cannot call server_init() more than"
			" once during the process lifetime.";
	}

	srv_start_has_been_called = true;

	srv_is_being_started = true;

	/* Register performance schema stages before any real work has been
	started which may need to be instrumented. */
	mysql_stage_register("innodb", srv_stages,
			     static_cast<int>(UT_ARR_SIZE(srv_stages)));

	/* Set the maximum number of threads which can wait for a semaphore
	inside InnoDB: this is the 'sync wait array' size */

	srv_max_n_threads = 1   /* io_ibuf_thread */
			    + 1 /* io_log_thread */
			    + 1 /* srv_print_monitor_task */
			    + 1 /* srv_purge_coordinator_thread */
			    + 1 /* buf_dump_thread */
			    + 1 /* dict_stats_thread */
			    + 1 /* fts_optimize_thread */
			    + 1 /* trx_rollback_all_recovered */
			    + 128 /* added as margin, for use of
				  InnoDB Memcached etc. */
			    + 1/* buf_flush_page_cleaner */
			    + max_connections
			    + srv_n_read_io_threads
			    + srv_n_write_io_threads
			    + srv_n_purge_threads
			    /* FTS Parallel Sort */
			    + fts_sort_pll_degree * FTS_NUM_AUX_INDEX
			      * max_connections;

	srv_boot();

	ib::info() << my_crc32c_implementation();

	if (!srv_read_only_mode) {

		mutex_create(LATCH_ID_SRV_MONITOR_FILE,
			     &srv_monitor_file_mutex);

		if (srv_innodb_status) {

			srv_monitor_file_name = static_cast<char*>(
				ut_malloc_nokey(
					strlen(fil_path_to_mysql_datadir)
					+ 20 + sizeof "/innodb_status."));

			sprintf(srv_monitor_file_name,
				"%s/innodb_status." ULINTPF,
				fil_path_to_mysql_datadir,
				static_cast<ulint>
				(IF_WIN(GetCurrentProcessId(), getpid())));

			srv_monitor_file = my_fopen(srv_monitor_file_name,
						    O_RDWR|O_TRUNC|O_CREAT,
						    MYF(MY_WME));

			if (!srv_monitor_file) {
				ib::error() << "Unable to create "
					<< srv_monitor_file_name << ": "
					<< strerror(errno);
				if (err == DB_SUCCESS) {
					err = DB_ERROR;
				}
			}
		} else {

			srv_monitor_file_name = NULL;
			srv_monitor_file = os_file_create_tmpfile();

			if (!srv_monitor_file && err == DB_SUCCESS) {
				err = DB_ERROR;
			}
		}

		mutex_create(LATCH_ID_SRV_MISC_TMPFILE,
			     &srv_misc_tmpfile_mutex);

		srv_misc_tmpfile = os_file_create_tmpfile();

		if (!srv_misc_tmpfile && err == DB_SUCCESS) {
			err = DB_ERROR;
		}
	}

	if (err != DB_SUCCESS) {
		return(srv_init_abort(err));
	}

	srv_n_file_io_threads = srv_n_read_io_threads;

	srv_n_file_io_threads += srv_n_write_io_threads;

	if (!srv_read_only_mode) {
		/* Add the log and ibuf IO threads. */
		srv_n_file_io_threads += 2;
	} else {
		ib::info() << "Disabling background log and ibuf IO write"
			<< " threads.";
	}

	ut_a(srv_n_file_io_threads <= SRV_MAX_N_IO_THREADS);

	if (!os_aio_init(srv_n_read_io_threads,
			 srv_n_write_io_threads,
			 SRV_MAX_N_PENDING_SYNC_IOS)) {

		ib::error() << "Cannot initialize AIO sub-system";

		return(srv_init_abort(DB_ERROR));
	}

	fil_system.create(srv_file_per_table ? 50000 : 5000);

	ib::info() << "Initializing buffer pool, total size = "
		<< srv_buf_pool_size
		<< ", chunk size = " << srv_buf_pool_chunk_unit;

	if (buf_pool.create()) {
		ib::error() << "Cannot allocate memory for the buffer pool";

		return(srv_init_abort(DB_ERROR));
	}

	ib::info() << "Completed initialization of buffer pool";

#ifdef UNIV_DEBUG
	/* We have observed deadlocks with a 5MB buffer pool but
	the actual lower limit could very well be a little higher. */

	if (srv_buf_pool_size <= 5 * 1024 * 1024) {

		ib::info() << "Small buffer pool size ("
			<< srv_buf_pool_size / 1024 / 1024
			<< "M), the flst_validate() debug function can cause a"
			<< " deadlock if the buffer pool fills up.";
	}
#endif /* UNIV_DEBUG */

	log_sys.create();
	recv_sys.create();
	lock_sys.create(srv_lock_table_size);


	if (!srv_read_only_mode) {
		buf_flush_page_cleaner_init();
		ut_ad(buf_page_cleaner_is_active);
	}

	srv_startup_is_before_trx_rollback_phase = !create_new_db;

	/* Check if undo tablespaces and redo log files exist before creating
	a new system tablespace */
	if (create_new_db) {
		err = srv_check_undo_redo_logs_exists();
		if (err != DB_SUCCESS) {
			return(srv_init_abort(DB_ERROR));
		}
		recv_sys.debug_free();
	}

	/* Open or create the data files. */
	ulint	sum_of_new_sizes;

	err = srv_sys_space.open_or_create(
		false, create_new_db, &sum_of_new_sizes, &flushed_lsn);

	switch (err) {
	case DB_SUCCESS:
		break;
	case DB_CANNOT_OPEN_FILE:
		ib::error()
			<< "Could not open or create the system tablespace. If"
			" you tried to add new data files to the system"
			" tablespace, and it failed here, you should now"
			" edit innodb_data_file_path in my.cnf back to what"
			" it was, and remove the new ibdata files InnoDB"
			" created in this failed attempt. InnoDB only wrote"
			" those files full of zeros, but did not yet use"
			" them in any way. But be careful: do not remove"
			" old data files which contain your precious data!";
		/* fall through */
	default:
		/* Other errors might come from Datafile::validate_first_page() */
		return(srv_init_abort(err));
	}

	srv_log_file_size_requested = srv_log_file_size;

	if (innodb_encrypt_temporary_tables && !log_crypt_init()) {
		return srv_init_abort(DB_ERROR);
	}

	std::string logfile0;
	bool create_new_log = create_new_db;
	if (create_new_db) {
		flushed_lsn = log_sys.get_lsn();
		log_sys.set_flushed_lsn(flushed_lsn);
		buf_flush_sync();

		err = create_log_file(true, flushed_lsn, logfile0);

		if (err != DB_SUCCESS) {
			return(srv_init_abort(err));
		}
	} else {
		srv_log_file_size = 0;

		bool log_file_found;
		if (dberr_t err = find_and_check_log_file(log_file_found)) {
			if (err == DB_NOT_FOUND) {
				return DB_SUCCESS;
			}
			return srv_init_abort(err);
		}

		create_new_log = srv_log_file_size == 0;
		if (create_new_log) {
			if (flushed_lsn < lsn_t(1000)) {
				ib::error()
					<< "Cannot create log file because"
					" data files are corrupt or the"
					" database was not shut down cleanly"
					" after creating the data files.";
				return srv_init_abort(DB_ERROR);
			}

			srv_log_file_size = srv_log_file_size_requested;

			err = create_log_file(false, flushed_lsn, logfile0);

			if (err == DB_SUCCESS) {
				err = create_log_file_rename(flushed_lsn,
							     logfile0);
			}

			if (err != DB_SUCCESS) {
				return(srv_init_abort(err));
			}

			/* Suppress the message about
			crash recovery. */
			flushed_lsn = log_get_lsn();
			goto file_checked;
		}

		srv_log_file_found = log_file_found;

		log_sys.log.open_file(get_log_file_path());

		log_sys.log.create();

		if (!log_set_capacity(srv_log_file_size_requested)) {
			return(srv_init_abort(DB_ERROR));
		}
	}

file_checked:
	/* Open log file and data files in the systemtablespace: we keep
        them open until database shutdown */
	ut_d(fil_system.sys_space->recv_size = srv_sys_space_size_debug);

	err = fil_system.sys_space->open(create_new_db)
		? srv_undo_tablespaces_init(create_new_db)
		: DB_ERROR;

	/* If the force recovery is set very high then we carry on regardless
	of all errors. Basically this is fingers crossed mode. */

	if (err != DB_SUCCESS
	    && srv_force_recovery < SRV_FORCE_NO_UNDO_LOG_SCAN) {

		return(srv_init_abort(err));
	}

	/* Initialize objects used by dict stats gathering thread, which
	can also be used by recovery if it tries to drop some table */
	if (!srv_read_only_mode) {
		dict_stats_init();
	}

	trx_sys.create();

	if (create_new_db) {
		ut_ad(!srv_read_only_mode);

		mtr_start(&mtr);
		ut_ad(fil_system.sys_space->id == 0);
		compile_time_assert(TRX_SYS_SPACE == 0);
		compile_time_assert(IBUF_SPACE_ID == 0);
		fsp_header_init(fil_system.sys_space,
				uint32_t(sum_of_new_sizes), &mtr);

		ulint ibuf_root = btr_create(
			DICT_CLUSTERED | DICT_IBUF, fil_system.sys_space,
			DICT_IBUF_ID_MIN, nullptr, &mtr);

		mtr_commit(&mtr);

		if (ibuf_root == FIL_NULL) {
			return(srv_init_abort(DB_ERROR));
		}

		ut_ad(ibuf_root == IBUF_TREE_ROOT_PAGE_NO);

		/* To maintain backward compatibility we create only
		the first rollback segment before the double write buffer.
		All the remaining rollback segments will be created later,
		after the double write buffer has been created. */
		trx_sys_create_sys_pages();
		trx_lists_init_at_db_start();

		err = dict_create();

		if (err != DB_SUCCESS) {
			return(srv_init_abort(err));
		}

		buf_flush_sync();

		flushed_lsn = log_get_lsn();

		err = fil_write_flushed_lsn(flushed_lsn);

		if (err == DB_SUCCESS) {
			err = create_log_file_rename(flushed_lsn, logfile0);
		}

		if (err != DB_SUCCESS) {
			return(srv_init_abort(err));
		}
	} else {
		/* Suppress warnings in fil_space_t::create() for files
		that are being read before dict_boot() has recovered
		DICT_HDR_MAX_SPACE_ID. */
		fil_system.space_id_reuse_warned = true;

		/* We always try to do a recovery, even if the database had
		been shut down normally: this is the normal startup path */

		err = create_new_log
			? DB_SUCCESS
			: recv_recovery_from_checkpoint_start(flushed_lsn);
		recv_sys.close_files();

		recv_sys.dblwr.pages.clear();

		if (err != DB_SUCCESS) {
			return(srv_init_abort(err));
		}

		switch (srv_operation) {
		case SRV_OPERATION_NORMAL:
		case SRV_OPERATION_RESTORE_EXPORT:
			/* Initialize the change buffer. */
			err = dict_boot();
			if (err != DB_SUCCESS) {
				return(srv_init_abort(err));
			}
			/* fall through */
		case SRV_OPERATION_RESTORE:
			/* This must precede
			recv_apply_hashed_log_recs(true). */
			srv_undo_tablespaces_active
				= trx_rseg_get_n_undo_tablespaces();
			err = srv_validate_undo_tablespaces();
			if (err != DB_SUCCESS) {
				return srv_init_abort(err);
			}
			if (srv_operation == SRV_OPERATION_RESTORE) {
				break;
			}
			trx_lists_init_at_db_start();
			break;
		case SRV_OPERATION_RESTORE_DELTA:
		case SRV_OPERATION_BACKUP:
			ut_ad("wrong mariabackup mode" == 0);
		}

		if (srv_force_recovery < SRV_FORCE_NO_LOG_REDO) {
			/* Apply the hashed log records to the
			respective file pages, for the last batch of
			recv_group_scan_log_recs(). */

			recv_sys.apply(true);

			if (recv_sys.found_corrupt_log
			    || recv_sys.found_corrupt_fs) {
				return(srv_init_abort(DB_CORRUPTION));
			}

			DBUG_PRINT("ib_log", ("apply completed"));

			if (recv_needed_recovery) {
				trx_sys_print_mysql_binlog_offset();
			}
		}

		fil_system.space_id_reuse_warned = false;

		if (!srv_read_only_mode) {
			const ulint flags = FSP_FLAGS_PAGE_SSIZE();
			for (ulint id = 0; id <= srv_undo_tablespaces; id++) {
				if (fil_space_t* space = fil_space_get(id)) {
					fsp_flags_try_adjust(space, flags);
				}
			}

			if (sum_of_new_sizes > 0) {
				/* New data file(s) were added */
				mtr.start();
				mtr.x_lock_space(fil_system.sys_space,
						 __FILE__, __LINE__);
				buf_block_t* block = buf_page_get(
					page_id_t(0, 0), 0,
					RW_SX_LATCH, &mtr);
				ulint size = mach_read_from_4(
					FSP_HEADER_OFFSET + FSP_SIZE
					+ block->frame);
				ut_ad(size == fil_system.sys_space
				      ->size_in_header);
				size += sum_of_new_sizes;
				mtr.write<4>(*block,
					     FSP_HEADER_OFFSET + FSP_SIZE
					     + block->frame, size);
				fil_system.sys_space->size_in_header
					= uint32_t(size);
				mtr.commit();
				/* Immediately write the log record about
				increased tablespace size to disk, so that it
				is durable even if mysqld would crash
				quickly */
				log_buffer_flush_to_disk();
			}
		}

#ifdef UNIV_DEBUG
		{
			mtr.start();
			buf_block_t* block = buf_page_get(page_id_t(0, 0), 0,
							  RW_S_LATCH, &mtr);
			ut_ad(mach_read_from_4(FSP_SIZE + FSP_HEADER_OFFSET
					       + block->frame)
			      == fil_system.sys_space->size_in_header);
			mtr.commit();
		}
#endif
		const ulint	tablespace_size_in_header
			= fil_system.sys_space->size_in_header;
		const ulint	sum_of_data_file_sizes
			= srv_sys_space.get_sum_of_sizes();
		/* Compare the system tablespace file size to what is
		stored in FSP_SIZE. In srv_sys_space.open_or_create()
		we already checked that the file sizes match the
		innodb_data_file_path specification. */
		if (srv_read_only_mode
		    || sum_of_data_file_sizes == tablespace_size_in_header) {
			/* Do not complain about the size. */
		} else if (!srv_sys_space.can_auto_extend_last_file()
			   || sum_of_data_file_sizes
			   < tablespace_size_in_header) {
			ib::error() << "Tablespace size stored in header is "
				<< tablespace_size_in_header
				<< " pages, but the sum of data file sizes is "
				<< sum_of_data_file_sizes << " pages";

			if (srv_force_recovery == 0
			    && sum_of_data_file_sizes
			    < tablespace_size_in_header) {
				ib::error() <<
					"Cannot start InnoDB. The tail of"
					" the system tablespace is"
					" missing. Have you edited"
					" innodb_data_file_path in my.cnf"
					" in an inappropriate way, removing"
					" data files from there?"
					" You can set innodb_force_recovery=1"
					" in my.cnf to force"
					" a startup if you are trying to"
					" recover a badly corrupt database.";

				return(srv_init_abort(DB_ERROR));
			}
		}

		recv_sys.debug_free();

		if (srv_operation == SRV_OPERATION_RESTORE
		    || srv_operation == SRV_OPERATION_RESTORE_EXPORT) {
			/* After applying the redo log from
			SRV_OPERATION_BACKUP, flush the changes
			to the data files and truncate or delete the log.
			Unless --export is specified, no further change to
			InnoDB files is needed. */
			ut_ad(!srv_force_recovery);
			ut_ad(recv_no_log_write);
			err = fil_write_flushed_lsn(log_get_lsn());
			DBUG_ASSERT(!buf_pool.any_io_pending());
			log_sys.log.close_file();
			if (err == DB_SUCCESS) {
				bool trunc = srv_operation
					== SRV_OPERATION_RESTORE;
				if (!trunc) {
					delete_log_file("0");
				} else {
					auto logfile0 = get_log_file_path();
					/* Truncate the first log file. */
					fclose(fopen(logfile0.c_str(), "w"));
				}
			}
			return(err);
		}

		/* Upgrade or resize or rebuild the redo logs before
		generating any dirty pages, so that the old redo log
		file will not be written to. */

		if (srv_force_recovery == SRV_FORCE_NO_LOG_REDO) {
			/* Completely ignore the redo log. */
		} else if (srv_read_only_mode) {
			/* Leave the redo log alone. */
		} else if (srv_log_file_size_requested == srv_log_file_size
			   && srv_log_file_found
			   && log_sys.log.format
			   == (srv_encrypt_log
			       ? log_t::FORMAT_ENC_10_5
			       : log_t::FORMAT_10_5)
			   && log_sys.log.subformat == 2) {
			/* No need to add or remove encryption,
			upgrade, downgrade, or resize. */
		} else {
			/* Prepare to delete the old redo log file */
			flushed_lsn = srv_prepare_to_delete_redo_log_file(
				srv_log_file_found);

			DBUG_EXECUTE_IF("innodb_log_abort_1",
					return(srv_init_abort(DB_ERROR)););
			/* Prohibit redo log writes from any other
			threads until creating a log checkpoint at the
			end of create_log_file(). */
			ut_d(recv_no_log_write = true);
			DBUG_ASSERT(!buf_pool.any_io_pending());

			DBUG_EXECUTE_IF("innodb_log_abort_3",
					return(srv_init_abort(DB_ERROR)););
			DBUG_PRINT("ib_log", ("After innodb_log_abort_3"));

			/* Stamp the LSN to the data files. */
			err = fil_write_flushed_lsn(flushed_lsn);

			DBUG_EXECUTE_IF("innodb_log_abort_4", err = DB_ERROR;);
			DBUG_PRINT("ib_log", ("After innodb_log_abort_4"));

			if (err != DB_SUCCESS) {
				return(srv_init_abort(err));
			}

			/* Close the redo log file, so that we can replace it */
			log_sys.log.close_file();

			DBUG_EXECUTE_IF("innodb_log_abort_5",
					return(srv_init_abort(DB_ERROR)););
			DBUG_PRINT("ib_log", ("After innodb_log_abort_5"));

			ib::info()
				<< "Starting to delete and rewrite log file.";

			srv_log_file_size = srv_log_file_size_requested;

			err = create_log_file(false, flushed_lsn, logfile0);

			if (err == DB_SUCCESS) {
				err = create_log_file_rename(flushed_lsn,
							     logfile0);
			}

			if (err != DB_SUCCESS) {
				return(srv_init_abort(err));
			}
		}
	}

	ut_ad(err == DB_SUCCESS);
	ut_a(sum_of_new_sizes != ULINT_UNDEFINED);

	/* Create the doublewrite buffer to a new tablespace */
	if (!srv_read_only_mode && srv_force_recovery < SRV_FORCE_NO_TRX_UNDO
	    && !buf_dblwr.create()) {
		return(srv_init_abort(DB_ERROR));
	}

	/* Here the double write buffer has already been created and so
	any new rollback segments will be allocated after the double
	write buffer. The default segment should already exist.
	We create the new segments only if it's a new database or
	the database was shutdown cleanly. */

	/* Note: When creating the extra rollback segments during an upgrade
	we violate the latching order, even if the change buffer is empty.
	We make an exception in sync0sync.cc and check srv_is_being_started
	for that violation. It cannot create a deadlock because we are still
	running in single threaded mode essentially. Only the IO threads
	should be running at this stage. */

	if (!trx_sys_create_rsegs()) {
		return(srv_init_abort(DB_ERROR));
	}

	if (!create_new_db) {
		ut_ad(high_level_read_only
		      || srv_force_recovery <= SRV_FORCE_NO_IBUF_MERGE);

		/* Validate a few system page types that were left
		uninitialized before MySQL or MariaDB 5.5. */
		if (!high_level_read_only
		    && !fil_system.sys_space->full_crc32()) {
			buf_block_t*	block;
			mtr.start();
			/* Bitmap page types will be reset in
			buf_dblwr_check_block() without redo logging. */
			block = buf_page_get(
				page_id_t(IBUF_SPACE_ID,
					  FSP_IBUF_HEADER_PAGE_NO),
				0, RW_X_LATCH, &mtr);
			fil_block_check_type(*block, FIL_PAGE_TYPE_SYS, &mtr);
			/* Already MySQL 3.23.53 initialized
			FSP_IBUF_TREE_ROOT_PAGE_NO to
			FIL_PAGE_INDEX. No need to reset that one. */
			block = buf_page_get(
				page_id_t(TRX_SYS_SPACE, TRX_SYS_PAGE_NO),
				0, RW_X_LATCH, &mtr);
			fil_block_check_type(*block, FIL_PAGE_TYPE_TRX_SYS,
					     &mtr);
			block = buf_page_get(
				page_id_t(TRX_SYS_SPACE,
					  FSP_FIRST_RSEG_PAGE_NO),
				0, RW_X_LATCH, &mtr);
			fil_block_check_type(*block, FIL_PAGE_TYPE_SYS, &mtr);
			block = buf_page_get(
				page_id_t(TRX_SYS_SPACE, FSP_DICT_HDR_PAGE_NO),
				0, RW_X_LATCH, &mtr);
			fil_block_check_type(*block, FIL_PAGE_TYPE_SYS, &mtr);
			mtr.commit();
		}

		/* Roll back any recovered data dictionary
		transactions, so that the data dictionary tables will
		be free of any locks.  The data dictionary latch
		should guarantee that there is at most one data
		dictionary transaction active at a time. */
		if (!high_level_read_only
		    && srv_force_recovery < SRV_FORCE_NO_TRX_UNDO) {
			/* If the following call is ever removed, the
			first-time ha_innobase::open() must hold (or
			acquire and release) a table lock that
			conflicts with trx_resurrect_table_locks(), to
			ensure that any recovered incomplete ALTER
			TABLE will have been rolled back. Otherwise,
			dict_table_t::instant could be cleared by
			rollback invoking
			dict_index_t::clear_instant_alter() while open
			table handles exist in client connections. */
			trx_rollback_recovered(false);
		}

		/* FIXME: Skip the following if srv_read_only_mode,
		while avoiding "Allocated tablespace ID" warnings. */
		if (srv_force_recovery <= SRV_FORCE_NO_IBUF_MERGE) {
			/* Open or Create SYS_TABLESPACES and SYS_DATAFILES
			so that tablespace names and other metadata can be
			found. */
			err = dict_create_or_check_sys_tablespace();
			if (err != DB_SUCCESS) {
				return(srv_init_abort(err));
			}

			/* The following call is necessary for the insert
			buffer to work with multiple tablespaces. We must
			know the mapping between space id's and .ibd file
			names.

			In a crash recovery, we check that the info in data
			dictionary is consistent with what we already know
			about space id's from the calls to fil_ibd_load().

			In a normal startup, we create the space objects for
			every table in the InnoDB data dictionary that has
			an .ibd file.

			We also determine the maximum tablespace id used. */
			dict_check_tablespaces_and_store_max_id();
		}

		if (srv_force_recovery < SRV_FORCE_NO_TRX_UNDO
		    && !srv_read_only_mode) {
			/* Drop partially created indexes. */
			row_merge_drop_temp_indexes();
			/* Drop garbage tables. */
			row_mysql_drop_garbage_tables();

			/* Drop any auxiliary tables that were not
			dropped when the parent table was
			dropped. This can happen if the parent table
			was dropped but the server crashed before the
			auxiliary tables were dropped. */
			fts_drop_orphaned_tables();

			/* Rollback incomplete non-DDL transactions */
			trx_rollback_is_active = true;
			os_thread_create(trx_rollback_all_recovered);
		}
	}

	srv_startup_is_before_trx_rollback_phase = false;

	if (!srv_read_only_mode) {
		/* timer task which watches the timeouts
		for lock waits */
		lock_sys.timeout_timer.reset(srv_thread_pool->create_timer(
			lock_wait_timeout_task));

		DBUG_EXECUTE_IF("innodb_skip_monitors", goto skip_monitors;);
		/* Create the task which warns of long semaphore waits */
		srv_start_periodic_timer(srv_error_monitor_timer, srv_error_monitor_task, 1000);
		srv_start_periodic_timer(srv_monitor_timer, srv_monitor_task, 5000);

#ifndef DBUG_OFF
skip_monitors:
#endif
		ut_ad(srv_force_recovery >= SRV_FORCE_NO_UNDO_LOG_SCAN
		      || !purge_sys.enabled());

		if (srv_force_recovery < SRV_FORCE_NO_BACKGROUND) {
			srv_undo_sources = true;
			/* Create the dict stats gathering task */
			dict_stats_start();
			/* Create the thread that will optimize the
			FULLTEXT search index subsystem. */
			fts_optimize_init();
		}
	}

	/* Create the SYS_FOREIGN and SYS_FOREIGN_COLS system tables */
	err = dict_create_or_check_foreign_constraint_tables();
	if (err == DB_SUCCESS) {
		err = dict_create_or_check_sys_tablespace();
		if (err == DB_SUCCESS) {
			err = dict_create_or_check_sys_virtual();
		}
	}
	switch (err) {
	case DB_SUCCESS:
		break;
	case DB_READ_ONLY:
		if (srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO) {
			break;
		}
		ib::error() << "Cannot create system tables in read-only mode";
		/* fall through */
	default:
		return(srv_init_abort(err));
	}

	if (!srv_read_only_mode && srv_operation == SRV_OPERATION_NORMAL) {
		/* Initialize the innodb_temporary tablespace and keep
		it open until shutdown. */
		err = srv_open_tmp_tablespace(create_new_db);

		if (err != DB_SUCCESS) {
			return(srv_init_abort(err));
		}

		trx_temp_rseg_create();

		if (srv_force_recovery < SRV_FORCE_NO_BACKGROUND) {
			srv_start_periodic_timer(srv_master_timer, srv_master_callback, 1000);
		}
	}

	if (!srv_read_only_mode && srv_operation == SRV_OPERATION_NORMAL
	    && srv_force_recovery < SRV_FORCE_NO_BACKGROUND) {
		srv_init_purge_tasks();
		purge_sys.coordinator_startup();
		srv_wake_purge_thread_if_not_active();
	}

	srv_is_being_started = false;

	if (srv_print_verbose_log) {
		ib::info() << INNODB_VERSION_STR
			   << " started; log sequence number "
			   << recv_sys.recovered_lsn
			   << "; transaction id " << trx_sys.get_max_trx_id();
	}

	if (srv_force_recovery > 0) {
		ib::info() << "!!! innodb_force_recovery is set to "
			<< srv_force_recovery << " !!!";
	}

	if (srv_force_recovery == 0) {
		/* In the change buffer we may have even bigger tablespace
		id's, because we may have dropped those tablespaces, but
		the buffered records have not been cleaned yet. */
		ibuf_update_max_tablespace_id();
	}

	if (!srv_read_only_mode) {
		if (create_new_db) {
			srv_buffer_pool_load_at_startup = FALSE;
		}

#ifdef WITH_WSREP
		/*
		  Create the dump/load thread only when not running with
		  --wsrep-recover.
		*/
		if (!get_wsrep_recovery()) {
#endif /* WITH_WSREP */

		/* Start buffer pool dump/load task */
		buf_load_at_startup();

#ifdef WITH_WSREP
		} else {
			ib::warn() <<
				"Skipping buffer pool dump/restore during "
				"wsrep recovery.";
		}
#endif /* WITH_WSREP */

		/* Create thread(s) that handles key rotation. This is
		needed already here as log_preflush_pool_modified_pages
		will flush dirty pages and that might need e.g.
		fil_crypt_threads_event. */
		fil_system_enter();
		fil_crypt_threads_init();
		fil_system_exit();

		/* Initialize online defragmentation. */
		btr_defragment_init();

		srv_started_redo = true;
	}

	return(DB_SUCCESS);
}

/** Shut down background threads that can generate undo log. */
void srv_shutdown_bg_undo_sources()
{
	srv_shutdown_state = SRV_SHUTDOWN_INITIATED;

	if (srv_undo_sources) {
		ut_ad(!srv_read_only_mode);
		fts_optimize_shutdown();
		dict_stats_shutdown();
		while (row_get_background_drop_list_len_low()) {
			srv_inc_activity_count();
			os_thread_yield();
		}
		srv_undo_sources = false;
	}
}

/**
  Shutdown purge to make sure that there is no possibility that we call any
  plugin code (e.g., audit) inside virtual column computation.
*/
void innodb_preshutdown()
{
  static bool first_time= true;
  if (!first_time)
    return;
  first_time= false;

  if (srv_read_only_mode)
    return;
  if (!srv_fast_shutdown && srv_operation == SRV_OPERATION_NORMAL)
  {
    /* Because a slow shutdown must empty the change buffer, we had
    better prevent any further changes from being buffered. */
    innodb_change_buffering= 0;

    if (trx_sys.is_initialised())
      while (trx_sys.any_active_transactions())
        os_thread_sleep(1000);
  }
  srv_shutdown_bg_undo_sources();
  srv_purge_shutdown();

  if (srv_n_fil_crypt_threads)
    fil_crypt_set_thread_cnt(0);
}


/** Shut down InnoDB. */
void innodb_shutdown()
{
	innodb_preshutdown();
	ut_ad(!srv_undo_sources);
	switch (srv_operation) {
	case SRV_OPERATION_BACKUP:
	case SRV_OPERATION_RESTORE_DELTA:
		break;
	case SRV_OPERATION_RESTORE:
	case SRV_OPERATION_RESTORE_EXPORT:
		srv_shutdown_state = SRV_SHUTDOWN_CLEANUP;
		if (!buf_page_cleaner_is_active) {
			break;
		}
		mysql_mutex_lock(&buf_pool.flush_list_mutex);
		while (buf_page_cleaner_is_active) {
			mysql_cond_signal(&buf_pool.do_flush_list);
			mysql_cond_wait(&buf_pool.done_flush_list,
					&buf_pool.flush_list_mutex);
		}
		mysql_mutex_unlock(&buf_pool.flush_list_mutex);
		break;
	case SRV_OPERATION_NORMAL:
		/* Shut down the persistent files. */
		logs_empty_and_mark_files_at_shutdown();
	}

	os_aio_free();
	fil_space_t::close_all();
	/* Exit any remaining threads. */
	ut_ad(!buf_page_cleaner_is_active);
	srv_shutdown_threads();

	if (srv_monitor_file) {
		my_fclose(srv_monitor_file, MYF(MY_WME));
		srv_monitor_file = 0;
		if (srv_monitor_file_name) {
			unlink(srv_monitor_file_name);
			ut_free(srv_monitor_file_name);
		}
	}

	if (srv_misc_tmpfile) {
		my_fclose(srv_misc_tmpfile, MYF(MY_WME));
		srv_misc_tmpfile = 0;
	}

	ut_ad(dict_sys.is_initialised() || !srv_was_started);
	ut_ad(trx_sys.is_initialised() || !srv_was_started);
	ut_ad(buf_dblwr.is_initialised() || !srv_was_started
	      || srv_read_only_mode
	      || srv_force_recovery >= SRV_FORCE_NO_TRX_UNDO);
	ut_ad(lock_sys.is_initialised() || !srv_was_started);
	ut_ad(log_sys.is_initialised() || !srv_was_started);
	ut_ad(ibuf.index || !srv_was_started);

	dict_stats_deinit();

	if (srv_started_redo) {
		ut_ad(!srv_read_only_mode);
		/* srv_shutdown_bg_undo_sources() already invoked
		fts_optimize_shutdown(); dict_stats_shutdown(); */

		fil_crypt_threads_cleanup();
		btr_defragment_shutdown();
	}

	/* This must be disabled before closing the buffer pool
	and closing the data dictionary.  */

#ifdef BTR_CUR_HASH_ADAPT
	if (dict_sys.is_initialised()) {
		btr_search_disable();
	}
#endif /* BTR_CUR_HASH_ADAPT */
	ibuf_close();
	log_sys.close();
	purge_sys.close();
	trx_sys.close();
	buf_dblwr.close();
	lock_sys.close();
	trx_pool_close();

	if (!srv_read_only_mode) {
		mutex_free(&srv_monitor_file_mutex);
		mutex_free(&srv_misc_tmpfile_mutex);
	}

	dict_sys.close();
	btr_search_sys_free();
	row_mysql_close();
	srv_free();
	fil_system.close();
	pars_lexer_close();
	recv_sys.close();

	ut_ad(buf_pool.is_initialised() || !srv_was_started);
	buf_pool.close();
	sync_check_close();

	if (srv_was_started && srv_print_verbose_log) {
		ib::info() << "Shutdown completed; log sequence number "
			   << srv_shutdown_lsn
			   << "; transaction id " << trx_sys.get_max_trx_id();
	}
	srv_thread_pool_end();
	srv_started_redo = false;
	srv_was_started = false;
	srv_start_has_been_called = false;
}

/** Get the meta-data filename from the table name for a
single-table tablespace.
@param[in]	table		table object
@param[out]	filename	filename
@param[in]	max_len		filename max length */
void
srv_get_meta_data_filename(
	dict_table_t*	table,
	char*		filename,
	ulint		max_len)
{
	ulint		len;
	char*		path;

	/* Make sure the data_dir_path is set. */
	dict_get_and_save_data_dir_path(table, false);

	if (DICT_TF_HAS_DATA_DIR(table->flags)) {
		ut_a(table->data_dir_path);

		path = fil_make_filepath(
			table->data_dir_path, table->name.m_name, CFG, true);
	} else {
		path = fil_make_filepath(NULL, table->name.m_name, CFG, false);
	}

	ut_a(path);
	len = strlen(path);
	ut_a(max_len >= len);

	strcpy(filename, path);

	ut_free(path);
}
