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