summaryrefslogtreecommitdiffstats
path: root/storage/maria/ma_control_file.c
diff options
context:
space:
mode:
Diffstat (limited to 'storage/maria/ma_control_file.c')
-rw-r--r--storage/maria/ma_control_file.c738
1 files changed, 738 insertions, 0 deletions
diff --git a/storage/maria/ma_control_file.c b/storage/maria/ma_control_file.c
new file mode 100644
index 00000000..21befb70
--- /dev/null
+++ b/storage/maria/ma_control_file.c
@@ -0,0 +1,738 @@
+/* Copyright (C) 2007 MySQL AB & Guilhem Bichot & Michael Widenius
+
+ 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 */
+
+/*
+ WL#3234 Maria control file
+ First version written by Guilhem Bichot on 2006-04-27.
+*/
+
+#ifndef EXTRACT_DEFINITIONS
+#include "maria_def.h"
+#include "ma_checkpoint.h"
+#endif
+
+/*
+ A control file contains the following objects:
+
+Start of create time variables (at start of file):
+ - Magic string (including version number of Maria control file)
+ - Uuid
+ - Size of create time part
+ - Size of dynamic part
+ - Maria block size
+..... Here we can add new variables without changing format
+ - Checksum of create time part (last of block)
+
+Start of changeable part:
+ - Checksum of changeable part
+ - LSN of last checkpoint
+ - Number of last log file
+ - Max trid in control file (since Maria 1.5 May 2008)
+ - Number of consecutive recovery failures (since Maria 1.5 May 2008)
+..... Here we can add new variables without changing format
+
+The idea is that one can add new variables to the control file and still
+use it with old program versions. If one needs to do an incompatible change
+one should increment the control file version number.
+*/
+
+/* Total size should be < sector size for atomic write operation */
+#define CF_MAX_SIZE 512
+#define CF_MIN_SIZE (CF_BLOCKSIZE_OFFSET + CF_BLOCKSIZE_SIZE + \
+ CF_CHECKSUM_SIZE * 2 + CF_LSN_SIZE + CF_FILENO_SIZE)
+
+/* Create time variables */
+#define CF_MAGIC_STRING "\xfe\xfe\xc"
+#define CF_MAGIC_STRING_OFFSET 0
+#define CF_MAGIC_STRING_SIZE (sizeof(CF_MAGIC_STRING)-1)
+#define CF_VERSION_OFFSET (CF_MAGIC_STRING_OFFSET + CF_MAGIC_STRING_SIZE)
+#define CF_VERSION_SIZE 1
+#define CF_UUID_OFFSET (CF_VERSION_OFFSET + CF_VERSION_SIZE)
+#define CF_UUID_SIZE MY_UUID_SIZE
+#define CF_CREATE_TIME_SIZE_OFFSET (CF_UUID_OFFSET + CF_UUID_SIZE)
+#define CF_SIZE_SIZE 2
+#define CF_CHANGEABLE_SIZE_OFFSET (CF_CREATE_TIME_SIZE_OFFSET + CF_SIZE_SIZE)
+#define CF_BLOCKSIZE_OFFSET (CF_CHANGEABLE_SIZE_OFFSET + CF_SIZE_SIZE)
+#define CF_BLOCKSIZE_SIZE 2
+
+#define CF_CREATE_TIME_TOTAL_SIZE (CF_BLOCKSIZE_OFFSET + CF_BLOCKSIZE_SIZE + \
+ CF_CHECKSUM_SIZE)
+
+/*
+ Start of the part that changes during execution
+ This is stored at offset uint2korr(file[CF_CHANGEABLE_SIZE])
+*/
+#define CF_CHECKSUM_OFFSET 0
+#define CF_CHECKSUM_SIZE 4
+#define CF_LSN_OFFSET (CF_CHECKSUM_OFFSET + CF_CHECKSUM_SIZE)
+#define CF_LSN_SIZE LSN_STORE_SIZE
+#define CF_FILENO_OFFSET (CF_LSN_OFFSET + CF_LSN_SIZE)
+#define CF_FILENO_SIZE 4
+#define CF_MAX_TRID_OFFSET (CF_FILENO_OFFSET + CF_FILENO_SIZE)
+#define CF_MAX_TRID_SIZE TRANSID_SIZE
+#define CF_RECOV_FAIL_OFFSET (CF_MAX_TRID_OFFSET + CF_MAX_TRID_SIZE)
+#define CF_RECOV_FAIL_SIZE 1
+#define CF_CHANGEABLE_TOTAL_SIZE (CF_RECOV_FAIL_OFFSET + CF_RECOV_FAIL_SIZE)
+
+/*
+ The following values should not be changed, except when changing version
+ number of the maria control file. These are the minimum sizes of the
+ parts the code can handle.
+*/
+
+#define CF_MIN_CREATE_TIME_TOTAL_SIZE \
+(CF_BLOCKSIZE_OFFSET + CF_BLOCKSIZE_SIZE + CF_CHECKSUM_SIZE)
+#define CF_MIN_CHANGEABLE_TOTAL_SIZE \
+(CF_FILENO_OFFSET + CF_FILENO_SIZE)
+
+#ifndef EXTRACT_DEFINITIONS
+
+/* This module owns these two vars. */
+/**
+ This LSN serves for the two-checkpoint rule, and also to find the
+ checkpoint record when doing a recovery.
+*/
+LSN last_checkpoint_lsn= LSN_IMPOSSIBLE;
+uint32 last_logno= FILENO_IMPOSSIBLE;
+/**
+ The maximum transaction id given to a transaction. It is only updated at
+ clean shutdown (in case of crash, logs have better information).
+*/
+TrID max_trid_in_control_file= 0;
+
+/**
+ Number of consecutive log or recovery failures. Reset to 0 after recovery's
+ success.
+*/
+uint8 recovery_failures= 0;
+
+/**
+ @brief If log's lock should be asserted when writing to control file.
+
+ Can be re-used by any function which needs to be thread-safe except when
+ it is called at startup.
+*/
+my_bool maria_multi_threaded= FALSE;
+/** @brief if currently doing a recovery */
+my_bool maria_in_recovery= FALSE;
+
+/**
+ Control file is less then 512 bytes (a disk sector),
+ to be as atomic as possible
+*/
+static int control_file_fd= -1;
+
+static uint cf_create_time_size;
+static uint cf_changeable_size;
+
+/**
+ @brief Create Maria control file
+*/
+
+static CONTROL_FILE_ERROR create_control_file(const char *name,
+ int open_flags)
+{
+ uint32 sum;
+ uchar buffer[CF_CREATE_TIME_TOTAL_SIZE];
+ ulong rnd1,rnd2;
+
+ DBUG_ENTER("maria_create_control_file");
+
+ if ((control_file_fd= mysql_file_create(key_file_control, name, 0,
+ open_flags, MYF(MY_SYNC_DIR | MY_WME))) < 0)
+ DBUG_RETURN(CONTROL_FILE_UNKNOWN_ERROR);
+
+ /* Reset variables, as we are creating the file */
+ cf_create_time_size= CF_CREATE_TIME_TOTAL_SIZE;
+ cf_changeable_size= CF_CHANGEABLE_TOTAL_SIZE;
+
+ /* Create unique uuid for the control file */
+ my_random_bytes((uchar *)&rnd1, sizeof (rnd1));
+ my_random_bytes((uchar *)&rnd2, sizeof (rnd2));
+ my_uuid_init(rnd1, rnd2);
+ my_uuid(maria_uuid);
+
+ /* Prepare and write the file header */
+ memcpy(buffer, CF_MAGIC_STRING, CF_MAGIC_STRING_SIZE);
+ buffer[CF_VERSION_OFFSET]= CONTROL_FILE_VERSION;
+ memcpy(buffer + CF_UUID_OFFSET, maria_uuid, CF_UUID_SIZE);
+ int2store(buffer + CF_CREATE_TIME_SIZE_OFFSET, cf_create_time_size);
+ int2store(buffer + CF_CHANGEABLE_SIZE_OFFSET, cf_changeable_size);
+
+ /* Write create time variables */
+ int2store(buffer + CF_BLOCKSIZE_OFFSET, maria_block_size);
+
+ /* Store checksum for create time parts */
+ sum= (uint32) my_checksum(0, buffer, cf_create_time_size -
+ CF_CHECKSUM_SIZE);
+ int4store(buffer + cf_create_time_size - CF_CHECKSUM_SIZE, sum);
+
+ if (my_pwrite(control_file_fd, buffer, cf_create_time_size,
+ 0, MYF(MY_FNABP | MY_WME)))
+ DBUG_RETURN(CONTROL_FILE_UNKNOWN_ERROR);
+
+ /*
+ To be safer we should make sure that there are no logs or data/index
+ files around (indeed it could be that the control file alone was deleted
+ or not restored, and we should not go on with life at this point).
+
+ Things should still be relatively safe as if someone tries to use
+ an old table with a new control file the different uuid:s between
+ the files will cause ma_open() to generate an HA_ERR_OLD_FILE
+ error. When used from mysqld this will cause the table to be open
+ in repair mode which will remove all dependencies between the
+ table and the old control file.
+
+ We could have a tool which can rebuild the control file, by reading the
+ directory of logs, finding the newest log, reading it to find last
+ checkpoint... Slow but can save your db. For this to be possible, we
+ must always write to the control file right after writing the checkpoint
+ log record, and do nothing in between (i.e. the checkpoint must be
+ usable as soon as it has been written to the log).
+ */
+
+ /* init the file with these "undefined" values */
+ DBUG_RETURN(ma_control_file_write_and_force(LSN_IMPOSSIBLE,
+ FILENO_IMPOSSIBLE, 0, 0));
+}
+
+
+/**
+ Locks control file exclusively. This is kept for the duration of the engine
+ process, to prevent another Maria instance to write to our logs or control
+ file.
+*/
+
+static int lock_control_file(const char *name, my_bool do_retry)
+{
+ /*
+ On Windows, my_lock() uses locking() which is mandatory locking and so
+ prevents maria-recovery.test from copying the control file. And in case of
+ crash, it may take a while for Windows to unlock file, causing downtime.
+ */
+ /**
+ @todo BUG We should explore my_sopen(_SH_DENYWRD) to open or create the
+ file under Windows.
+ */
+#ifndef _WIN32
+ uint retry= 0;
+ uint retry_count= do_retry ? MARIA_MAX_CONTROL_FILE_LOCK_RETRY : 0;
+
+ /*
+ We can't here use the automatic wait in my_lock() as the alarm thread
+ may not yet exists.
+ */
+ while (my_lock(control_file_fd, F_WRLCK, 0L, F_TO_EOF,
+ MYF(MY_SEEK_NOT_DONE | MY_FORCE_LOCK | MY_NO_WAIT)))
+ {
+ if (retry == 0)
+ my_printf_error(HA_ERR_INITIALIZATION,
+ "Can't lock aria control file '%s' for exclusive use, "
+ "error: %d. Will retry for %d seconds", 0,
+ name, my_errno, retry_count);
+ if (++retry > retry_count)
+ return 1;
+ sleep(1);
+ }
+#endif
+ return 0;
+}
+
+
+/*
+ @brief Initialize control file subsystem
+
+ Looks for the control file. If none and creation is requested, creates file.
+ If present, reads it to find out last checkpoint's LSN and last log, updates
+ the last_checkpoint_lsn and last_logno global variables.
+ Called at engine's start.
+
+ @note
+ The format of the control file is defined in the comments and defines
+ at the start of this file.
+
+ @param create_if_missing create file if not found
+
+ @return Operation status
+ @retval 0 OK
+ @retval 1 Error (in which case the file is left closed)
+*/
+
+CONTROL_FILE_ERROR ma_control_file_open(my_bool create_if_missing,
+ my_bool print_error,
+ my_bool wait_for_lock)
+{
+ uchar buffer[CF_MAX_SIZE];
+ char name[FN_REFLEN], errmsg_buff[256];
+ const char *errmsg, *lock_failed_errmsg= "Could not get an exclusive lock;"
+ " file is probably in use by another process";
+ uint new_cf_create_time_size, new_cf_changeable_size, new_block_size;
+ my_off_t file_size;
+ int open_flags= O_BINARY | /*O_DIRECT |*/ O_RDWR | O_CLOEXEC;
+ int error= CONTROL_FILE_UNKNOWN_ERROR;
+ DBUG_ENTER("ma_control_file_open");
+
+ /*
+ If you change sizes in the #defines, you at least have to change the
+ "*store" and "*korr" calls in this file, and can even create backward
+ compatibility problems. Beware!
+ */
+ DBUG_ASSERT(CF_LSN_SIZE == (3+4));
+ DBUG_ASSERT(CF_FILENO_SIZE == 4);
+
+ if (control_file_fd >= 0) /* already open */
+ DBUG_RETURN(0);
+
+ if (fn_format(name, CONTROL_FILE_BASE_NAME,
+ maria_data_root, "", MYF(MY_WME)) == NullS)
+ DBUG_RETURN(CONTROL_FILE_UNKNOWN_ERROR);
+
+ if (my_access(name,F_OK))
+ {
+ CONTROL_FILE_ERROR create_error;
+ if (!create_if_missing)
+ {
+ error= CONTROL_FILE_MISSING;
+ errmsg= "Can't find file";
+ goto err;
+ }
+ if ((create_error= create_control_file(name, open_flags)))
+ {
+ error= create_error;
+ errmsg= "Can't create file";
+ goto err;
+ }
+ if (!aria_readonly && lock_control_file(name, wait_for_lock))
+ {
+ error= CONTROL_FILE_LOCKED;
+ errmsg= lock_failed_errmsg;
+ goto err;
+ }
+ goto ok;
+ }
+
+ /* Otherwise, file exists */
+ if ((control_file_fd= mysql_file_open(key_file_control, name,
+ open_flags, MYF(MY_WME))) < 0)
+ {
+ errmsg= "Can't open file";
+ goto err;
+ }
+
+ /* lock it before reading content */
+ if (!aria_readonly && lock_control_file(name, wait_for_lock))
+ {
+ error= CONTROL_FILE_LOCKED;
+ errmsg= lock_failed_errmsg;
+ goto err;
+ }
+
+ file_size= mysql_file_seek(control_file_fd, 0, SEEK_END, MYF(MY_WME));
+ if (file_size == MY_FILEPOS_ERROR)
+ {
+ errmsg= "Can't read size";
+ goto err;
+ }
+ if (file_size < CF_MIN_SIZE)
+ {
+ /*
+ Given that normally we write only a sector and it's atomic, the only
+ possibility for a file to be of too short size is if we crashed at the
+ very first startup, between file creation and file write. Quite unlikely
+ (and can be made even more unlikely by doing this: create a temp file,
+ write it, and then rename it to be the control file).
+ What's more likely is if someone forgot to restore the control file,
+ just did a "touch control" to try to get Maria to start, or if the
+ disk/filesystem has a problem.
+ So let's be rigid.
+ */
+ error= CONTROL_FILE_TOO_SMALL;
+ errmsg= "Size of control file is smaller than expected";
+ goto err;
+ }
+
+ /* Check if control file is unexpectedly big */
+ if (file_size > CF_MAX_SIZE)
+ {
+ error= CONTROL_FILE_TOO_BIG;
+ errmsg= "File size bigger than expected";
+ goto err;
+ }
+
+ if (mysql_file_pread(control_file_fd, buffer, (size_t)file_size, 0, MYF(MY_FNABP)))
+ {
+ errmsg= "Can't read file";
+ goto err;
+ }
+
+ if (memcmp(buffer + CF_MAGIC_STRING_OFFSET,
+ CF_MAGIC_STRING, CF_MAGIC_STRING_SIZE))
+ {
+ error= CONTROL_FILE_BAD_MAGIC_STRING;
+ errmsg= "Missing valid id at start of file. File is not a valid aria control file";
+ goto err;
+ }
+
+ if (buffer[CF_VERSION_OFFSET] > CONTROL_FILE_VERSION)
+ {
+ error= CONTROL_FILE_BAD_VERSION;
+ sprintf(errmsg_buff, "File is from a future aria system: %d. Current version is: %d",
+ (int) buffer[CF_VERSION_OFFSET], CONTROL_FILE_VERSION);
+ errmsg= errmsg_buff;
+ goto err;
+ }
+
+ new_cf_create_time_size= uint2korr(buffer + CF_CREATE_TIME_SIZE_OFFSET);
+ new_cf_changeable_size= uint2korr(buffer + CF_CHANGEABLE_SIZE_OFFSET);
+
+ if (new_cf_create_time_size < CF_MIN_CREATE_TIME_TOTAL_SIZE ||
+ new_cf_changeable_size < CF_MIN_CHANGEABLE_TOTAL_SIZE ||
+ new_cf_create_time_size + new_cf_changeable_size != file_size)
+ {
+ error= CONTROL_FILE_INCONSISTENT_INFORMATION;
+ errmsg= "Sizes stored in control file are inconsistent";
+ goto err;
+ }
+
+ new_block_size= uint2korr(buffer + CF_BLOCKSIZE_OFFSET);
+ if (new_block_size != maria_block_size && maria_block_size)
+ {
+ error= CONTROL_FILE_WRONG_BLOCKSIZE;
+ sprintf(errmsg_buff,
+ "Block size in control file (%u) is different than given aria_block_size: %u",
+ new_block_size, (uint) maria_block_size);
+ errmsg= errmsg_buff;
+ goto err;
+ }
+ maria_block_size= new_block_size;
+
+ if (my_checksum(0, buffer, new_cf_create_time_size - CF_CHECKSUM_SIZE) !=
+ uint4korr(buffer + new_cf_create_time_size - CF_CHECKSUM_SIZE))
+ {
+ error= CONTROL_FILE_BAD_HEAD_CHECKSUM;
+ errmsg= "Fixed part checksum mismatch";
+ goto err;
+ }
+
+ if (my_checksum(0, buffer + new_cf_create_time_size + CF_CHECKSUM_SIZE,
+ new_cf_changeable_size - CF_CHECKSUM_SIZE) !=
+ uint4korr(buffer + new_cf_create_time_size))
+ {
+ error= CONTROL_FILE_BAD_CHECKSUM;
+ errmsg= "Changeable part (end of control file) checksum mismatch";
+ goto err;
+ }
+
+ memcpy(maria_uuid, buffer + CF_UUID_OFFSET, CF_UUID_SIZE);
+ cf_create_time_size= new_cf_create_time_size;
+ cf_changeable_size= new_cf_changeable_size;
+ last_checkpoint_lsn= lsn_korr(buffer + new_cf_create_time_size +
+ CF_LSN_OFFSET);
+ last_logno= uint4korr(buffer + new_cf_create_time_size + CF_FILENO_OFFSET);
+ if (new_cf_changeable_size >= (CF_MAX_TRID_OFFSET + CF_MAX_TRID_SIZE))
+ max_trid_in_control_file=
+ transid_korr(buffer + new_cf_create_time_size + CF_MAX_TRID_OFFSET);
+ if (new_cf_changeable_size >= (CF_RECOV_FAIL_OFFSET + CF_RECOV_FAIL_SIZE))
+ recovery_failures=
+ (buffer + new_cf_create_time_size + CF_RECOV_FAIL_OFFSET)[0];
+
+ok:
+ DBUG_RETURN(0);
+
+err:
+ if (print_error)
+ my_printf_error(HA_ERR_INITIALIZATION,
+ "Got error '%s' when trying to use aria control file "
+ "'%s'", 0, errmsg, name);
+ ma_control_file_end(); /* will unlock file if needed */
+ DBUG_RETURN(error);
+}
+
+
+/*
+ Write information durably to the control file; stores this information into
+ the last_checkpoint_lsn, last_logno, max_trid_in_control_file,
+ recovery_failures global variables.
+ Called when we have created a new log (after syncing this log's creation),
+ when we have written a checkpoint (after syncing this log record), at
+ shutdown (for storing trid in case logs are soon removed by user), and
+ before and after recovery (to store recovery_failures).
+ Variables last_checkpoint_lsn and last_logno must be protected by caller
+ using log's lock, unless this function is called at startup.
+
+ SYNOPSIS
+ ma_control_file_write_and_force()
+ last_checkpoint_lsn_arg LSN of last checkpoint
+ last_logno_arg last log file number
+ max_trid_arg maximum transaction longid
+ recovery_failures_arg consecutive recovery failures
+
+ NOTE
+ We always want to do one single my_pwrite() here to be as atomic as
+ possible.
+
+ RETURN
+ 0 - OK
+ 1 - Error
+*/
+
+int ma_control_file_write_and_force(LSN last_checkpoint_lsn_arg,
+ uint32 last_logno_arg,
+ TrID max_trid_arg,
+ uint8 recovery_failures_arg)
+{
+ uchar buffer[CF_MAX_SIZE];
+ uint32 sum;
+ my_bool no_need_sync;
+ DBUG_ENTER("ma_control_file_write_and_force");
+
+ /*
+ We don't need to sync if this is just an increase of
+ recovery_failures: it's even good if that counter is not increased on disk
+ in case of power or hardware failure (less false positives when removing
+ logs).
+ */
+ no_need_sync= ((last_checkpoint_lsn == last_checkpoint_lsn_arg) &&
+ (last_logno == last_logno_arg) &&
+ (max_trid_in_control_file == max_trid_arg) &&
+ (recovery_failures_arg > 0));
+
+ if (control_file_fd < 0)
+ DBUG_RETURN(1);
+
+#ifndef DBUG_OFF
+ if (maria_multi_threaded)
+ translog_lock_handler_assert_owner();
+#endif
+
+ lsn_store(buffer + CF_LSN_OFFSET, last_checkpoint_lsn_arg);
+ int4store(buffer + CF_FILENO_OFFSET, last_logno_arg);
+ transid_store(buffer + CF_MAX_TRID_OFFSET, max_trid_arg);
+ (buffer + CF_RECOV_FAIL_OFFSET)[0]= recovery_failures_arg;
+
+ if (cf_changeable_size > CF_CHANGEABLE_TOTAL_SIZE)
+ {
+ /*
+ More room than needed for us. Must be a newer version. Clear part which
+ we cannot maintain, so that any future version notices we didn't
+ maintain its extra data.
+ */
+ uint zeroed= cf_changeable_size - CF_CHANGEABLE_TOTAL_SIZE;
+ char msg[150];
+ bzero(buffer + CF_CHANGEABLE_TOTAL_SIZE, zeroed);
+ my_snprintf(msg, sizeof(msg),
+ "Control file must be from a newer version; zero-ing out %u"
+ " unknown bytes in control file at offset %u", zeroed,
+ cf_changeable_size + cf_create_time_size);
+ ma_message_no_user(ME_WARNING, msg);
+ }
+ else
+ {
+ /* not enough room for what we need to store: enlarge */
+ cf_changeable_size= CF_CHANGEABLE_TOTAL_SIZE;
+ }
+ /* Note that the create-time portion is not touched */
+
+ /* Checksum is stored first */
+ compile_time_assert(CF_CHECKSUM_OFFSET == 0);
+ sum= my_checksum(0, buffer + CF_CHECKSUM_SIZE,
+ cf_changeable_size - CF_CHECKSUM_SIZE);
+ int4store(buffer, sum);
+
+ if (my_pwrite(control_file_fd, buffer, cf_changeable_size,
+ cf_create_time_size, MYF(MY_FNABP | MY_WME)) ||
+ (!no_need_sync && mysql_file_sync(control_file_fd, MYF(MY_WME))))
+ DBUG_RETURN(1);
+
+ last_checkpoint_lsn= last_checkpoint_lsn_arg;
+ last_logno= last_logno_arg;
+ max_trid_in_control_file= max_trid_arg;
+ recovery_failures= recovery_failures_arg;
+
+ cf_changeable_size= CF_CHANGEABLE_TOTAL_SIZE; /* no more warning */
+ DBUG_RETURN(0);
+}
+
+
+/*
+ Free resources taken by control file subsystem
+
+ SYNOPSIS
+ ma_control_file_end()
+*/
+
+int ma_control_file_end(void)
+{
+ int close_error;
+ DBUG_ENTER("ma_control_file_end");
+
+ if (control_file_fd < 0) /* already closed */
+ DBUG_RETURN(0);
+
+#ifndef _WIN32
+ (void) my_lock(control_file_fd, F_UNLCK, 0L, F_TO_EOF,
+ MYF(MY_SEEK_NOT_DONE | MY_FORCE_LOCK));
+#endif
+
+ close_error= mysql_file_close(control_file_fd, MYF(MY_WME));
+ /*
+ As mysql_file_close() frees structures even if close() fails, we do the
+ same, i.e. we mark the file as closed in all cases.
+ */
+ control_file_fd= -1;
+ /*
+ As this module owns these variables, closing the module forbids access to
+ them (just a safety):
+ */
+ last_checkpoint_lsn= LSN_IMPOSSIBLE;
+ last_logno= FILENO_IMPOSSIBLE;
+ max_trid_in_control_file= recovery_failures= 0;
+
+ DBUG_RETURN(close_error);
+}
+
+
+/**
+ Tells if control file is initialized.
+*/
+
+my_bool ma_control_file_inited(void)
+{
+ return (control_file_fd >= 0);
+}
+
+/**
+ Print content of aria_log_control file
+*/
+
+my_bool print_aria_log_control()
+{
+ uchar buffer[CF_MAX_SIZE];
+ char name[FN_REFLEN], uuid_str[MY_UUID_STRING_LENGTH+1];
+ const char *errmsg;
+ uint new_cf_create_time_size, new_cf_changeable_size;
+ my_off_t file_size;
+ ulong logno;
+ ulonglong trid,checkpoint_lsn;
+ int open_flags= O_BINARY | /*O_DIRECT |*/ O_RDWR | O_CLOEXEC;
+ int error= CONTROL_FILE_UNKNOWN_ERROR;
+ uint recovery_fails;
+ File file;
+ DBUG_ENTER("ma_control_file_open");
+
+ if (fn_format(name, CONTROL_FILE_BASE_NAME,
+ maria_data_root, "", MYF(MY_WME)) == NullS)
+ DBUG_RETURN(CONTROL_FILE_UNKNOWN_ERROR);
+
+ if ((file= mysql_file_open(key_file_control, name,
+ open_flags, MYF(MY_WME))) < 0)
+ {
+ errmsg= "Can't open file";
+ goto err2;
+ }
+
+ file_size= mysql_file_seek(file, 0, SEEK_END, MYF(MY_WME));
+ if (file_size == MY_FILEPOS_ERROR)
+ {
+ errmsg= "Can't read size";
+ goto err;
+ }
+ if (file_size < CF_MIN_SIZE)
+ {
+ /*
+ Given that normally we write only a sector and it's atomic, the only
+ possibility for a file to be of too short size is if we crashed at the
+ very first startup, between file creation and file write. Quite unlikely
+ (and can be made even more unlikely by doing this: create a temp file,
+ write it, and then rename it to be the control file).
+ What's more likely is if someone forgot to restore the control file,
+ just did a "touch control" to try to get Maria to start, or if the
+ disk/filesystem has a problem.
+ So let's be rigid.
+ */
+ error= CONTROL_FILE_TOO_SMALL;
+ errmsg= "Size of control file is smaller than expected";
+ goto err;
+ }
+
+ /* Check if control file is unexpectedly big */
+ if (file_size > CF_MAX_SIZE)
+ {
+ error= CONTROL_FILE_TOO_BIG;
+ errmsg= "File size bigger than expected";
+ goto err;
+ }
+
+ if (mysql_file_pread(file, buffer, (size_t)file_size, 0, MYF(MY_FNABP)))
+ {
+ errmsg= "Can't read file";
+ goto err;
+ }
+
+ if (memcmp(buffer + CF_MAGIC_STRING_OFFSET,
+ CF_MAGIC_STRING, CF_MAGIC_STRING_SIZE))
+ {
+ error= CONTROL_FILE_BAD_MAGIC_STRING;
+ errmsg= "Missing valid id at start of file. File is not a valid aria control file";
+ goto err;
+ }
+
+ printf("Aria file version: %u\n", buffer[CF_VERSION_OFFSET]);
+
+ new_cf_create_time_size= uint2korr(buffer + CF_CREATE_TIME_SIZE_OFFSET);
+ new_cf_changeable_size= uint2korr(buffer + CF_CHANGEABLE_SIZE_OFFSET);
+
+ if (new_cf_create_time_size < CF_MIN_CREATE_TIME_TOTAL_SIZE ||
+ new_cf_changeable_size < CF_MIN_CHANGEABLE_TOTAL_SIZE ||
+ new_cf_create_time_size + new_cf_changeable_size != file_size)
+ {
+ error= CONTROL_FILE_INCONSISTENT_INFORMATION;
+ errmsg= "Sizes stored in control file are inconsistent";
+ goto err;
+ }
+ checkpoint_lsn= lsn_korr(buffer + new_cf_create_time_size +
+ CF_LSN_OFFSET);
+ logno= uint4korr(buffer + new_cf_create_time_size + CF_FILENO_OFFSET);
+ my_uuid2str(buffer + CF_UUID_OFFSET, uuid_str, 1);
+ uuid_str[MY_UUID_STRING_LENGTH]= 0;
+
+ printf("Block size: %u\n", uint2korr(buffer + CF_BLOCKSIZE_OFFSET));
+ printf("maria_uuid: %s\n", uuid_str);
+ printf("last_checkpoint_lsn: " LSN_FMT "\n", LSN_IN_PARTS(checkpoint_lsn));
+ printf("last_log_number: %lu\n", (ulong) logno);
+ if (new_cf_changeable_size >= (CF_MAX_TRID_OFFSET + CF_MAX_TRID_SIZE))
+ {
+ trid= transid_korr(buffer + new_cf_create_time_size + CF_MAX_TRID_OFFSET);
+ printf("trid: %llu\n", (ulonglong) trid);
+ }
+ if (new_cf_changeable_size >= (CF_RECOV_FAIL_OFFSET + CF_RECOV_FAIL_SIZE))
+ {
+ recovery_fails=
+ (buffer + new_cf_create_time_size + CF_RECOV_FAIL_OFFSET)[0];
+ printf("recovery_failures: %u\n", recovery_fails);
+ }
+ mysql_file_close(file, MYF(0));
+ DBUG_RETURN(0);
+
+err:
+ mysql_file_close(file, MYF(0));
+err2:
+ my_printf_error(HA_ERR_INITIALIZATION,
+ "Got error '%s' when trying to use aria control file "
+ "'%s'", 0, errmsg, name);
+ DBUG_RETURN(error);
+}
+
+#endif /* EXTRACT_DEFINITIONS */