1 /* Copyright (C) 2007 MySQL AB & Guilhem Bichot & Michael Widenius
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License as published by
5    the Free Software Foundation; version 2 of the License.
6 
7    This program is distributed in the hope that it will be useful,
8    but WITHOUT ANY WARRANTY; without even the implied warranty of
9    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10    GNU General Public License for more details.
11 
12    You should have received a copy of the GNU General Public License
13    along with this program; if not, write to the Free Software
14    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */
15 
16 /*
17   WL#3234 Maria control file
18   First version written by Guilhem Bichot on 2006-04-27.
19 */
20 
21 #ifndef EXTRACT_DEFINITIONS
22 #include "maria_def.h"
23 #include "ma_checkpoint.h"
24 #endif
25 
26 /*
27   A control file contains the following objects:
28 
29 Start of create time variables (at start of file):
30   - Magic string (including version number of Maria control file)
31   - Uuid
32   - Size of create time part
33   - Size of dynamic part
34   - Maria block size
35 .....  Here we can add new variables without changing format
36   - Checksum of create time part (last of block)
37 
38 Start of changeable part:
39   - Checksum of changeable part
40   - LSN of last checkpoint
41   - Number of last log file
42   - Max trid in control file (since Maria 1.5 May 2008)
43   - Number of consecutive recovery failures (since Maria 1.5 May 2008)
44 .....  Here we can add new variables without changing format
45 
46 The idea is that one can add new variables to the control file and still
47 use it with old program versions. If one needs to do an incompatible change
48 one should increment the control file version number.
49 */
50 
51 /* Total size should be < sector size for atomic write operation */
52 #define CF_MAX_SIZE 512
53 #define CF_MIN_SIZE (CF_BLOCKSIZE_OFFSET + CF_BLOCKSIZE_SIZE + \
54                      CF_CHECKSUM_SIZE * 2 + CF_LSN_SIZE + CF_FILENO_SIZE)
55 
56 /* Create time variables */
57 #define CF_MAGIC_STRING "\xfe\xfe\xc"
58 #define CF_MAGIC_STRING_OFFSET 0
59 #define CF_MAGIC_STRING_SIZE   (sizeof(CF_MAGIC_STRING)-1)
60 #define CF_VERSION_OFFSET      (CF_MAGIC_STRING_OFFSET + CF_MAGIC_STRING_SIZE)
61 #define CF_VERSION_SIZE        1
62 #define CF_UUID_OFFSET         (CF_VERSION_OFFSET + CF_VERSION_SIZE)
63 #define CF_UUID_SIZE           MY_UUID_SIZE
64 #define CF_CREATE_TIME_SIZE_OFFSET  (CF_UUID_OFFSET + CF_UUID_SIZE)
65 #define CF_SIZE_SIZE           2
66 #define CF_CHANGEABLE_SIZE_OFFSET   (CF_CREATE_TIME_SIZE_OFFSET + CF_SIZE_SIZE)
67 #define CF_BLOCKSIZE_OFFSET    (CF_CHANGEABLE_SIZE_OFFSET + CF_SIZE_SIZE)
68 #define CF_BLOCKSIZE_SIZE      2
69 
70 #define CF_CREATE_TIME_TOTAL_SIZE (CF_BLOCKSIZE_OFFSET + CF_BLOCKSIZE_SIZE + \
71                                    CF_CHECKSUM_SIZE)
72 
73 /*
74   Start of the part that changes during execution
75   This is stored at offset uint2korr(file[CF_CHANGEABLE_SIZE])
76 */
77 #define CF_CHECKSUM_OFFSET 0
78 #define CF_CHECKSUM_SIZE 4
79 #define CF_LSN_OFFSET (CF_CHECKSUM_OFFSET + CF_CHECKSUM_SIZE)
80 #define CF_LSN_SIZE LSN_STORE_SIZE
81 #define CF_FILENO_OFFSET (CF_LSN_OFFSET + CF_LSN_SIZE)
82 #define CF_FILENO_SIZE 4
83 #define CF_MAX_TRID_OFFSET (CF_FILENO_OFFSET + CF_FILENO_SIZE)
84 #define CF_MAX_TRID_SIZE TRANSID_SIZE
85 #define CF_RECOV_FAIL_OFFSET (CF_MAX_TRID_OFFSET + CF_MAX_TRID_SIZE)
86 #define CF_RECOV_FAIL_SIZE 1
87 #define CF_CHANGEABLE_TOTAL_SIZE (CF_RECOV_FAIL_OFFSET + CF_RECOV_FAIL_SIZE)
88 
89 /*
90   The following values should not be changed, except when changing version
91   number of the maria control file. These are the minimum sizes of the
92   parts the code can handle.
93 */
94 
95 #define CF_MIN_CREATE_TIME_TOTAL_SIZE \
96 (CF_BLOCKSIZE_OFFSET + CF_BLOCKSIZE_SIZE + CF_CHECKSUM_SIZE)
97 #define CF_MIN_CHANGEABLE_TOTAL_SIZE \
98 (CF_FILENO_OFFSET + CF_FILENO_SIZE)
99 
100 #ifndef EXTRACT_DEFINITIONS
101 
102 /* This module owns these two vars. */
103 /**
104    This LSN serves for the two-checkpoint rule, and also to find the
105    checkpoint record when doing a recovery.
106 */
107 LSN    last_checkpoint_lsn= LSN_IMPOSSIBLE;
108 uint32 last_logno=          FILENO_IMPOSSIBLE;
109 /**
110    The maximum transaction id given to a transaction. It is only updated at
111    clean shutdown (in case of crash, logs have better information).
112 */
113 TrID   max_trid_in_control_file= 0;
114 
115 /**
116   Number of consecutive log or recovery failures. Reset to 0 after recovery's
117   success.
118 */
119 uint8 recovery_failures= 0;
120 
121 /**
122    @brief If log's lock should be asserted when writing to control file.
123 
124    Can be re-used by any function which needs to be thread-safe except when
125    it is called at startup.
126 */
127 my_bool maria_multi_threaded= FALSE;
128 /** @brief if currently doing a recovery */
129 my_bool maria_in_recovery= FALSE;
130 
131 /**
132   Control file is less then  512 bytes (a disk sector),
133   to be as atomic as possible
134 */
135 static int control_file_fd= -1;
136 
137 static uint cf_create_time_size;
138 static uint cf_changeable_size;
139 
140 /**
141    @brief Create Maria control file
142 */
143 
create_control_file(const char * name,int open_flags)144 static CONTROL_FILE_ERROR create_control_file(const char *name,
145                                               int open_flags)
146 {
147   uint32 sum;
148   uchar buffer[CF_CREATE_TIME_TOTAL_SIZE];
149   ulong rnd1,rnd2;
150 
151   DBUG_ENTER("maria_create_control_file");
152 
153   if ((control_file_fd= mysql_file_create(key_file_control, name, 0,
154                                   open_flags, MYF(MY_SYNC_DIR | MY_WME))) < 0)
155     DBUG_RETURN(CONTROL_FILE_UNKNOWN_ERROR);
156 
157   /* Reset variables, as we are creating the file */
158   cf_create_time_size= CF_CREATE_TIME_TOTAL_SIZE;
159   cf_changeable_size=  CF_CHANGEABLE_TOTAL_SIZE;
160 
161   /* Create unique uuid for the control file */
162   my_random_bytes((uchar *)&rnd1, sizeof (rnd1));
163   my_random_bytes((uchar *)&rnd2, sizeof (rnd2));
164   my_uuid_init(rnd1, rnd2);
165   my_uuid(maria_uuid);
166 
167   /* Prepare and write the file header */
168   memcpy(buffer, CF_MAGIC_STRING, CF_MAGIC_STRING_SIZE);
169   buffer[CF_VERSION_OFFSET]= CONTROL_FILE_VERSION;
170   memcpy(buffer + CF_UUID_OFFSET, maria_uuid, CF_UUID_SIZE);
171   int2store(buffer + CF_CREATE_TIME_SIZE_OFFSET, cf_create_time_size);
172   int2store(buffer + CF_CHANGEABLE_SIZE_OFFSET, cf_changeable_size);
173 
174   /* Write create time variables */
175   int2store(buffer + CF_BLOCKSIZE_OFFSET, maria_block_size);
176 
177   /* Store checksum for create time parts */
178   sum= (uint32) my_checksum(0, buffer, cf_create_time_size -
179                             CF_CHECKSUM_SIZE);
180   int4store(buffer + cf_create_time_size - CF_CHECKSUM_SIZE, sum);
181 
182   if (my_pwrite(control_file_fd, buffer, cf_create_time_size,
183                 0, MYF(MY_FNABP |  MY_WME)))
184     DBUG_RETURN(CONTROL_FILE_UNKNOWN_ERROR);
185 
186   /*
187     To be safer we should make sure that there are no logs or data/index
188     files around (indeed it could be that the control file alone was deleted
189     or not restored, and we should not go on with life at this point).
190 
191     Things should still be relatively safe as if someone tries to use
192     an old table with a new control file the different uuid:s between
193     the files will cause ma_open() to generate an HA_ERR_OLD_FILE
194     error. When used from mysqld this will cause the table to be open
195     in repair mode which will remove all dependencies between the
196     table and the old control file.
197 
198     We could have a tool which can rebuild the control file, by reading the
199     directory of logs, finding the newest log, reading it to find last
200     checkpoint... Slow but can save your db. For this to be possible, we
201     must always write to the control file right after writing the checkpoint
202     log record, and do nothing in between (i.e. the checkpoint must be
203     usable as soon as it has been written to the log).
204   */
205 
206   /* init the file with these "undefined" values */
207   DBUG_RETURN(ma_control_file_write_and_force(LSN_IMPOSSIBLE,
208                                               FILENO_IMPOSSIBLE, 0, 0));
209 }
210 
211 
212 /**
213   Locks control file exclusively. This is kept for the duration of the engine
214   process, to prevent another Maria instance to write to our logs or control
215   file.
216 */
217 
lock_control_file(const char * name,my_bool do_retry)218 static int lock_control_file(const char *name, my_bool do_retry)
219 {
220   /*
221     On Windows, my_lock() uses locking() which is mandatory locking and so
222     prevents maria-recovery.test from copying the control file. And in case of
223     crash, it may take a while for Windows to unlock file, causing downtime.
224   */
225   /**
226     @todo BUG We should explore my_sopen(_SH_DENYWRD) to open or create the
227     file under Windows.
228   */
229 #ifndef __WIN__
230   uint retry= 0;
231   uint retry_count= do_retry ? MARIA_MAX_CONTROL_FILE_LOCK_RETRY : 0;
232 
233   /*
234     We can't here use the automatic wait in my_lock() as the alarm thread
235     may not yet exists.
236   */
237   while (my_lock(control_file_fd, F_WRLCK, 0L, F_TO_EOF,
238                  MYF(MY_SEEK_NOT_DONE | MY_FORCE_LOCK | MY_NO_WAIT)))
239   {
240     if (retry == 0)
241       my_printf_error(HA_ERR_INITIALIZATION,
242                       "Can't lock aria control file '%s' for exclusive use, "
243                       "error: %d. Will retry for %d seconds", 0,
244                       name, my_errno, retry_count);
245     if (++retry > retry_count)
246       return 1;
247     sleep(1);
248   }
249 #endif
250   return 0;
251 }
252 
253 
254 /*
255   @brief Initialize control file subsystem
256 
257   Looks for the control file. If none and creation is requested, creates file.
258   If present, reads it to find out last checkpoint's LSN and last log, updates
259   the last_checkpoint_lsn and last_logno global variables.
260   Called at engine's start.
261 
262   @note
263     The format of the control file is defined in the comments and defines
264     at the start of this file.
265 
266   @param create_if_missing create file if not found
267 
268   @return Operation status
269     @retval 0      OK
270     @retval 1      Error (in which case the file is left closed)
271 */
272 
ma_control_file_open(my_bool create_if_missing,my_bool print_error,my_bool wait_for_lock)273 CONTROL_FILE_ERROR ma_control_file_open(my_bool create_if_missing,
274                                         my_bool print_error,
275                                         my_bool wait_for_lock)
276 {
277   uchar buffer[CF_MAX_SIZE];
278   char name[FN_REFLEN], errmsg_buff[256];
279   const char *errmsg, *lock_failed_errmsg= "Could not get an exclusive lock;"
280     " file is probably in use by another process";
281   uint new_cf_create_time_size, new_cf_changeable_size, new_block_size;
282   my_off_t file_size;
283   int open_flags= O_BINARY | /*O_DIRECT |*/ O_RDWR | O_CLOEXEC;
284   int error= CONTROL_FILE_UNKNOWN_ERROR;
285   DBUG_ENTER("ma_control_file_open");
286 
287   /*
288     If you change sizes in the #defines, you at least have to change the
289     "*store" and "*korr" calls in this file, and can even create backward
290     compatibility problems. Beware!
291   */
292   DBUG_ASSERT(CF_LSN_SIZE == (3+4));
293   DBUG_ASSERT(CF_FILENO_SIZE == 4);
294 
295   if (control_file_fd >= 0) /* already open */
296     DBUG_RETURN(0);
297 
298   if (fn_format(name, CONTROL_FILE_BASE_NAME,
299                 maria_data_root, "", MYF(MY_WME)) == NullS)
300     DBUG_RETURN(CONTROL_FILE_UNKNOWN_ERROR);
301 
302   if (my_access(name,F_OK))
303   {
304     CONTROL_FILE_ERROR create_error;
305     if (!create_if_missing)
306     {
307       error= CONTROL_FILE_MISSING;
308       errmsg= "Can't find file";
309       goto err;
310     }
311     if ((create_error= create_control_file(name, open_flags)))
312     {
313       error= create_error;
314       errmsg= "Can't create file";
315       goto err;
316     }
317     if (!aria_readonly && lock_control_file(name, wait_for_lock))
318     {
319       error= CONTROL_FILE_LOCKED;
320       errmsg= lock_failed_errmsg;
321       goto err;
322     }
323     goto ok;
324   }
325 
326   /* Otherwise, file exists */
327   if ((control_file_fd= mysql_file_open(key_file_control, name,
328                                         open_flags, MYF(MY_WME))) < 0)
329   {
330     errmsg= "Can't open file";
331     goto err;
332   }
333 
334   /* lock it before reading content */
335   if (!aria_readonly && lock_control_file(name, wait_for_lock))
336   {
337     error= CONTROL_FILE_LOCKED;
338     errmsg= lock_failed_errmsg;
339     goto err;
340   }
341 
342   file_size= mysql_file_seek(control_file_fd, 0, SEEK_END, MYF(MY_WME));
343   if (file_size == MY_FILEPOS_ERROR)
344   {
345     errmsg= "Can't read size";
346     goto err;
347   }
348   if (file_size < CF_MIN_SIZE)
349   {
350     /*
351       Given that normally we write only a sector and it's atomic, the only
352       possibility for a file to be of too short size is if we crashed at the
353       very first startup, between file creation and file write. Quite unlikely
354       (and can be made even more unlikely by doing this: create a temp file,
355       write it, and then rename it to be the control file).
356       What's more likely is if someone forgot to restore the control file,
357       just did a "touch control" to try to get Maria to start, or if the
358       disk/filesystem has a problem.
359       So let's be rigid.
360     */
361     error= CONTROL_FILE_TOO_SMALL;
362     errmsg= "Size of control file is smaller than expected";
363     goto err;
364   }
365 
366   /* Check if control file is unexpectedly big */
367   if (file_size > CF_MAX_SIZE)
368   {
369     error= CONTROL_FILE_TOO_BIG;
370     errmsg= "File size bigger than expected";
371     goto err;
372   }
373 
374   if (mysql_file_pread(control_file_fd, buffer, (size_t)file_size, 0, MYF(MY_FNABP)))
375   {
376     errmsg= "Can't read file";
377     goto err;
378   }
379 
380   if (memcmp(buffer + CF_MAGIC_STRING_OFFSET,
381              CF_MAGIC_STRING, CF_MAGIC_STRING_SIZE))
382   {
383     error= CONTROL_FILE_BAD_MAGIC_STRING;
384     errmsg= "Missing valid id at start of file. File is not a valid aria control file";
385     goto err;
386   }
387 
388   if (buffer[CF_VERSION_OFFSET] > CONTROL_FILE_VERSION)
389   {
390     error= CONTROL_FILE_BAD_VERSION;
391     sprintf(errmsg_buff, "File is from a future aria system: %d. Current version is: %d",
392             (int) buffer[CF_VERSION_OFFSET], CONTROL_FILE_VERSION);
393     errmsg= errmsg_buff;
394     goto err;
395   }
396 
397   new_cf_create_time_size= uint2korr(buffer + CF_CREATE_TIME_SIZE_OFFSET);
398   new_cf_changeable_size=  uint2korr(buffer + CF_CHANGEABLE_SIZE_OFFSET);
399 
400   if (new_cf_create_time_size < CF_MIN_CREATE_TIME_TOTAL_SIZE ||
401       new_cf_changeable_size <  CF_MIN_CHANGEABLE_TOTAL_SIZE ||
402       new_cf_create_time_size + new_cf_changeable_size != file_size)
403   {
404     error= CONTROL_FILE_INCONSISTENT_INFORMATION;
405     errmsg= "Sizes stored in control file are inconsistent";
406     goto err;
407   }
408 
409   new_block_size= uint2korr(buffer + CF_BLOCKSIZE_OFFSET);
410   if (new_block_size != maria_block_size && maria_block_size)
411   {
412     error= CONTROL_FILE_WRONG_BLOCKSIZE;
413     sprintf(errmsg_buff,
414             "Block size in control file (%u) is different than given aria_block_size: %u",
415             new_block_size, (uint) maria_block_size);
416     errmsg= errmsg_buff;
417     goto err;
418   }
419   maria_block_size= new_block_size;
420 
421   if (my_checksum(0, buffer, new_cf_create_time_size - CF_CHECKSUM_SIZE) !=
422       uint4korr(buffer + new_cf_create_time_size - CF_CHECKSUM_SIZE))
423   {
424     error= CONTROL_FILE_BAD_HEAD_CHECKSUM;
425     errmsg= "Fixed part checksum mismatch";
426     goto err;
427   }
428 
429   if (my_checksum(0, buffer + new_cf_create_time_size + CF_CHECKSUM_SIZE,
430                   new_cf_changeable_size - CF_CHECKSUM_SIZE) !=
431       uint4korr(buffer + new_cf_create_time_size))
432   {
433     error= CONTROL_FILE_BAD_CHECKSUM;
434     errmsg= "Changeable part (end of control file) checksum mismatch";
435     goto err;
436   }
437 
438   memcpy(maria_uuid, buffer + CF_UUID_OFFSET, CF_UUID_SIZE);
439   cf_create_time_size= new_cf_create_time_size;
440   cf_changeable_size=  new_cf_changeable_size;
441   last_checkpoint_lsn= lsn_korr(buffer + new_cf_create_time_size +
442                                 CF_LSN_OFFSET);
443   last_logno= uint4korr(buffer + new_cf_create_time_size + CF_FILENO_OFFSET);
444   if (new_cf_changeable_size >= (CF_MAX_TRID_OFFSET + CF_MAX_TRID_SIZE))
445     max_trid_in_control_file=
446       transid_korr(buffer + new_cf_create_time_size + CF_MAX_TRID_OFFSET);
447   if (new_cf_changeable_size >= (CF_RECOV_FAIL_OFFSET + CF_RECOV_FAIL_SIZE))
448     recovery_failures=
449       (buffer + new_cf_create_time_size + CF_RECOV_FAIL_OFFSET)[0];
450 
451 ok:
452   DBUG_RETURN(0);
453 
454 err:
455   if (print_error)
456     my_printf_error(HA_ERR_INITIALIZATION,
457                     "Got error '%s' when trying to use aria control file "
458                     "'%s'", 0, errmsg, name);
459   ma_control_file_end(); /* will unlock file if needed */
460   DBUG_RETURN(error);
461 }
462 
463 
464 /*
465   Write information durably to the control file; stores this information into
466   the last_checkpoint_lsn, last_logno, max_trid_in_control_file,
467   recovery_failures global variables.
468   Called when we have created a new log (after syncing this log's creation),
469   when we have written a checkpoint (after syncing this log record), at
470   shutdown (for storing trid in case logs are soon removed by user), and
471   before and after recovery (to store recovery_failures).
472   Variables last_checkpoint_lsn and last_logno must be protected by caller
473   using log's lock, unless this function is called at startup.
474 
475   SYNOPSIS
476     ma_control_file_write_and_force()
477     last_checkpoint_lsn_arg LSN of last checkpoint
478     last_logno_arg          last log file number
479     max_trid_arg            maximum transaction longid
480     recovery_failures_arg   consecutive recovery failures
481 
482   NOTE
483     We always want to do one single my_pwrite() here to be as atomic as
484     possible.
485 
486   RETURN
487     0 - OK
488     1 - Error
489 */
490 
ma_control_file_write_and_force(LSN last_checkpoint_lsn_arg,uint32 last_logno_arg,TrID max_trid_arg,uint8 recovery_failures_arg)491 int ma_control_file_write_and_force(LSN last_checkpoint_lsn_arg,
492                                     uint32 last_logno_arg,
493                                     TrID max_trid_arg,
494                                     uint8 recovery_failures_arg)
495 {
496   uchar buffer[CF_MAX_SIZE];
497   uint32 sum;
498   my_bool no_need_sync;
499   DBUG_ENTER("ma_control_file_write_and_force");
500 
501   /*
502     We don't need to sync if this is just an increase of
503     recovery_failures: it's even good if that counter is not increased on disk
504     in case of power or hardware failure (less false positives when removing
505     logs).
506   */
507   no_need_sync= ((last_checkpoint_lsn == last_checkpoint_lsn_arg) &&
508                  (last_logno == last_logno_arg) &&
509                  (max_trid_in_control_file == max_trid_arg) &&
510                  (recovery_failures_arg > 0));
511 
512   if (control_file_fd < 0)
513     DBUG_RETURN(1);
514 
515 #ifndef DBUG_OFF
516   if (maria_multi_threaded)
517     translog_lock_handler_assert_owner();
518 #endif
519 
520   lsn_store(buffer + CF_LSN_OFFSET, last_checkpoint_lsn_arg);
521   int4store(buffer + CF_FILENO_OFFSET, last_logno_arg);
522   transid_store(buffer + CF_MAX_TRID_OFFSET, max_trid_arg);
523   (buffer + CF_RECOV_FAIL_OFFSET)[0]= recovery_failures_arg;
524 
525   if (cf_changeable_size > CF_CHANGEABLE_TOTAL_SIZE)
526   {
527     /*
528       More room than needed for us. Must be a newer version. Clear part which
529       we cannot maintain, so that any future version notices we didn't
530       maintain its extra data.
531     */
532     uint zeroed= cf_changeable_size - CF_CHANGEABLE_TOTAL_SIZE;
533     char msg[150];
534     bzero(buffer + CF_CHANGEABLE_TOTAL_SIZE, zeroed);
535     my_snprintf(msg, sizeof(msg),
536                 "Control file must be from a newer version; zero-ing out %u"
537                 " unknown bytes in control file at offset %u", zeroed,
538                 cf_changeable_size + cf_create_time_size);
539     ma_message_no_user(ME_WARNING, msg);
540   }
541   else
542   {
543     /* not enough room for what we need to store: enlarge */
544     cf_changeable_size= CF_CHANGEABLE_TOTAL_SIZE;
545   }
546   /* Note that the create-time portion is not touched */
547 
548   /* Checksum is stored first */
549   compile_time_assert(CF_CHECKSUM_OFFSET == 0);
550   sum= my_checksum(0, buffer + CF_CHECKSUM_SIZE,
551                    cf_changeable_size - CF_CHECKSUM_SIZE);
552   int4store(buffer, sum);
553 
554   if (my_pwrite(control_file_fd, buffer, cf_changeable_size,
555                 cf_create_time_size, MYF(MY_FNABP |  MY_WME)) ||
556       (!no_need_sync && mysql_file_sync(control_file_fd, MYF(MY_WME))))
557     DBUG_RETURN(1);
558 
559   last_checkpoint_lsn= last_checkpoint_lsn_arg;
560   last_logno= last_logno_arg;
561   max_trid_in_control_file= max_trid_arg;
562   recovery_failures= recovery_failures_arg;
563 
564   cf_changeable_size= CF_CHANGEABLE_TOTAL_SIZE; /* no more warning */
565   DBUG_RETURN(0);
566 }
567 
568 
569 /*
570   Free resources taken by control file subsystem
571 
572   SYNOPSIS
573     ma_control_file_end()
574 */
575 
ma_control_file_end(void)576 int ma_control_file_end(void)
577 {
578   int close_error;
579   DBUG_ENTER("ma_control_file_end");
580 
581   if (control_file_fd < 0) /* already closed */
582     DBUG_RETURN(0);
583 
584 #ifndef __WIN__
585   (void) my_lock(control_file_fd, F_UNLCK, 0L, F_TO_EOF,
586                  MYF(MY_SEEK_NOT_DONE | MY_FORCE_LOCK));
587 #endif
588 
589   close_error= mysql_file_close(control_file_fd, MYF(MY_WME));
590   /*
591     As mysql_file_close() frees structures even if close() fails, we do the
592     same, i.e. we mark the file as closed in all cases.
593   */
594   control_file_fd= -1;
595   /*
596     As this module owns these variables, closing the module forbids access to
597     them (just a safety):
598   */
599   last_checkpoint_lsn= LSN_IMPOSSIBLE;
600   last_logno= FILENO_IMPOSSIBLE;
601   max_trid_in_control_file= recovery_failures= 0;
602 
603   DBUG_RETURN(close_error);
604 }
605 
606 
607 /**
608   Tells if control file is initialized.
609 */
610 
ma_control_file_inited(void)611 my_bool ma_control_file_inited(void)
612 {
613   return (control_file_fd >= 0);
614 }
615 
616 /**
617    Print content of aria_log_control file
618 */
619 
print_aria_log_control()620 my_bool print_aria_log_control()
621 {
622   uchar buffer[CF_MAX_SIZE];
623   char name[FN_REFLEN], uuid_str[MY_UUID_STRING_LENGTH+1];
624   const char *errmsg;
625   uint new_cf_create_time_size, new_cf_changeable_size;
626   my_off_t file_size;
627   ulong logno;
628   ulonglong trid,checkpoint_lsn;
629   int open_flags= O_BINARY | /*O_DIRECT |*/ O_RDWR | O_CLOEXEC;
630   int error= CONTROL_FILE_UNKNOWN_ERROR;
631   uint recovery_fails;
632   File file;
633   DBUG_ENTER("ma_control_file_open");
634 
635   if (fn_format(name, CONTROL_FILE_BASE_NAME,
636                 maria_data_root, "", MYF(MY_WME)) == NullS)
637     DBUG_RETURN(CONTROL_FILE_UNKNOWN_ERROR);
638 
639   if ((file= mysql_file_open(key_file_control, name,
640                              open_flags, MYF(MY_WME))) < 0)
641   {
642     errmsg= "Can't open file";
643     goto err;
644   }
645 
646   file_size= mysql_file_seek(file, 0, SEEK_END, MYF(MY_WME));
647   if (file_size == MY_FILEPOS_ERROR)
648   {
649     errmsg= "Can't read size";
650     goto err;
651   }
652   if (file_size < CF_MIN_SIZE)
653   {
654     /*
655       Given that normally we write only a sector and it's atomic, the only
656       possibility for a file to be of too short size is if we crashed at the
657       very first startup, between file creation and file write. Quite unlikely
658       (and can be made even more unlikely by doing this: create a temp file,
659       write it, and then rename it to be the control file).
660       What's more likely is if someone forgot to restore the control file,
661       just did a "touch control" to try to get Maria to start, or if the
662       disk/filesystem has a problem.
663       So let's be rigid.
664     */
665     error= CONTROL_FILE_TOO_SMALL;
666     errmsg= "Size of control file is smaller than expected";
667     goto err;
668   }
669 
670   /* Check if control file is unexpectedly big */
671   if (file_size > CF_MAX_SIZE)
672   {
673     error= CONTROL_FILE_TOO_BIG;
674     errmsg= "File size bigger than expected";
675     goto err;
676   }
677 
678   if (mysql_file_pread(file, buffer, (size_t)file_size, 0, MYF(MY_FNABP)))
679   {
680     errmsg= "Can't read file";
681     goto err;
682   }
683 
684   if (memcmp(buffer + CF_MAGIC_STRING_OFFSET,
685              CF_MAGIC_STRING, CF_MAGIC_STRING_SIZE))
686   {
687     error= CONTROL_FILE_BAD_MAGIC_STRING;
688     errmsg= "Missing valid id at start of file. File is not a valid aria control file";
689     goto err;
690   }
691 
692   printf("Aria file version:   %u\n", buffer[CF_VERSION_OFFSET]);
693 
694   new_cf_create_time_size= uint2korr(buffer + CF_CREATE_TIME_SIZE_OFFSET);
695   new_cf_changeable_size=  uint2korr(buffer + CF_CHANGEABLE_SIZE_OFFSET);
696 
697   if (new_cf_create_time_size < CF_MIN_CREATE_TIME_TOTAL_SIZE ||
698       new_cf_changeable_size <  CF_MIN_CHANGEABLE_TOTAL_SIZE ||
699       new_cf_create_time_size + new_cf_changeable_size != file_size)
700   {
701     error= CONTROL_FILE_INCONSISTENT_INFORMATION;
702     errmsg= "Sizes stored in control file are inconsistent";
703     goto err;
704   }
705   checkpoint_lsn= lsn_korr(buffer + new_cf_create_time_size +
706                            CF_LSN_OFFSET);
707   logno= uint4korr(buffer + new_cf_create_time_size + CF_FILENO_OFFSET);
708   my_uuid2str(buffer + CF_UUID_OFFSET, uuid_str);
709   uuid_str[MY_UUID_STRING_LENGTH]= 0;
710 
711   printf("Block size:          %u\n", uint2korr(buffer + CF_BLOCKSIZE_OFFSET));
712   printf("maria_uuid:          %s\n", uuid_str);
713   printf("last_checkpoint_lsn: " LSN_FMT "\n", LSN_IN_PARTS(checkpoint_lsn));
714   printf("last_log_number:     %lu\n", (ulong) logno);
715   if (new_cf_changeable_size >= (CF_MAX_TRID_OFFSET + CF_MAX_TRID_SIZE))
716   {
717     trid= transid_korr(buffer + new_cf_create_time_size + CF_MAX_TRID_OFFSET);
718     printf("trid:                %llu\n", (ulonglong) trid);
719   }
720   if (new_cf_changeable_size >= (CF_RECOV_FAIL_OFFSET + CF_RECOV_FAIL_SIZE))
721   {
722     recovery_fails=
723       (buffer + new_cf_create_time_size + CF_RECOV_FAIL_OFFSET)[0];
724     printf("recovery_failures:   %u\n", recovery_fails);
725   }
726 
727   DBUG_RETURN(0);
728 
729 err:
730   my_printf_error(HA_ERR_INITIALIZATION,
731                   "Got error '%s' when trying to use aria control file "
732                   "'%s'", 0, errmsg, name);
733   DBUG_RETURN(error);
734 }
735 
736 #endif /* EXTRACT_DEFINITIONS */
737