| 1 | /* Copyright (C) 2006 MySQL AB & MySQL Finland AB & TCX DataKonsult AB |
| 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 02111-1301 USA */ |
| 15 | |
| 16 | /* Unit test of the control file module of the Aria engine WL#3234 */ |
| 17 | |
| 18 | /* |
| 19 | Note that it is not possible to test the durability of the write (can't |
| 20 | pull the plug programmatically :) |
| 21 | */ |
| 22 | |
| 23 | #include <my_global.h> |
| 24 | #include <my_sys.h> |
| 25 | #include <tap.h> |
| 26 | #ifdef _WIN32 |
| 27 | #include <direct.h> /* rmdir */ |
| 28 | #endif |
| 29 | #ifndef WITH_ARIA_STORAGE_ENGINE |
| 30 | /* |
| 31 | If Aria is not compiled in, normally we don't come to building this test. |
| 32 | */ |
| 33 | #error "Aria engine is not compiled in, test cannot be built" |
| 34 | #endif |
| 35 | |
| 36 | #include "maria.h" |
| 37 | #include "../../../storage/maria/maria_def.h" |
| 38 | #include <my_getopt.h> |
| 39 | |
| 40 | #define |
| 41 | #include "../ma_control_file.c" |
| 42 | #undef EXTRACT_DEFINITIONS |
| 43 | |
| 44 | char file_name[FN_REFLEN]; |
| 45 | |
| 46 | /* The values we'll set and expect the control file module to return */ |
| 47 | LSN expect_checkpoint_lsn; |
| 48 | uint32 expect_logno; |
| 49 | TrID expect_max_trid; |
| 50 | uint8 expect_recovery_failures; |
| 51 | |
| 52 | static int delete_file(myf my_flags); |
| 53 | /* |
| 54 | Those are test-specific wrappers around the module's API functions: after |
| 55 | calling the module's API functions they perform checks on the result. |
| 56 | */ |
| 57 | static int close_file(void); /* wraps ma_control_file_end */ |
| 58 | /* wraps ma_control_file_open_or_create */ |
| 59 | static int open_file(void); |
| 60 | /* wraps ma_control_file_write_and_force */ |
| 61 | static int write_file(LSN checkpoint_lsn, uint32 logno, TrID trid, |
| 62 | uint8 rec_failures); |
| 63 | |
| 64 | /* Tests */ |
| 65 | static int test_one_log_and_recovery_failures(void); |
| 66 | static int test_five_logs_and_max_trid(void); |
| 67 | static int test_3_checkpoints_and_2_logs(void); |
| 68 | static int test_binary_content(void); |
| 69 | static int test_start_stop(void); |
| 70 | static int test_2_open_and_2_close(void); |
| 71 | static int test_bad_magic_string(void); |
| 72 | static int test_bad_checksum(void); |
| 73 | static int test_bad_hchecksum(void); |
| 74 | static int test_future_size(void); |
| 75 | static int test_bad_blocksize(void); |
| 76 | static int test_bad_size(void); |
| 77 | |
| 78 | /* Utility */ |
| 79 | static int verify_module_values_match_expected(void); |
| 80 | static int verify_module_values_are_impossible(void); |
| 81 | static void usage(void); |
| 82 | static void get_options(int argc, char *argv[]); |
| 83 | |
| 84 | /* |
| 85 | If "expr" is FALSE, this macro will make the function print a diagnostic |
| 86 | message and immediately return 1. |
| 87 | This is inspired from assert() but does not crash the binary (sometimes we |
| 88 | may want to see how other tests go even if one fails). |
| 89 | RET_ERR means "return error". |
| 90 | */ |
| 91 | |
| 92 | #define RET_ERR_UNLESS(expr) \ |
| 93 | {if (!(expr)) {diag("line %d: failure: '%s'", __LINE__, #expr); assert(0);return 1;}} |
| 94 | |
| 95 | |
| 96 | /* Used to ignore error messages from ma_control_file_open() */ |
| 97 | |
| 98 | static void my_ignore_message(uint error __attribute__((unused)), |
| 99 | const char *str __attribute__((unused)), |
| 100 | myf MyFlags __attribute__((unused))) |
| 101 | { |
| 102 | DBUG_ENTER("my_message_no_curses" ); |
| 103 | DBUG_PRINT("enter" ,("message: %s" ,str)); |
| 104 | DBUG_VOID_RETURN; |
| 105 | } |
| 106 | |
| 107 | void (*default_error_handler_hook)(uint my_err, const char *str, |
| 108 | myf MyFlags) = 0; |
| 109 | |
| 110 | |
| 111 | /* like ma_control_file_open(), but without error messages */ |
| 112 | |
| 113 | static CONTROL_FILE_ERROR local_ma_control_file_open(void) |
| 114 | { |
| 115 | CONTROL_FILE_ERROR error; |
| 116 | error_handler_hook= my_ignore_message; |
| 117 | error= ma_control_file_open(TRUE, TRUE); |
| 118 | error_handler_hook= default_error_handler_hook; |
| 119 | return error; |
| 120 | } |
| 121 | |
| 122 | static char *create_tmpdir(const char *progname) |
| 123 | { |
| 124 | static char test_dirname[FN_REFLEN]; |
| 125 | char tmp_name[FN_REFLEN]; |
| 126 | size_t length; |
| 127 | |
| 128 | /* Create a temporary directory of name TMP-'executable', but without the -t extension */ |
| 129 | fn_format(tmp_name, progname, "" , "" , MY_REPLACE_DIR | MY_REPLACE_EXT); |
| 130 | length= strlen(tmp_name); |
| 131 | if (length > 2 && tmp_name[length-2] == '-' && tmp_name[length-1] == 't') |
| 132 | tmp_name[length-2]= 0; |
| 133 | strxmov(test_dirname, "TMP-" , tmp_name, NullS); |
| 134 | |
| 135 | /* |
| 136 | Don't give an error if we can't create dir, as it may already exist from a previously aborted |
| 137 | run |
| 138 | */ |
| 139 | (void) my_mkdir(test_dirname, 0777, MYF(0)); |
| 140 | return test_dirname; |
| 141 | } |
| 142 | |
| 143 | |
| 144 | int main(int argc,char *argv[]) |
| 145 | { |
| 146 | MY_INIT(argv[0]); |
| 147 | my_init(); |
| 148 | |
| 149 | default_error_handler_hook= error_handler_hook; |
| 150 | |
| 151 | plan(12); |
| 152 | |
| 153 | maria_data_root= create_tmpdir(argv[0]); |
| 154 | |
| 155 | diag("Unit tests for control file" ); |
| 156 | |
| 157 | get_options(argc,argv); |
| 158 | |
| 159 | diag("Deleting control file at startup, if there is an old one" ); |
| 160 | RET_ERR_UNLESS(0 == delete_file(0)); /* if fails, can't continue */ |
| 161 | |
| 162 | diag("Tests of normal conditions" ); |
| 163 | ok(0 == test_one_log_and_recovery_failures(), |
| 164 | "test of creating one log and recording recovery failures" ); |
| 165 | ok(0 == test_five_logs_and_max_trid(), |
| 166 | "test of creating five logs and many transactions" ); |
| 167 | ok(0 == test_3_checkpoints_and_2_logs(), |
| 168 | "test of creating three checkpoints and two logs" ); |
| 169 | ok(0 == test_binary_content(), "test of the binary content of the file" ); |
| 170 | ok(0 == test_start_stop(), "test of multiple starts and stops" ); |
| 171 | diag("Tests of abnormal conditions" ); |
| 172 | ok(0 == test_2_open_and_2_close(), |
| 173 | "test of two open and two close (strange call sequence)" ); |
| 174 | ok(0 == test_bad_magic_string(), "test of bad magic string" ); |
| 175 | ok(0 == test_bad_checksum(), "test of bad checksum" ); |
| 176 | ok(0 == test_bad_hchecksum(), "test of bad hchecksum" ); |
| 177 | ok(0 == test_future_size(), "test of ability to handlr future versions" ); |
| 178 | ok(0 == test_bad_blocksize(), "test of bad blocksize" ); |
| 179 | ok(0 == test_bad_size(), "test of too small/big file" ); |
| 180 | |
| 181 | delete_file(0); |
| 182 | rmdir(maria_data_root); |
| 183 | |
| 184 | my_uuid_end(); |
| 185 | my_end(0); |
| 186 | return exit_status(); |
| 187 | } |
| 188 | |
| 189 | |
| 190 | static int delete_file(myf my_flags) |
| 191 | { |
| 192 | RET_ERR_UNLESS(fn_format(file_name, CONTROL_FILE_BASE_NAME, |
| 193 | maria_data_root, "" , MYF(MY_WME)) != NullS); |
| 194 | /* |
| 195 | Maybe file does not exist, ignore error. |
| 196 | The error will however be printed on stderr. |
| 197 | */ |
| 198 | my_delete(file_name, my_flags); |
| 199 | expect_checkpoint_lsn= LSN_IMPOSSIBLE; |
| 200 | expect_logno= FILENO_IMPOSSIBLE; |
| 201 | expect_max_trid= expect_recovery_failures= 0; |
| 202 | |
| 203 | return 0; |
| 204 | } |
| 205 | |
| 206 | /* |
| 207 | Verifies that global values last_checkpoint_lsn, last_logno, |
| 208 | max_trid_in_control_file (belonging to the module) match what we expect. |
| 209 | */ |
| 210 | static int verify_module_values_match_expected(void) |
| 211 | { |
| 212 | RET_ERR_UNLESS(last_logno == expect_logno); |
| 213 | RET_ERR_UNLESS(last_checkpoint_lsn == expect_checkpoint_lsn); |
| 214 | RET_ERR_UNLESS(max_trid_in_control_file == expect_max_trid); |
| 215 | RET_ERR_UNLESS(recovery_failures == expect_recovery_failures); |
| 216 | return 0; |
| 217 | } |
| 218 | |
| 219 | |
| 220 | /* |
| 221 | Verifies that global values last_checkpoint_lsn and last_logno (belonging |
| 222 | to the module) are impossible (this is used when the file has been closed). |
| 223 | */ |
| 224 | static int verify_module_values_are_impossible(void) |
| 225 | { |
| 226 | RET_ERR_UNLESS(last_logno == FILENO_IMPOSSIBLE); |
| 227 | RET_ERR_UNLESS(last_checkpoint_lsn == LSN_IMPOSSIBLE); |
| 228 | RET_ERR_UNLESS(max_trid_in_control_file == 0); |
| 229 | return 0; |
| 230 | } |
| 231 | |
| 232 | |
| 233 | static int close_file(void) |
| 234 | { |
| 235 | /* Simulate shutdown */ |
| 236 | ma_control_file_end(); |
| 237 | /* Verify amnesia */ |
| 238 | RET_ERR_UNLESS(verify_module_values_are_impossible() == 0); |
| 239 | return 0; |
| 240 | } |
| 241 | |
| 242 | static int open_file(void) |
| 243 | { |
| 244 | RET_ERR_UNLESS(local_ma_control_file_open() == CONTROL_FILE_OK); |
| 245 | /* Check that the module reports expected information */ |
| 246 | RET_ERR_UNLESS(verify_module_values_match_expected() == 0); |
| 247 | return 0; |
| 248 | } |
| 249 | |
| 250 | static int write_file(LSN checkpoint_lsn, uint32 logno, TrID trid, |
| 251 | uint8 rec_failures) |
| 252 | { |
| 253 | RET_ERR_UNLESS(ma_control_file_write_and_force(checkpoint_lsn, logno, trid, |
| 254 | rec_failures) |
| 255 | == 0); |
| 256 | /* Check that the module reports expected information */ |
| 257 | RET_ERR_UNLESS(verify_module_values_match_expected() == 0); |
| 258 | return 0; |
| 259 | } |
| 260 | |
| 261 | static int test_one_log_and_recovery_failures(void) |
| 262 | { |
| 263 | RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK); |
| 264 | expect_logno= 123; |
| 265 | RET_ERR_UNLESS(write_file(last_checkpoint_lsn, expect_logno, |
| 266 | max_trid_in_control_file, |
| 267 | recovery_failures) == 0); |
| 268 | expect_recovery_failures= 158; |
| 269 | RET_ERR_UNLESS(write_file(last_checkpoint_lsn, expect_logno, |
| 270 | max_trid_in_control_file, |
| 271 | expect_recovery_failures) == 0); |
| 272 | RET_ERR_UNLESS(close_file() == 0); |
| 273 | return 0; |
| 274 | } |
| 275 | |
| 276 | static int test_five_logs_and_max_trid(void) |
| 277 | { |
| 278 | uint i; |
| 279 | |
| 280 | RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK); |
| 281 | expect_logno= 100; |
| 282 | expect_max_trid= 14111978111ULL; |
| 283 | for (i= 0; i<5; i++) |
| 284 | { |
| 285 | expect_logno*= 3; |
| 286 | RET_ERR_UNLESS(write_file(last_checkpoint_lsn, expect_logno, |
| 287 | expect_max_trid, |
| 288 | recovery_failures) == 0); |
| 289 | } |
| 290 | RET_ERR_UNLESS(close_file() == 0); |
| 291 | return 0; |
| 292 | } |
| 293 | |
| 294 | static int test_3_checkpoints_and_2_logs(void) |
| 295 | { |
| 296 | /* |
| 297 | Simulate one checkpoint, one log creation, two checkpoints, one |
| 298 | log creation. |
| 299 | */ |
| 300 | RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK); |
| 301 | expect_checkpoint_lsn= MAKE_LSN(5, 10000); |
| 302 | RET_ERR_UNLESS(write_file(expect_checkpoint_lsn, expect_logno, |
| 303 | max_trid_in_control_file, |
| 304 | recovery_failures) == 0); |
| 305 | |
| 306 | expect_logno= 17; |
| 307 | RET_ERR_UNLESS(write_file(expect_checkpoint_lsn, expect_logno, |
| 308 | max_trid_in_control_file, |
| 309 | recovery_failures) == 0); |
| 310 | |
| 311 | expect_checkpoint_lsn= MAKE_LSN(17, 20000); |
| 312 | RET_ERR_UNLESS(write_file(expect_checkpoint_lsn, expect_logno, |
| 313 | max_trid_in_control_file, |
| 314 | recovery_failures) == 0); |
| 315 | |
| 316 | expect_checkpoint_lsn= MAKE_LSN(17, 45000); |
| 317 | RET_ERR_UNLESS(write_file(expect_checkpoint_lsn, expect_logno, |
| 318 | max_trid_in_control_file, |
| 319 | recovery_failures) == 0); |
| 320 | |
| 321 | expect_logno= 19; |
| 322 | RET_ERR_UNLESS(write_file(expect_checkpoint_lsn, expect_logno, |
| 323 | max_trid_in_control_file, |
| 324 | recovery_failures) == 0); |
| 325 | RET_ERR_UNLESS(close_file() == 0); |
| 326 | return 0; |
| 327 | } |
| 328 | |
| 329 | static int test_binary_content(void) |
| 330 | { |
| 331 | uint i; |
| 332 | int fd; |
| 333 | |
| 334 | /* |
| 335 | TEST4: actually check by ourselves the content of the file. |
| 336 | Note that constants (offsets) are hard-coded here, precisely to prevent |
| 337 | someone from changing them in the control file module and breaking |
| 338 | backward-compatibility. |
| 339 | TODO: when we reach the format-freeze state, we may even just do a |
| 340 | comparison with a raw binary string, to not depend on any uint4korr |
| 341 | future change/breakage. |
| 342 | */ |
| 343 | |
| 344 | uchar buffer[45]; |
| 345 | RET_ERR_UNLESS((fd= my_open(file_name, |
| 346 | O_BINARY | O_RDWR, |
| 347 | MYF(MY_WME))) >= 0); |
| 348 | RET_ERR_UNLESS(my_read(fd, buffer, 45, MYF(MY_FNABP | MY_WME)) == 0); |
| 349 | RET_ERR_UNLESS(my_close(fd, MYF(MY_WME)) == 0); |
| 350 | RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK); |
| 351 | i= uint3korr(buffer + 34 ); |
| 352 | RET_ERR_UNLESS(i == LSN_FILE_NO(last_checkpoint_lsn)); |
| 353 | i= uint4korr(buffer + 37); |
| 354 | RET_ERR_UNLESS(i == LSN_OFFSET(last_checkpoint_lsn)); |
| 355 | i= uint4korr(buffer + 41); |
| 356 | RET_ERR_UNLESS(i == last_logno); |
| 357 | RET_ERR_UNLESS(close_file() == 0); |
| 358 | return 0; |
| 359 | } |
| 360 | |
| 361 | static int test_start_stop(void) |
| 362 | { |
| 363 | /* TEST5: Simulate start/nothing/stop/start/nothing/stop/start */ |
| 364 | |
| 365 | RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK); |
| 366 | RET_ERR_UNLESS(close_file() == 0); |
| 367 | RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK); |
| 368 | RET_ERR_UNLESS(close_file() == 0); |
| 369 | RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK); |
| 370 | RET_ERR_UNLESS(close_file() == 0); |
| 371 | return 0; |
| 372 | } |
| 373 | |
| 374 | static int test_2_open_and_2_close(void) |
| 375 | { |
| 376 | RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK); |
| 377 | RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK); |
| 378 | RET_ERR_UNLESS(close_file() == 0); |
| 379 | RET_ERR_UNLESS(close_file() == 0); |
| 380 | return 0; |
| 381 | } |
| 382 | |
| 383 | |
| 384 | static int test_bad_magic_string(void) |
| 385 | { |
| 386 | uchar buffer[4]; |
| 387 | int fd; |
| 388 | |
| 389 | RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK); |
| 390 | RET_ERR_UNLESS(close_file() == 0); |
| 391 | |
| 392 | /* Corrupt magic string */ |
| 393 | RET_ERR_UNLESS((fd= my_open(file_name, |
| 394 | O_BINARY | O_RDWR, |
| 395 | MYF(MY_WME))) >= 0); |
| 396 | RET_ERR_UNLESS(my_pread(fd, buffer, 4, 0, MYF(MY_FNABP | MY_WME)) == 0); |
| 397 | RET_ERR_UNLESS(my_pwrite(fd, (const uchar *)"papa" , 4, 0, |
| 398 | MYF(MY_FNABP | MY_WME)) == 0); |
| 399 | |
| 400 | /* Check that control file module sees the problem */ |
| 401 | RET_ERR_UNLESS(local_ma_control_file_open() == |
| 402 | CONTROL_FILE_BAD_MAGIC_STRING); |
| 403 | /* Restore magic string */ |
| 404 | RET_ERR_UNLESS(my_pwrite(fd, buffer, 4, 0, MYF(MY_FNABP | MY_WME)) == 0); |
| 405 | RET_ERR_UNLESS(my_close(fd, MYF(MY_WME)) == 0); |
| 406 | RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK); |
| 407 | RET_ERR_UNLESS(close_file() == 0); |
| 408 | return 0; |
| 409 | } |
| 410 | |
| 411 | static int test_bad_checksum(void) |
| 412 | { |
| 413 | uchar buffer[4]; |
| 414 | int fd; |
| 415 | |
| 416 | RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK); |
| 417 | RET_ERR_UNLESS(close_file() == 0); |
| 418 | |
| 419 | /* Corrupt checksum */ |
| 420 | RET_ERR_UNLESS((fd= my_open(file_name, |
| 421 | O_BINARY | O_RDWR, |
| 422 | MYF(MY_WME))) >= 0); |
| 423 | RET_ERR_UNLESS(my_pread(fd, buffer, 1, 30, MYF(MY_FNABP | MY_WME)) == 0); |
| 424 | buffer[0]+= 3; /* mangle checksum */ |
| 425 | RET_ERR_UNLESS(my_pwrite(fd, buffer, 1, 30, MYF(MY_FNABP | MY_WME)) == 0); |
| 426 | /* Check that control file module sees the problem */ |
| 427 | RET_ERR_UNLESS(local_ma_control_file_open() == |
| 428 | CONTROL_FILE_BAD_CHECKSUM); |
| 429 | /* Restore checksum */ |
| 430 | buffer[0]-= 3; |
| 431 | RET_ERR_UNLESS(my_pwrite(fd, buffer, 1, 30, MYF(MY_FNABP | MY_WME)) == 0); |
| 432 | RET_ERR_UNLESS(my_close(fd, MYF(MY_WME)) == 0); |
| 433 | |
| 434 | return 0; |
| 435 | } |
| 436 | |
| 437 | |
| 438 | static int test_bad_blocksize(void) |
| 439 | { |
| 440 | maria_block_size<<= 1; |
| 441 | /* Check that control file module sees the problem */ |
| 442 | RET_ERR_UNLESS(local_ma_control_file_open() == |
| 443 | CONTROL_FILE_WRONG_BLOCKSIZE); |
| 444 | /* Restore blocksize */ |
| 445 | maria_block_size>>= 1; |
| 446 | |
| 447 | RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK); |
| 448 | RET_ERR_UNLESS(close_file() == 0); |
| 449 | return 0; |
| 450 | } |
| 451 | |
| 452 | |
| 453 | static int test_future_size(void) |
| 454 | { |
| 455 | /* |
| 456 | Here we check ability to add fields only so we can use |
| 457 | defined constants |
| 458 | */ |
| 459 | uint32 sum; |
| 460 | int fd; |
| 461 | uchar buffer[CF_CREATE_TIME_TOTAL_SIZE + CF_CHANGEABLE_TOTAL_SIZE + 2]; |
| 462 | RET_ERR_UNLESS((fd= my_open(file_name, |
| 463 | O_BINARY | O_RDWR, |
| 464 | MYF(MY_WME))) >= 0); |
| 465 | RET_ERR_UNLESS(my_read(fd, buffer, |
| 466 | CF_CREATE_TIME_TOTAL_SIZE + CF_CHANGEABLE_TOTAL_SIZE, |
| 467 | MYF(MY_FNABP | MY_WME)) == 0); |
| 468 | RET_ERR_UNLESS(my_close(fd, MYF(MY_WME)) == 0); |
| 469 | /* "add" new field of 1 byte (value 1) to header and variable part */ |
| 470 | memmove(buffer + CF_CREATE_TIME_TOTAL_SIZE + 1, |
| 471 | buffer + CF_CREATE_TIME_TOTAL_SIZE, |
| 472 | CF_CHANGEABLE_TOTAL_SIZE); |
| 473 | buffer[CF_CREATE_TIME_TOTAL_SIZE - CF_CHECKSUM_SIZE]= '\1'; |
| 474 | buffer[CF_CREATE_TIME_TOTAL_SIZE + CF_CHANGEABLE_TOTAL_SIZE + 1]= '\1'; |
| 475 | /* fix lengths */ |
| 476 | int2store(buffer + CF_CREATE_TIME_SIZE_OFFSET, CF_CREATE_TIME_TOTAL_SIZE + 1); |
| 477 | int2store(buffer + CF_CHANGEABLE_SIZE_OFFSET, CF_CHANGEABLE_TOTAL_SIZE + 1); |
| 478 | /* recalculete checksums */ |
| 479 | sum= (uint32) my_checksum(0, buffer, CF_CREATE_TIME_TOTAL_SIZE - |
| 480 | CF_CHECKSUM_SIZE + 1); |
| 481 | int4store(buffer + CF_CREATE_TIME_TOTAL_SIZE - CF_CHECKSUM_SIZE + 1, sum); |
| 482 | sum= (uint32) my_checksum(0, buffer + CF_CREATE_TIME_TOTAL_SIZE + 1 + |
| 483 | CF_CHECKSUM_SIZE, |
| 484 | CF_CHANGEABLE_TOTAL_SIZE - CF_CHECKSUM_SIZE + 1); |
| 485 | int4store(buffer + CF_CREATE_TIME_TOTAL_SIZE + 1, sum); |
| 486 | /* write new file and check it */ |
| 487 | RET_ERR_UNLESS((fd= my_open(file_name, |
| 488 | O_BINARY | O_RDWR, |
| 489 | MYF(MY_WME))) >= 0); |
| 490 | RET_ERR_UNLESS(my_pwrite(fd, buffer, |
| 491 | CF_CREATE_TIME_TOTAL_SIZE + |
| 492 | CF_CHANGEABLE_TOTAL_SIZE + 2, |
| 493 | 0, MYF(MY_FNABP | MY_WME)) == 0); |
| 494 | RET_ERR_UNLESS(my_close(fd, MYF(MY_WME)) == 0); |
| 495 | RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK); |
| 496 | RET_ERR_UNLESS(close_file() == 0); |
| 497 | |
| 498 | return(0); |
| 499 | } |
| 500 | |
| 501 | static int test_bad_hchecksum(void) |
| 502 | { |
| 503 | uchar buffer[4]; |
| 504 | int fd; |
| 505 | |
| 506 | RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK); |
| 507 | RET_ERR_UNLESS(close_file() == 0); |
| 508 | |
| 509 | /* Corrupt checksum */ |
| 510 | RET_ERR_UNLESS((fd= my_open(file_name, |
| 511 | O_BINARY | O_RDWR, |
| 512 | MYF(MY_WME))) >= 0); |
| 513 | RET_ERR_UNLESS(my_pread(fd, buffer, 1, 26, MYF(MY_FNABP | MY_WME)) == 0); |
| 514 | buffer[0]+= 3; /* mangle checksum */ |
| 515 | RET_ERR_UNLESS(my_pwrite(fd, buffer, 1, 26, MYF(MY_FNABP | MY_WME)) == 0); |
| 516 | /* Check that control file module sees the problem */ |
| 517 | RET_ERR_UNLESS(local_ma_control_file_open() == |
| 518 | CONTROL_FILE_BAD_HEAD_CHECKSUM); |
| 519 | /* Restore checksum */ |
| 520 | buffer[0]-= 3; |
| 521 | RET_ERR_UNLESS(my_pwrite(fd, buffer, 1, 26, MYF(MY_FNABP | MY_WME)) == 0); |
| 522 | RET_ERR_UNLESS(my_close(fd, MYF(MY_WME)) == 0); |
| 523 | |
| 524 | return 0; |
| 525 | } |
| 526 | |
| 527 | |
| 528 | static int test_bad_size(void) |
| 529 | { |
| 530 | uchar buffer[]= |
| 531 | "123456789012345678901234567890123456789012345678901234567890123456" ; |
| 532 | int fd, i; |
| 533 | |
| 534 | /* A too short file */ |
| 535 | RET_ERR_UNLESS(delete_file(MYF(MY_WME)) == 0); |
| 536 | RET_ERR_UNLESS((fd= my_open(file_name, |
| 537 | O_BINARY | O_RDWR | O_CREAT, |
| 538 | MYF(MY_WME))) >= 0); |
| 539 | RET_ERR_UNLESS(my_write(fd, buffer, 10, MYF(MY_FNABP | MY_WME)) == 0); |
| 540 | /* Check that control file module sees the problem */ |
| 541 | RET_ERR_UNLESS(local_ma_control_file_open() == |
| 542 | CONTROL_FILE_TOO_SMALL); |
| 543 | for (i= 0; i < 8; i++) |
| 544 | { |
| 545 | RET_ERR_UNLESS(my_write(fd, buffer, 66, MYF(MY_FNABP | MY_WME)) == 0); |
| 546 | } |
| 547 | /* Check that control file module sees the problem */ |
| 548 | RET_ERR_UNLESS(local_ma_control_file_open() == |
| 549 | CONTROL_FILE_TOO_BIG); |
| 550 | RET_ERR_UNLESS(my_close(fd, MYF(MY_WME)) == 0); |
| 551 | |
| 552 | /* Leave a correct control file */ |
| 553 | RET_ERR_UNLESS(delete_file(MYF(MY_WME)) == 0); |
| 554 | RET_ERR_UNLESS(open_file() == CONTROL_FILE_OK); |
| 555 | RET_ERR_UNLESS(close_file() == 0); |
| 556 | |
| 557 | return 0; |
| 558 | } |
| 559 | |
| 560 | |
| 561 | static struct my_option my_long_options[] = |
| 562 | { |
| 563 | #ifndef DBUG_OFF |
| 564 | {"debug" , '#', "Debug log." , |
| 565 | 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, |
| 566 | #endif |
| 567 | {"help" , '?', "Display help and exit" , |
| 568 | 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, |
| 569 | {"version" , 'V', "Print version number and exit" , |
| 570 | 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}, |
| 571 | { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} |
| 572 | }; |
| 573 | |
| 574 | |
| 575 | static void version(void) |
| 576 | { |
| 577 | printf("ma_control_file_test: unit test for the control file " |
| 578 | "module of the Aria storage engine. Ver 1.0 \n" ); |
| 579 | } |
| 580 | |
| 581 | static my_bool |
| 582 | get_one_option(int optid, const struct my_option *opt __attribute__((unused)), |
| 583 | char *argument __attribute__((unused))) |
| 584 | { |
| 585 | switch(optid) { |
| 586 | case 'V': |
| 587 | version(); |
| 588 | exit(0); |
| 589 | case '#': |
| 590 | DBUG_PUSH (argument); |
| 591 | break; |
| 592 | case '?': |
| 593 | version(); |
| 594 | usage(); |
| 595 | exit(0); |
| 596 | } |
| 597 | return 0; |
| 598 | } |
| 599 | |
| 600 | |
| 601 | /* Read options */ |
| 602 | |
| 603 | static void get_options(int argc, char *argv[]) |
| 604 | { |
| 605 | int ho_error; |
| 606 | |
| 607 | if ((ho_error=handle_options(&argc, &argv, my_long_options, |
| 608 | get_one_option))) |
| 609 | exit(ho_error); |
| 610 | |
| 611 | return; |
| 612 | } /* get options */ |
| 613 | |
| 614 | |
| 615 | static void usage(void) |
| 616 | { |
| 617 | printf("Usage: %s [options]\n\n" , my_progname); |
| 618 | my_print_help(my_long_options); |
| 619 | my_print_variables(my_long_options); |
| 620 | } |
| 621 | |
| 622 | #include "../ma_check_standalone.h" |
| 623 | |