if (!success) {
m_last_os_error = os_file_get_last_error(true);
ib::error() << "Cannot open datafile '" << m_filepath << "'"; return(DB_CANNOT_OPEN_FILE);
}
return(DB_SUCCESS);
}
/** Open a data file in read-only mode to check if it exists so that it canbevalidated. @param[in]strictwhethertoissueerrormessages
@return DB_SUCCESS or error code */
dberr_t
Datafile::open_read_only(bool strict)
{ bool success = false;
ut_ad(m_handle == OS_FILE_CLOSED);
/* This function can be called for file objects that do not need
to be opened, which is the case when the m_filepath is NULL */ if (m_filepath == NULL) { return(DB_ERROR);
}
if (strict) {
m_last_os_error = os_file_get_last_error(true);
ib::error() << "Cannot open datafile for read-only: '"
<< m_filepath << "' OS error: " << m_last_os_error;
}
return(DB_CANNOT_OPEN_FILE);
}
/** Open a data file in read-write mode during start-up so that doublewritepagescanberestoredandthenitcanbevalidated.*
@return DB_SUCCESS or error code */ inline dberr_t Datafile::open_read_write()
{ bool success = false;
ut_ad(m_handle == OS_FILE_CLOSED);
ut_ad(!srv_read_only_mode);
/* This function can be called for file objects that do not need
to be opened, which is the case when the m_filepath is NULL */ if (m_filepath == NULL) { return(DB_ERROR);
}
/** Close a data file.
@return DB_SUCCESS or error code */
dberr_t
Datafile::close()
{ if (m_handle != OS_FILE_CLOSED) {
ibool success = os_file_close(m_handle);
ut_a(success);
m_handle = OS_FILE_CLOSED;
}
return(DB_SUCCESS);
}
/** Make a full filepath from a directory path and a filename. Prependthedirpathtofilenameusingtheextensiongiven. IfdirpathisNULL,prependthedefaultdatadirtofilepath. Storetheresultinm_filepath. @paramdirpathdirectorypath @paramnametablespace(table)name
@param ext filename extension */ void Datafile::make_filepath(constchar *dirpath, fil_space_t::name_type name,
ib_extention ext)
{
ut_ad(dirpath || name.size());
free_filepath();
m_filepath= fil_make_filepath(dirpath, name, ext, false);
ut_ad(m_filepath);
set_filename();
}
/** Set the filepath by duplicating the filepath sent in. This is the nameofthefilewithitsextensionandabsoluteorrelativepath.
@param[in] filepath filepath to set */ void
Datafile::set_filepath(constchar* filepath)
{
free_filepath();
m_filepath = static_cast<char*>(ut_malloc_nokey(strlen(filepath) + 1));
::strcpy(m_filepath, filepath);
set_filename();
}
/** Do a quick test if the filepath provided looks the same as this filepath bytebybyte.Iftheyaretwodifferentlookingpathstothesamefile, same_as()willbeusedtoshowthatafterthefilesareopened. @param[in]otherfilepathtocomparewith @retvaltrueifitisthesamefilenamebybytecomparison
@retval false if it looks different */ bool
Datafile::same_filepath_as( constchar* other) const
{ return(0 == strcmp(m_filepath, other));
}
/** Test if another opened datafile is the same file as this object. @param[in]otherDatafiletocomparewith
@return true if it is the same file, else false */ bool
Datafile::same_as( const Datafile& other) const
{ #ifdef _WIN32 return(m_file_info.dwVolumeSerialNumber
== other.m_file_info.dwVolumeSerialNumber
&& m_file_info.nFileIndexHigh
== other.m_file_info.nFileIndexHigh
&& m_file_info.nFileIndexLow
== other.m_file_info.nFileIndexLow); #else return(m_file_info.st_ino == other.m_file_info.st_ino
&& m_file_info.st_dev == other.m_file_info.st_dev); #endif/* WIN32 */
}
if (memcmp_aligned<2>(FIL_PAGE_SPACE_ID + page,
FSP_HEADER_OFFSET + FSP_SPACE_ID + page, 4))
{
sql_print_error("InnoDB: Inconsistent tablespace ID in %s", m_filepath); return DB_CORRUPTION;
}
m_space_id= mach_read_from_4(FIL_PAGE_SPACE_ID + page);
m_flags= fsp_header_get_flags(page); if (!fil_space_t::is_valid_flags(m_flags, m_space_id))
{
uint32_t cflags= fsp_flags_convert_from_101(m_flags); if (cflags == UINT32_MAX) switch (fsp_flags_is_incompatible_mysql(m_flags)) { case0:
sql_print_error("InnoDB: Invalid flags 0x%" PRIx32 " in %s",
m_flags, m_filepath); return DB_CORRUPTION; case3: case2:
sql_print_error("InnoDB: MySQL-8.0 tablespace in %s", m_filepath); goto unsupported; case1:
sql_print_error("InnoDB: MySQL Encrypted tablespace in %s",
m_filepath);
unsupported:
sql_print_error("InnoDB: Restart in MySQL for migration/recovery."); return DB_UNSUPPORTED;
} else
m_flags= cflags;
}
return DB_SUCCESS;
}
/** Reads a few significant fields from the first page of the first datafile.TheDatafilemustalreadybeopen. @param[in]read_only_modeIftrue,thenreadonlymodechecksareenforced.
@return DB_SUCCESS or DB_IO_ERROR if page cannot be read */
dberr_t
Datafile::read_first_page(bool read_only_mode)
{ if (m_handle == OS_FILE_CLOSED) {
dberr_t err = open_or_create(read_only_mode);
if (err != DB_SUCCESS) { return(err);
}
}
/* Align the memory for a possible read from a raw device */
if (err == DB_SUCCESS
&& fil_space_t::physical_size(m_flags) > page_size) {
ib::error() << "File " << m_filepath
<< " should be longer than "
<< page_size << " bytes";
err = DB_CORRUPTION;
}
return(err);
}
/** Free the first page from memory when it is no longer needed. */ void Datafile::free_first_page()
{
aligned_free(m_first_page);
m_first_page= nullptr;
}
/** Validates the datafile and checks that it conforms with the expected spaceIDandflags.Thefileshouldexistandbesuccessfullyopened inorderforthisfunctiontovalidateit. @param[in]space_idTheexpectedtablespaceID. @param[in]flagsTheexpectedtablespaceflags. @retvalDB_SUCCESSiftablespaceisvalid,DB_ERRORifnot.
m_is_valid is also set true on success, else false. */
dberr_t Datafile::validate_to_dd(uint32_t space_id, uint32_t flags)
{
dberr_t err;
if (!is_open()) { return DB_ERROR;
}
/* Validate this single-table-tablespace with the data dictionary, butdonotcomparetheDATA_DIRflag,incasethetablespacewas
remotely located. */
err = validate_first_page(m_first_page); if (err != DB_SUCCESS) { return(err);
}
flags &= ~FSP_FLAGS_MEM_MASK;
/* Make sure the datafile we found matched the space ID. Ifthedatafileisafile-per-tabletablespacethenalsomatch
the row format and zip page size. */ if (m_space_id == space_id
&& (fil_space_t::is_flags_equal(flags, m_flags)
|| fil_space_t::is_flags_equal(m_flags, flags))) { /* Datafile matches the tablespace expected. */ return(DB_SUCCESS);
}
/* else do not use this tablespace. */
m_is_valid = false;
if (!page)
{ /* If the first page of the given user tablespace is not there inthedoublewritebuffer,thentherecoveryisgoingtofail
now. Report error only when doublewrite buffer is not empty */
sql_print_error("InnoDB: Corrupted page " "[page id: space=" UINT32PF ", page number=0]" " of datafile '%s' could not be found" " in the doublewrite buffer", space_id, name); break;
}
return space_id;
}
} return0;
}
/** Validates this datafile for the purpose of recovery. The file should existandbesuccessfullyopened.Weinitiallyopenitinread-onlymode becausewejustwanttoreadtheSpaceID.However,ifthefirstpageis corruptandneedstoberestoredfromthedoublewritebuffer,wewill reopenitinwritemodeandrytorestorethatpage. @retvalDB_SUCCESSiftablespaceisvalid,DB_ERRORifnot.
m_is_valid is also set true on success, else false. */
dberr_t
Datafile::validate_for_recovery()
{
dberr_t err;
ut_ad(is_open());
ut_ad(!srv_read_only_mode);
err = validate_first_page(m_first_page);
switch (err) { case DB_TABLESPACE_EXISTS: break; case DB_SUCCESS: if (!m_defer || !m_space_id) { break;
} /* InnoDB should check whether the deferred tablespacepage0canberecoveredfrom doublewritebuffer.InnoDBshouldtry torecoveronlyifm_space_idexistsbecause dblwrpagescanbesearchedvia{space_id,0}.
m_space_id is set in read_first_page(). */ /* fall through */ default: const page_t *first_page = nullptr;
if (!m_space_id) {
m_space_id = recv_sys.dblwr.find_first_page(
m_filepath, m_handle); if (m_space_id) {
m_defer= false; goto free_first_page;
} elsereturn err;
}
if (!m_defer) {
err = find_space_id(); if (err != DB_SUCCESS || m_space_id == 0) {
sql_print_error( "InnoDB: Datafile '%s' is corrupted." " Cannot determine the space ID from" " the first 64 pages.", m_filepath); return(err);
}
}
if (!first_page) { return m_defer ? err : DB_CORRUPTION;
}
free_first_page: /* Free the previously read first page and then re-validate. */
free_first_page();
m_defer = false;
err = validate_first_page(first_page);
}
return(err);
}
/** Check the consistency of the first page of a datafile when the tablespaceisopened.Thisoccursbeforethefil_space_tiscreated sotheSpaceIDfoundheremustnotalreadybeopen. m_is_validissettrueonsuccess,elsefalse. @param[in]first_pagethecontentsofthefirstpage @retvalDB_SUCCESSonifthedatafileisvalid @retvalDB_CORRUPTIONifthedatafileisnotreadable
@retval DB_TABLESPACE_EXISTS if there is a duplicate space_id */
dberr_t Datafile::validate_first_page(const page_t *first_page) noexcept
{ constchar* error_txt = NULL;
/* Check if the whole page is blank. */ if (!m_space_id && !m_flags) { const byte* b = first_page;
ulint nonzero_bytes = srv_page_size;
while (*b == '\0' && --nonzero_bytes != 0) {
b++;
}
if (nonzero_bytes == 0) {
error_txt = "Header page consists of zero bytes"; goto err_exit;
}
}
if (!fil_space_t::is_valid_flags(m_flags, m_space_id)) { /* Tablespace flags must be valid. */
error_txt = "Tablespace flags are invalid"; goto err_exit;
}
if (srv_page_size != logical_size) {
free_first_page(); if (recv_recovery_is_on()
|| srv_operation == SRV_OPERATION_BACKUP) {
m_defer= true; return DB_SUCCESS;
} /* Logical size must be innodb_page_size. */
ib::error()
<< "Data file '" << m_filepath << "' uses page size "
<< logical_size << ", but the innodb_page_size" " start-up parameter is "
<< srv_page_size; return(DB_ERROR);
}
if (page_get_page_no(first_page) != 0) { /* First page must be number 0 */
error_txt = "Header page contains inconsistent data"; goto err_exit;
}
if (m_space_id >= SRV_SPACE_ID_UPPER_BOUND) {
error_txt = "A bad Space ID was found"; goto err_exit;
}
switch (buf_page_is_corrupted(false, first_page, m_flags)) { case NOT_CORRUPTED: break; case CORRUPTED_FUTURE_LSN:
error_txt = "LSN is in the future"; goto err_exit; case CORRUPTED_OTHER:
error_txt = "Checksum mismatch"; goto err_exit;
}
mysql_mutex_lock(&fil_system.mutex);
fil_space_t* space = fil_space_get_by_id(m_space_id);
if (space) {
fil_node_t* node = UT_LIST_GET_FIRST(space->chain);
/* Make sure the space_id has not already been opened. */
ib::error() << "Attempted to open a previously opened" " tablespace. Previous tablespace: "
<< (node ? node->name : "(unknown)")
<< " uses space ID: " << m_space_id
<< ". Cannot open filepath: " << m_filepath
<< " which uses the same space ID.";
}
/** Determine the space id of the given file descriptor by reading a few pagesfromthebeginningofthe.ibdfile.
@return DB_SUCCESS if space id was successfully identified, else DB_ERROR. */
dberr_t
Datafile::find_space_id()
{
os_offset_t file_size;
ut_ad(m_handle != OS_FILE_CLOSED);
file_size = os_file_get_size(m_handle);
if (!file_size) { return DB_SUCCESS;
}
if (file_size == (os_offset_t) -1) {
ib::error() << "Could not get file size of datafile '"
<< m_filepath << "'"; return(DB_CORRUPTION);
}
/* Assuming a page size, read the space_id from each page and store it inamap.Findoutwhichspace_idisagreedonbymajorityofthe
pages. Choose that space_id. */ for (ulint page_size = UNIV_ZIP_SIZE_MIN;
page_size <= UNIV_PAGE_SIZE_MAX;
page_size <<= 1) { /* map[space_id] = count of pages */ typedef std::map<
uint32_t,
uint32_t,
std::less<uint32_t>,
ut_allocator<std::pair<const uint32_t, uint32_t> > >
Pages;
/* For noncompressed pages, the page size must be
equal to srv_page_size. */ if (page_size == srv_page_size
&& !fil_space_t::zip_size(fsp_flags)) {
noncompressed_ok = !buf_page_is_corrupted( false, page, fsp_flags);
}
if (size_t len= strlen(filepath))
{ /* Trim whitespace from end of filepath */
len--; while (static_cast<byte>(filepath[len]) <= 0x20)
{ if (!len) return nullptr;
filepath[len--]= 0;
} /* Ensure that the last 2 path separators are forward slashes, becauseelsewhereweareassumingthattablespacefilenamesend
in "/databasename/tablename.ibd". */ unsigned trailing_slashes= 0; for (; len; len--)
{ switch (filepath[len]) { #ifdef _WIN32 case'\\':
filepath[len]= '/'; /* fall through */ #endif case'/': if (++trailing_slashes >= 2) return filepath;
}
}
}
}
return nullptr;
}
/** Create a link filename, openthatfile,andreadthecontentsintom_filepath. @paramnametablename @returnfilepath()
@retval nullptr if the .isl file does not exist or cannot be read */ constchar *RemoteDatafile::open_link_file(const fil_space_t::name_type name)
{ if (!m_link_filepath)
m_link_filepath= fil_make_filepath(nullptr, name, ISL, false);
m_filepath= read_link_file(m_link_filepath); return m_filepath;
}
/** Release the resources. */ void
RemoteDatafile::shutdown()
{
Datafile::shutdown();
prev_filepath = read_link_file(link_filepath); if (prev_filepath) { /* Truncate (starting with MySQL 5.6, probably no longersinceMariaDBServer10.2.19)usedtocallthis
with an existing link file which contains the same filepath. */ bool same = !strncmp(prev_filepath, name.data(), name.size())
&& !strcmp(prev_filepath + name.size(), DOT_IBD);
ut_free(prev_filepath); if (same) {
ut_free(link_filepath); return(DB_SUCCESS);
}
}
/** Check if the file already exists. */
FILE* file = NULL; bool exists;
os_file_type_t ftype;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.