1 /* 2 * badblocks.c - Bad blocks checker 3 * 4 * Copyright (C) 1992, 1993, 1994 Remy Card <card (at) masi.ibp.fr> 5 * Laboratoire MASI, Institut Blaise Pascal 6 * Universite Pierre et Marie Curie (Paris VI) 7 * 8 * Copyright 1995, 1996, 1997, 1998, 1999 by Theodore Ts'o 9 * Copyright 1999 by David Beattie 10 * 11 * This file is based on the minix file system programs fsck and mkfs 12 * written and copyrighted by Linus Torvalds <Linus.Torvalds (at) cs.helsinki.fi> 13 * 14 * %Begin-Header% 15 * This file may be redistributed under the terms of the GNU Public 16 * License. 17 * %End-Header% 18 */ 19 20 /* 21 * History: 22 * 93/05/26 - Creation from e2fsck 23 * 94/02/27 - Made a separate bad blocks checker 24 * 99/06/30...99/07/26 - Added non-destructive write-testing, 25 * configurable blocks-at-once parameter, 26 * loading of badblocks list to avoid testing 27 * blocks known to be bad, multiple passes to 28 * make sure that no new blocks are added to the 29 * list. (Work done by David Beattie) 30 */ 31 32 #ifndef _GNU_SOURCE 33 #define _GNU_SOURCE /* for O_DIRECT */ 34 #endif 35 36 #include "config.h" 37 #include <errno.h> 38 #include <fcntl.h> 39 #ifdef HAVE_GETOPT_H 40 #include <getopt.h> 41 #else 42 extern char *optarg; 43 extern int optind; 44 #endif 45 #include <signal.h> 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <string.h> 49 #include <unistd.h> 50 #include <setjmp.h> 51 #include <time.h> 52 #include <limits.h> 53 54 #include <sys/time.h> 55 #include <sys/ioctl.h> 56 #include <sys/types.h> 57 58 #include "et/com_err.h" 59 #include "ext2fs/ext2_io.h" 60 #include "ext2fs/ext2_fs.h" 61 #include "ext2fs/ext2fs.h" 62 #include "support/nls-enable.h" 63 64 #ifndef O_LARGEFILE 65 #define O_LARGEFILE 0 66 #endif 67 68 /* Maximum number of bad blocks we support */ 69 #define MAX_BAD_BLOCKS (INT_MAX/2) 70 71 static const char * program_name = "badblocks"; 72 static const char * done_string = N_("done \n"); 73 74 static int v_flag; /* verbose */ 75 static int w_flag; /* do r/w test: 0=no, 1=yes, 76 * 2=non-destructive */ 77 static int s_flag; /* show progress of test */ 78 static int force; /* force check of mounted device */ 79 static int t_flag; /* number of test patterns */ 80 static int t_max; /* allocated test patterns */ 81 static unsigned int *t_patts; /* test patterns */ 82 static int use_buffered_io; 83 static int exclusive_ok; 84 static unsigned int max_bb = MAX_BAD_BLOCKS; /* Abort test if more than this 85 * number of bad blocks has been 86 * encountered */ 87 static unsigned int d_flag; /* delay factor between reads */ 88 static struct timeval time_start; 89 90 #define T_INC 32 91 92 static unsigned int sys_page_size = 4096; 93 94 static void usage(void) 95 { 96 fprintf(stderr, _( 97 "Usage: %s [-b block_size] [-i input_file] [-o output_file] [-svwnf]\n" 98 " [-c blocks_at_once] [-d delay_factor_between_reads] [-e max_bad_blocks]\n" 99 " [-p num_passes] [-t test_pattern [-t test_pattern [...]]]\n" 100 " device [last_block [first_block]]\n"), 101 program_name); 102 exit (1); 103 } 104 105 static void exclusive_usage(void) 106 { 107 fprintf(stderr, 108 _("%s: The -n and -w options are mutually exclusive.\n\n"), 109 program_name); 110 exit(1); 111 } 112 113 static blk_t currently_testing = 0; 114 static blk_t num_blocks = 0; 115 static blk_t num_read_errors = 0; 116 static blk_t num_write_errors = 0; 117 static blk_t num_corruption_errors = 0; 118 static ext2_badblocks_list bb_list = NULL; 119 static FILE *out; 120 static blk_t next_bad = 0; 121 static ext2_badblocks_iterate bb_iter = NULL; 122 123 enum error_types { READ_ERROR, WRITE_ERROR, CORRUPTION_ERROR }; 124 125 static void *allocate_buffer(size_t size) 126 { 127 void *ret = 0; 128 129 #ifdef HAVE_POSIX_MEMALIGN 130 if (posix_memalign(&ret, sys_page_size, size) < 0) 131 ret = 0; 132 #else 133 #ifdef HAVE_MEMALIGN 134 ret = memalign(sys_page_size, size); 135 #else 136 #ifdef HAVE_VALLOC 137 ret = valloc(size); 138 #endif /* HAVE_VALLOC */ 139 #endif /* HAVE_MEMALIGN */ 140 #endif /* HAVE_POSIX_MEMALIGN */ 141 142 if (!ret) 143 ret = malloc(size); 144 145 return ret; 146 } 147 148 /* 149 * This routine reports a new bad block. If the bad block has already 150 * been seen before, then it returns 0; otherwise it returns 1. 151 */ 152 static int bb_output (blk_t bad, enum error_types error_type) 153 { 154 errcode_t errcode; 155 156 if (ext2fs_badblocks_list_test(bb_list, bad)) 157 return 0; 158 159 fprintf(out, "%lu\n", (unsigned long) bad); 160 fflush(out); 161 162 errcode = ext2fs_badblocks_list_add (bb_list, bad); 163 if (errcode) { 164 com_err (program_name, errcode, "adding to in-memory bad block list"); 165 exit (1); 166 } 167 168 /* kludge: 169 increment the iteration through the bb_list if 170 an element was just added before the current iteration 171 position. This should not cause next_bad to change. */ 172 if (bb_iter && bad < next_bad) 173 ext2fs_badblocks_list_iterate (bb_iter, &next_bad); 174 175 if (error_type == READ_ERROR) { 176 num_read_errors++; 177 } else if (error_type == WRITE_ERROR) { 178 num_write_errors++; 179 } else if (error_type == CORRUPTION_ERROR) { 180 num_corruption_errors++; 181 } 182 return 1; 183 } 184 185 static char *time_diff_format(struct timeval *tv1, 186 struct timeval *tv2, char *buf) 187 { 188 time_t diff = (tv1->tv_sec - tv2->tv_sec); 189 int hr,min,sec; 190 191 sec = diff % 60; 192 diff /= 60; 193 min = diff % 60; 194 hr = diff / 60; 195 196 if (hr) 197 sprintf(buf, "%d:%02d:%02d", hr, min, sec); 198 else 199 sprintf(buf, "%d:%02d", min, sec); 200 return buf; 201 } 202 203 static float calc_percent(unsigned long current, unsigned long total) { 204 float percent = 0.0; 205 if (total <= 0) 206 return percent; 207 if (current >= total) { 208 percent = 100.0; 209 } else { 210 percent=(100.0*(float)current/(float)total); 211 } 212 return percent; 213 } 214 215 static void print_status(void) 216 { 217 struct timeval time_end; 218 char diff_buf[32], line_buf[128]; 219 int len; 220 221 gettimeofday(&time_end, 0); 222 len = snprintf(line_buf, sizeof(line_buf), 223 _("%6.2f%% done, %s elapsed. " 224 "(%d/%d/%d errors)"), 225 calc_percent((unsigned long) currently_testing, 226 (unsigned long) num_blocks), 227 time_diff_format(&time_end, &time_start, diff_buf), 228 num_read_errors, 229 num_write_errors, 230 num_corruption_errors); 231 #ifdef HAVE_MBSTOWCS 232 len = mbstowcs(NULL, line_buf, sizeof(line_buf)); 233 #endif 234 fputs(line_buf, stderr); 235 memset(line_buf, '\b', len); 236 line_buf[len] = 0; 237 fputs(line_buf, stderr); 238 fflush (stderr); 239 } 240 241 static void alarm_intr(int alnum EXT2FS_ATTR((unused))) 242 { 243 signal (SIGALRM, alarm_intr); 244 alarm(1); 245 if (!num_blocks) 246 return; 247 print_status(); 248 } 249 250 static void *terminate_addr = NULL; 251 252 static void terminate_intr(int signo EXT2FS_ATTR((unused))) 253 { 254 fflush(out); 255 fprintf(stderr, "\n\nInterrupted at block %llu\n", 256 (unsigned long long) currently_testing); 257 fflush(stderr); 258 if (terminate_addr) 259 longjmp(terminate_addr,1); 260 exit(1); 261 } 262 263 static void capture_terminate(jmp_buf term_addr) 264 { 265 terminate_addr = term_addr; 266 signal (SIGHUP, terminate_intr); 267 signal (SIGINT, terminate_intr); 268 signal (SIGPIPE, terminate_intr); 269 signal (SIGTERM, terminate_intr); 270 signal (SIGUSR1, terminate_intr); 271 signal (SIGUSR2, terminate_intr); 272 } 273 274 static void uncapture_terminate(void) 275 { 276 terminate_addr = NULL; 277 signal (SIGHUP, SIG_DFL); 278 signal (SIGINT, SIG_DFL); 279 signal (SIGPIPE, SIG_DFL); 280 signal (SIGTERM, SIG_DFL); 281 signal (SIGUSR1, SIG_DFL); 282 signal (SIGUSR2, SIG_DFL); 283 } 284 285 /* Linux requires that O_DIRECT I/Os be 512-byte sector aligned */ 286 287 #define O_DIRECT_SIZE 512 288 289 static void set_o_direct(int dev, unsigned char *buffer, size_t size, 290 ext2_loff_t offset) 291 { 292 #ifdef O_DIRECT 293 static int current_O_DIRECT; /* Current status of O_DIRECT flag */ 294 int new_flag = O_DIRECT; 295 int flag; 296 297 if ((use_buffered_io != 0) || 298 (((unsigned long) buffer & (sys_page_size - 1)) != 0) || 299 ((size & (sys_page_size - 1)) != 0) || 300 ((offset & (O_DIRECT_SIZE - 1)) != 0)) 301 new_flag = 0; 302 303 if (new_flag != current_O_DIRECT) { 304 /* printf("%s O_DIRECT\n", new_flag ? "Setting" : "Clearing"); */ 305 flag = fcntl(dev, F_GETFL); 306 if (flag > 0) { 307 flag = (flag & ~O_DIRECT) | new_flag; 308 if (fcntl(dev, F_SETFL, flag) < 0) 309 perror("set_o_direct"); 310 } 311 current_O_DIRECT = new_flag; 312 } 313 #endif 314 } 315 316 317 static void pattern_fill(unsigned char *buffer, unsigned int pattern, 318 size_t n) 319 { 320 unsigned int i, nb; 321 unsigned char bpattern[sizeof(pattern)], *ptr; 322 323 if (pattern == (unsigned int) ~0) { 324 for (ptr = buffer; ptr < buffer + n; ptr++) { 325 (*ptr) = random() % (1 << (8 * sizeof(char))); 326 } 327 if (s_flag | v_flag) 328 fputs(_("Testing with random pattern: "), stderr); 329 } else { 330 bpattern[0] = 0; 331 for (i = 0; i < sizeof(bpattern); i++) { 332 if (pattern == 0) 333 break; 334 bpattern[i] = pattern & 0xFF; 335 pattern = pattern >> 8; 336 } 337 nb = i ? (i-1) : 0; 338 for (ptr = buffer, i = nb; ptr < buffer + n; ptr++) { 339 *ptr = bpattern[i]; 340 if (i == 0) 341 i = nb; 342 else 343 i--; 344 } 345 if (s_flag | v_flag) { 346 fputs(_("Testing with pattern 0x"), stderr); 347 for (i = 0; i <= nb; i++) 348 fprintf(stderr, "%02x", buffer[i]); 349 fputs(": ", stderr); 350 } 351 } 352 } 353 354 /* 355 * Perform a read of a sequence of blocks; return the number of blocks 356 * successfully sequentially read. 357 */ 358 static int do_read (int dev, unsigned char * buffer, int try, int block_size, 359 blk_t current_block) 360 { 361 long got; 362 struct timeval tv1, tv2; 363 #define NANOSEC (1000000000L) 364 #define MILISEC (1000L) 365 366 #if 0 367 printf("do_read: block %d, try %d\n", current_block, try); 368 #endif 369 set_o_direct(dev, buffer, try * block_size, 370 ((ext2_loff_t) current_block) * block_size); 371 372 if (v_flag > 1) 373 print_status(); 374 375 /* Seek to the correct loc. */ 376 if (ext2fs_llseek (dev, (ext2_loff_t) current_block * block_size, 377 SEEK_SET) != (ext2_loff_t) current_block * block_size) 378 com_err (program_name, errno, "%s", _("during seek")); 379 380 /* Try the read */ 381 if (d_flag) 382 gettimeofday(&tv1, NULL); 383 got = read (dev, buffer, try * block_size); 384 if (d_flag) 385 gettimeofday(&tv2, NULL); 386 if (got < 0) 387 got = 0; 388 if (got & 511) 389 fprintf(stderr, _("Weird value (%ld) in do_read\n"), got); 390 got /= block_size; 391 if (d_flag && got == try) { 392 #ifdef HAVE_NANOSLEEP 393 struct timespec ts; 394 ts.tv_sec = tv2.tv_sec - tv1.tv_sec; 395 ts.tv_nsec = (tv2.tv_usec - tv1.tv_usec) * MILISEC; 396 if (ts.tv_nsec < 0) { 397 ts.tv_nsec += NANOSEC; 398 ts.tv_sec -= 1; 399 } 400 /* increase/decrease the sleep time based on d_flag value */ 401 ts.tv_sec = ts.tv_sec * d_flag / 100; 402 ts.tv_nsec = ts.tv_nsec * d_flag / 100; 403 if (ts.tv_nsec > NANOSEC) { 404 ts.tv_sec += ts.tv_nsec / NANOSEC; 405 ts.tv_nsec %= NANOSEC; 406 } 407 if (ts.tv_sec || ts.tv_nsec) 408 nanosleep(&ts, NULL); 409 #else 410 #ifdef HAVE_USLEEP 411 struct timeval tv; 412 tv.tv_sec = tv2.tv_sec - tv1.tv_sec; 413 tv.tv_usec = tv2.tv_usec - tv1.tv_usec; 414 tv.tv_sec = tv.tv_sec * d_flag / 100; 415 tv.tv_usec = tv.tv_usec * d_flag / 100; 416 if (tv.tv_usec > 1000000) { 417 tv.tv_sec += tv.tv_usec / 1000000; 418 tv.tv_usec %= 1000000; 419 } 420 if (tv.tv_sec) 421 sleep(tv.tv_sec); 422 if (tv.tv_usec) 423 usleep(tv.tv_usec); 424 #endif 425 #endif 426 } 427 return got; 428 } 429 430 /* 431 * Perform a write of a sequence of blocks; return the number of blocks 432 * successfully sequentially written. 433 */ 434 static int do_write(int dev, unsigned char * buffer, int try, int block_size, 435 unsigned long current_block) 436 { 437 long got; 438 439 #if 0 440 printf("do_write: block %lu, try %d\n", current_block, try); 441 #endif 442 set_o_direct(dev, buffer, try * block_size, 443 ((ext2_loff_t) current_block) * block_size); 444 445 if (v_flag > 1) 446 print_status(); 447 448 /* Seek to the correct loc. */ 449 if (ext2fs_llseek (dev, (ext2_loff_t) current_block * block_size, 450 SEEK_SET) != (ext2_loff_t) current_block * block_size) 451 com_err (program_name, errno, "%s", _("during seek")); 452 453 /* Try the write */ 454 got = write (dev, buffer, try * block_size); 455 if (got < 0) 456 got = 0; 457 if (got & 511) 458 fprintf(stderr, "Weird value (%ld) in do_write\n", got); 459 got /= block_size; 460 return got; 461 } 462 463 static int host_dev; 464 465 static void flush_bufs(void) 466 { 467 errcode_t retval; 468 469 #ifdef O_DIRECT 470 if (!use_buffered_io) 471 return; 472 #endif 473 retval = ext2fs_sync_device(host_dev, 1); 474 if (retval) 475 com_err(program_name, retval, "%s", 476 _("during ext2fs_sync_device")); 477 } 478 479 static unsigned int test_ro (int dev, blk_t last_block, 480 int block_size, blk_t first_block, 481 unsigned int blocks_at_once) 482 { 483 unsigned char * blkbuf; 484 int try; 485 int got; 486 unsigned int bb_count = 0; 487 errcode_t errcode; 488 blk_t recover_block = ~0; 489 490 /* set up abend handler */ 491 capture_terminate(NULL); 492 493 errcode = ext2fs_badblocks_list_iterate_begin(bb_list,&bb_iter); 494 if (errcode) { 495 com_err(program_name, errcode, "%s", 496 _("while beginning bad block list iteration")); 497 exit (1); 498 } 499 do { 500 ext2fs_badblocks_list_iterate (bb_iter, &next_bad); 501 } while (next_bad && next_bad < first_block); 502 503 if (t_flag) { 504 blkbuf = allocate_buffer((blocks_at_once + 1) * block_size); 505 } else { 506 blkbuf = allocate_buffer(blocks_at_once * block_size); 507 } 508 if (!blkbuf) 509 { 510 com_err(program_name, ENOMEM, "%s", 511 _("while allocating buffers")); 512 exit (1); 513 } 514 if (v_flag) { 515 fprintf(stderr, _("Checking blocks %lu to %lu\n"), 516 (unsigned long)first_block, 517 (unsigned long)last_block - 1); 518 } 519 if (t_flag) { 520 fputs(_("Checking for bad blocks in read-only mode\n"), stderr); 521 pattern_fill(blkbuf + blocks_at_once * block_size, 522 t_patts[0], block_size); 523 } 524 flush_bufs(); 525 try = blocks_at_once; 526 currently_testing = first_block; 527 num_blocks = last_block - 1; 528 if (!t_flag && (s_flag || v_flag)) 529 fputs(_("Checking for bad blocks (read-only test): "), stderr); 530 if (s_flag && v_flag <= 1) 531 alarm_intr(SIGALRM); 532 while (currently_testing < last_block) 533 { 534 if (bb_count >= max_bb) { 535 if (s_flag || v_flag) { 536 fputs(_("Too many bad blocks, aborting test\n"), stderr); 537 } 538 break; 539 } 540 if (next_bad) { 541 if (currently_testing == next_bad) { 542 /* fprintf (out, "%lu\n", nextbad); */ 543 ext2fs_badblocks_list_iterate (bb_iter, &next_bad); 544 currently_testing++; 545 continue; 546 } 547 else if (currently_testing + try > next_bad) 548 try = next_bad - currently_testing; 549 } 550 if (currently_testing + try > last_block) 551 try = last_block - currently_testing; 552 got = do_read (dev, blkbuf, try, block_size, currently_testing); 553 if (t_flag) { 554 /* test the comparison between all the 555 blocks successfully read */ 556 int i; 557 for (i = 0; i < got; ++i) 558 if (memcmp (blkbuf+i*block_size, 559 blkbuf+blocks_at_once*block_size, 560 block_size)) 561 bb_count += bb_output(currently_testing + i, CORRUPTION_ERROR); 562 } 563 if (got == 0 && try == 1) 564 bb_count += bb_output(currently_testing++, READ_ERROR); 565 currently_testing += got; 566 if (got != try) { 567 try = 1; 568 if (recover_block == ~0U) 569 recover_block = currently_testing - got + 570 blocks_at_once; 571 continue; 572 } else if (currently_testing == recover_block) { 573 try = blocks_at_once; 574 recover_block = ~0; 575 } 576 } 577 num_blocks = 0; 578 alarm(0); 579 if (s_flag || v_flag) 580 fputs(_(done_string), stderr); 581 582 fflush (stderr); 583 free (blkbuf); 584 585 ext2fs_badblocks_list_iterate_end(bb_iter); 586 587 uncapture_terminate(); 588 589 return bb_count; 590 } 591 592 static unsigned int test_rw (int dev, blk_t last_block, 593 int block_size, blk_t first_block, 594 unsigned int blocks_at_once) 595 { 596 unsigned char *buffer, *read_buffer; 597 const unsigned int patterns[] = {0xaa, 0x55, 0xff, 0x00}; 598 const unsigned int *pattern; 599 int i, try, got, nr_pattern, pat_idx; 600 unsigned int bb_count = 0; 601 blk_t recover_block = ~0; 602 603 /* set up abend handler */ 604 capture_terminate(NULL); 605 606 buffer = allocate_buffer(2 * blocks_at_once * block_size); 607 read_buffer = buffer + blocks_at_once * block_size; 608 609 if (!buffer) { 610 com_err(program_name, ENOMEM, "%s", 611 _("while allocating buffers")); 612 exit (1); 613 } 614 615 flush_bufs(); 616 617 if (v_flag) { 618 fputs(_("Checking for bad blocks in read-write mode\n"), 619 stderr); 620 fprintf(stderr, _("From block %lu to %lu\n"), 621 (unsigned long) first_block, 622 (unsigned long) last_block - 1); 623 } 624 if (t_flag) { 625 pattern = t_patts; 626 nr_pattern = t_flag; 627 } else { 628 pattern = patterns; 629 nr_pattern = sizeof(patterns) / sizeof(patterns[0]); 630 } 631 for (pat_idx = 0; pat_idx < nr_pattern; pat_idx++) { 632 pattern_fill(buffer, pattern[pat_idx], 633 blocks_at_once * block_size); 634 num_blocks = last_block - 1; 635 currently_testing = first_block; 636 if (s_flag && v_flag <= 1) 637 alarm_intr(SIGALRM); 638 639 try = blocks_at_once; 640 while (currently_testing < last_block) { 641 if (bb_count >= max_bb) { 642 if (s_flag || v_flag) { 643 fputs(_("Too many bad blocks, aborting test\n"), stderr); 644 } 645 break; 646 } 647 if (currently_testing + try > last_block) 648 try = last_block - currently_testing; 649 got = do_write(dev, buffer, try, block_size, 650 currently_testing); 651 if (v_flag > 1) 652 print_status(); 653 654 if (got == 0 && try == 1) 655 bb_count += bb_output(currently_testing++, WRITE_ERROR); 656 currently_testing += got; 657 if (got != try) { 658 try = 1; 659 if (recover_block == ~0U) 660 recover_block = currently_testing - 661 got + blocks_at_once; 662 continue; 663 } else if (currently_testing == recover_block) { 664 try = blocks_at_once; 665 recover_block = ~0; 666 } 667 } 668 669 num_blocks = 0; 670 alarm (0); 671 if (s_flag | v_flag) 672 fputs(_(done_string), stderr); 673 flush_bufs(); 674 if (s_flag | v_flag) 675 fputs(_("Reading and comparing: "), stderr); 676 num_blocks = last_block; 677 currently_testing = first_block; 678 if (s_flag && v_flag <= 1) 679 alarm_intr(SIGALRM); 680 681 try = blocks_at_once; 682 while (currently_testing < last_block) { 683 if (bb_count >= max_bb) { 684 if (s_flag || v_flag) { 685 fputs(_("Too many bad blocks, aborting test\n"), stderr); 686 } 687 break; 688 } 689 if (currently_testing + try > last_block) 690 try = last_block - currently_testing; 691 got = do_read (dev, read_buffer, try, block_size, 692 currently_testing); 693 if (got == 0 && try == 1) 694 bb_count += bb_output(currently_testing++, READ_ERROR); 695 currently_testing += got; 696 if (got != try) { 697 try = 1; 698 if (recover_block == ~0U) 699 recover_block = currently_testing - 700 got + blocks_at_once; 701 continue; 702 } else if (currently_testing == recover_block) { 703 try = blocks_at_once; 704 recover_block = ~0U; 705 } 706 for (i=0; i < got; i++) { 707 if (memcmp(read_buffer + i * block_size, 708 buffer + i * block_size, 709 block_size)) 710 bb_count += bb_output(currently_testing+i, CORRUPTION_ERROR); 711 } 712 if (v_flag > 1) 713 print_status(); 714 } 715 716 num_blocks = 0; 717 alarm (0); 718 if (s_flag | v_flag) 719 fputs(_(done_string), stderr); 720 flush_bufs(); 721 } 722 uncapture_terminate(); 723 free(buffer); 724 return bb_count; 725 } 726 727 struct saved_blk_record { 728 blk_t block; 729 int num; 730 }; 731 732 static unsigned int test_nd (int dev, blk_t last_block, 733 int block_size, blk_t first_block, 734 unsigned int blocks_at_once) 735 { 736 unsigned char *blkbuf, *save_ptr, *test_ptr, *read_ptr; 737 unsigned char *test_base, *save_base, *read_base; 738 int try, i; 739 const unsigned int patterns[] = { ~0 }; 740 const unsigned int *pattern; 741 int nr_pattern, pat_idx; 742 int got, used2, written; 743 blk_t save_currently_testing; 744 struct saved_blk_record *test_record; 745 /* This is static to prevent being clobbered by the longjmp */ 746 static int num_saved; 747 jmp_buf terminate_env; 748 errcode_t errcode; 749 unsigned long buf_used; 750 static unsigned int bb_count; 751 unsigned int granularity = blocks_at_once; 752 blk_t recover_block = ~0U; 753 754 bb_count = 0; 755 errcode = ext2fs_badblocks_list_iterate_begin(bb_list,&bb_iter); 756 if (errcode) { 757 com_err(program_name, errcode, "%s", 758 _("while beginning bad block list iteration")); 759 exit (1); 760 } 761 do { 762 ext2fs_badblocks_list_iterate (bb_iter, &next_bad); 763 } while (next_bad && next_bad < first_block); 764 765 blkbuf = allocate_buffer(3 * blocks_at_once * block_size); 766 test_record = malloc(blocks_at_once * sizeof(struct saved_blk_record)); 767 if (!blkbuf || !test_record) { 768 com_err(program_name, ENOMEM, "%s", 769 _("while allocating buffers")); 770 exit (1); 771 } 772 773 save_base = blkbuf; 774 test_base = blkbuf + (blocks_at_once * block_size); 775 read_base = blkbuf + (2 * blocks_at_once * block_size); 776 777 num_saved = 0; 778 779 flush_bufs(); 780 if (v_flag) { 781 fputs(_("Checking for bad blocks in non-destructive read-write mode\n"), stderr); 782 fprintf (stderr, _("From block %lu to %lu\n"), 783 (unsigned long) first_block, 784 (unsigned long) last_block - 1); 785 } 786 if (s_flag || v_flag > 1) { 787 fputs(_("Checking for bad blocks (non-destructive read-write test)\n"), stderr); 788 } 789 if (setjmp(terminate_env)) { 790 /* 791 * Abnormal termination by a signal is handled here. 792 */ 793 signal (SIGALRM, SIG_IGN); 794 fputs(_("\nInterrupt caught, cleaning up\n"), stderr); 795 796 save_ptr = save_base; 797 for (i=0; i < num_saved; i++) { 798 do_write(dev, save_ptr, test_record[i].num, 799 block_size, test_record[i].block); 800 save_ptr += test_record[i].num * block_size; 801 } 802 fflush (out); 803 exit(1); 804 } 805 806 /* set up abend handler */ 807 capture_terminate(terminate_env); 808 809 if (t_flag) { 810 pattern = t_patts; 811 nr_pattern = t_flag; 812 } else { 813 pattern = patterns; 814 nr_pattern = sizeof(patterns) / sizeof(patterns[0]); 815 } 816 for (pat_idx = 0; pat_idx < nr_pattern; pat_idx++) { 817 pattern_fill(test_base, pattern[pat_idx], 818 blocks_at_once * block_size); 819 820 buf_used = 0; 821 bb_count = 0; 822 save_ptr = save_base; 823 test_ptr = test_base; 824 currently_testing = first_block; 825 num_blocks = last_block - 1; 826 if (s_flag && v_flag <= 1) 827 alarm_intr(SIGALRM); 828 829 while (currently_testing < last_block) { 830 if (bb_count >= max_bb) { 831 if (s_flag || v_flag) { 832 fputs(_("Too many bad blocks, aborting test\n"), stderr); 833 } 834 break; 835 } 836 got = try = granularity - buf_used; 837 if (next_bad) { 838 if (currently_testing == next_bad) { 839 /* fprintf (out, "%lu\n", nextbad); */ 840 ext2fs_badblocks_list_iterate (bb_iter, &next_bad); 841 currently_testing++; 842 goto check_for_more; 843 } 844 else if (currently_testing + try > next_bad) 845 try = next_bad - currently_testing; 846 } 847 if (currently_testing + try > last_block) 848 try = last_block - currently_testing; 849 got = do_read (dev, save_ptr, try, block_size, 850 currently_testing); 851 if (got == 0) { 852 if (recover_block == ~0U) 853 recover_block = currently_testing + 854 blocks_at_once; 855 if (granularity != 1) { 856 granularity = 1; 857 continue; 858 } 859 /* First block must have been bad. */ 860 bb_count += bb_output(currently_testing++, READ_ERROR); 861 goto check_for_more; 862 } 863 864 /* 865 * Note the fact that we've saved this much data 866 * *before* we overwrite it with test data 867 */ 868 test_record[num_saved].block = currently_testing; 869 test_record[num_saved].num = got; 870 num_saved++; 871 872 /* Write the test data */ 873 written = do_write (dev, test_ptr, got, block_size, 874 currently_testing); 875 if (written != got) 876 com_err (program_name, errno, 877 _("during test data write, block %lu"), 878 (unsigned long) currently_testing + 879 written); 880 881 buf_used += got; 882 save_ptr += got * block_size; 883 test_ptr += got * block_size; 884 currently_testing += got; 885 if (got != try) { 886 try = 1; 887 if (recover_block == ~0U) 888 recover_block = currently_testing - 889 got + blocks_at_once; 890 continue; 891 } 892 893 check_for_more: 894 /* 895 * If there's room for more blocks to be tested this 896 * around, and we're not done yet testing the disk, go 897 * back and get some more blocks. 898 */ 899 if ((buf_used != granularity) && 900 (currently_testing < last_block)) 901 continue; 902 903 if (currently_testing >= recover_block) { 904 granularity = blocks_at_once; 905 recover_block = ~0; 906 } 907 908 flush_bufs(); 909 save_currently_testing = currently_testing; 910 911 /* 912 * for each contiguous block that we read into the 913 * buffer (and wrote test data into afterwards), read 914 * it back (looping if necessary, to get past newly 915 * discovered unreadable blocks, of which there should 916 * be none, but with a hard drive which is unreliable, 917 * it has happened), and compare with the test data 918 * that was written; output to the bad block list if 919 * it doesn't match. 920 */ 921 used2 = 0; 922 save_ptr = save_base; 923 test_ptr = test_base; 924 read_ptr = read_base; 925 try = 0; 926 927 while (1) { 928 if (try == 0) { 929 if (used2 >= num_saved) 930 break; 931 currently_testing = test_record[used2].block; 932 try = test_record[used2].num; 933 used2++; 934 } 935 936 got = do_read (dev, read_ptr, try, 937 block_size, currently_testing); 938 939 /* test the comparison between all the 940 blocks successfully read */ 941 for (i = 0; i < got; ++i) 942 if (memcmp (test_ptr+i*block_size, 943 read_ptr+i*block_size, block_size)) 944 bb_count += bb_output(currently_testing + i, CORRUPTION_ERROR); 945 if (got < try) { 946 bb_count += bb_output(currently_testing + got, READ_ERROR); 947 got++; 948 } 949 950 /* write back original data */ 951 do_write (dev, save_ptr, got, 952 block_size, currently_testing); 953 save_ptr += got * block_size; 954 955 currently_testing += got; 956 test_ptr += got * block_size; 957 read_ptr += got * block_size; 958 try -= got; 959 } 960 961 /* empty the buffer so it can be reused */ 962 num_saved = 0; 963 buf_used = 0; 964 save_ptr = save_base; 965 test_ptr = test_base; 966 currently_testing = save_currently_testing; 967 } 968 num_blocks = 0; 969 alarm(0); 970 if (s_flag || v_flag > 1) 971 fputs(_(done_string), stderr); 972 973 flush_bufs(); 974 } 975 uncapture_terminate(); 976 fflush(stderr); 977 free(blkbuf); 978 free(test_record); 979 980 ext2fs_badblocks_list_iterate_end(bb_iter); 981 982 return bb_count; 983 } 984 985 static void check_mount(char *device_name) 986 { 987 errcode_t retval; 988 int mount_flags; 989 990 retval = ext2fs_check_if_mounted(device_name, &mount_flags); 991 if (retval) { 992 com_err("ext2fs_check_if_mount", retval, 993 _("while determining whether %s is mounted."), 994 device_name); 995 return; 996 } 997 if (mount_flags & EXT2_MF_MOUNTED) { 998 fprintf(stderr, _("%s is mounted; "), device_name); 999 if (force) { 1000 fputs(_("badblocks forced anyway. " 1001 "Hope /etc/mtab is incorrect.\n"), stderr); 1002 return; 1003 } 1004 abort_badblocks: 1005 fputs(_("it's not safe to run badblocks!\n"), stderr); 1006 exit(1); 1007 } 1008 1009 if ((mount_flags & EXT2_MF_BUSY) && !exclusive_ok) { 1010 fprintf(stderr, _("%s is apparently in use by the system; "), 1011 device_name); 1012 if (force) 1013 fputs(_("badblocks forced anyway.\n"), stderr); 1014 else 1015 goto abort_badblocks; 1016 } 1017 1018 } 1019 1020 /* 1021 * This function will convert a string to an unsigned long, printing 1022 * an error message if it fails, and returning success or failure in err. 1023 */ 1024 static unsigned int parse_uint(const char *str, const char *descr) 1025 { 1026 char *tmp; 1027 unsigned long ret; 1028 1029 errno = 0; 1030 ret = strtoul(str, &tmp, 0); 1031 if (*tmp || errno || (ret > UINT_MAX) || 1032 (ret == ULONG_MAX && errno == ERANGE)) { 1033 com_err (program_name, 0, _("invalid %s - %s"), descr, str); 1034 exit (1); 1035 } 1036 return ret; 1037 } 1038 1039 int main (int argc, char ** argv) 1040 { 1041 int c; 1042 char * device_name; 1043 char * host_device_name = NULL; 1044 char * input_file = NULL; 1045 char * output_file = NULL; 1046 FILE * in = NULL; 1047 int block_size = 1024; 1048 unsigned int blocks_at_once = 64; 1049 blk64_t last_block, first_block; 1050 int num_passes = 0; 1051 int passes_clean = 0; 1052 int dev; 1053 errcode_t errcode; 1054 unsigned int pattern; 1055 unsigned int (*test_func)(int, blk_t, 1056 int, blk_t, 1057 unsigned int); 1058 int open_flag; 1059 long sysval; 1060 blk64_t inblk; 1061 1062 setbuf(stdout, NULL); 1063 setbuf(stderr, NULL); 1064 #ifdef ENABLE_NLS 1065 setlocale(LC_MESSAGES, ""); 1066 setlocale(LC_CTYPE, ""); 1067 bindtextdomain(NLS_CAT_NAME, LOCALEDIR); 1068 textdomain(NLS_CAT_NAME); 1069 set_com_err_gettext(gettext); 1070 #endif 1071 srandom((unsigned int)time(NULL)); /* simple randomness is enough */ 1072 test_func = test_ro; 1073 1074 /* Determine the system page size if possible */ 1075 #ifdef HAVE_SYSCONF 1076 #if (!defined(_SC_PAGESIZE) && defined(_SC_PAGE_SIZE)) 1077 #define _SC_PAGESIZE _SC_PAGE_SIZE 1078 #endif 1079 #ifdef _SC_PAGESIZE 1080 sysval = sysconf(_SC_PAGESIZE); 1081 if (sysval > 0) 1082 sys_page_size = sysval; 1083 #endif /* _SC_PAGESIZE */ 1084 #endif /* HAVE_SYSCONF */ 1085 1086 if (argc && *argv) 1087 program_name = *argv; 1088 while ((c = getopt (argc, argv, "b:d:e:fi:o:svwnc:p:h:t:BX")) != EOF) { 1089 switch (c) { 1090 case 'b': 1091 block_size = parse_uint(optarg, "block size"); 1092 break; 1093 case 'f': 1094 force++; 1095 break; 1096 case 'i': 1097 input_file = optarg; 1098 break; 1099 case 'o': 1100 output_file = optarg; 1101 break; 1102 case 's': 1103 s_flag = 1; 1104 break; 1105 case 'v': 1106 v_flag++; 1107 break; 1108 case 'w': 1109 if (w_flag) 1110 exclusive_usage(); 1111 test_func = test_rw; 1112 w_flag = 1; 1113 break; 1114 case 'n': 1115 if (w_flag) 1116 exclusive_usage(); 1117 test_func = test_nd; 1118 w_flag = 2; 1119 break; 1120 case 'c': 1121 blocks_at_once = parse_uint(optarg, "blocks at once"); 1122 break; 1123 case 'e': 1124 max_bb = parse_uint(optarg, "max bad block count"); 1125 if (max_bb > MAX_BAD_BLOCKS) { 1126 com_err (program_name, 0, 1127 _("Too big max bad blocks count %u - " 1128 "maximum is %u"), max_bb, 1129 MAX_BAD_BLOCKS); 1130 exit (1); 1131 } 1132 /* 0 really means unlimited but we cannot do that much... */ 1133 if (max_bb == 0) 1134 max_bb = MAX_BAD_BLOCKS; 1135 break; 1136 case 'd': 1137 d_flag = parse_uint(optarg, "read delay factor"); 1138 break; 1139 case 'p': 1140 num_passes = parse_uint(optarg, 1141 "number of clean passes"); 1142 break; 1143 case 'h': 1144 host_device_name = optarg; 1145 break; 1146 case 't': 1147 if (t_flag + 1 > t_max) { 1148 unsigned int *t_patts_new; 1149 1150 t_patts_new = realloc(t_patts, sizeof(int) * 1151 (t_max + T_INC)); 1152 if (!t_patts_new) { 1153 com_err(program_name, ENOMEM, 1154 _("can't allocate memory for " 1155 "test_pattern - %s"), 1156 optarg); 1157 exit(1); 1158 } 1159 t_patts = t_patts_new; 1160 t_max += T_INC; 1161 } 1162 if (!strcmp(optarg, "r") || !strcmp(optarg,"random")) { 1163 t_patts[t_flag++] = ~0; 1164 } else { 1165 pattern = parse_uint(optarg, "test pattern"); 1166 if (pattern == (unsigned int) ~0) 1167 pattern = 0xffff; 1168 t_patts[t_flag++] = pattern; 1169 } 1170 break; 1171 case 'B': 1172 use_buffered_io = 1; 1173 break; 1174 case 'X': 1175 exclusive_ok++; 1176 break; 1177 default: 1178 usage(); 1179 } 1180 } 1181 if (!w_flag) { 1182 if (t_flag > 1) { 1183 com_err(program_name, 0, "%s", 1184 _("Maximum of one test_pattern may be " 1185 "specified in read-only mode")); 1186 exit(1); 1187 } 1188 if (t_patts && (t_patts[0] == (unsigned int) ~0)) { 1189 com_err(program_name, 0, "%s", 1190 _("Random test_pattern is not allowed " 1191 "in read-only mode")); 1192 exit(1); 1193 } 1194 } 1195 if (optind > argc - 1) 1196 usage(); 1197 device_name = argv[optind++]; 1198 if (optind > argc - 1) { 1199 errcode = ext2fs_get_device_size2(device_name, 1200 block_size, 1201 &last_block); 1202 if (errcode == EXT2_ET_UNIMPLEMENTED) { 1203 com_err(program_name, 0, "%s", 1204 _("Couldn't determine device size; you " 1205 "must specify\nthe size manually\n")); 1206 exit(1); 1207 } 1208 if (errcode) { 1209 com_err(program_name, errcode, "%s", 1210 _("while trying to determine device size")); 1211 exit(1); 1212 } 1213 } else { 1214 errno = 0; 1215 last_block = parse_uint(argv[optind], _("last block")); 1216 last_block++; 1217 optind++; 1218 } 1219 if (optind <= argc-1) { 1220 errno = 0; 1221 first_block = parse_uint(argv[optind], _("first block")); 1222 } else first_block = 0; 1223 if (first_block >= last_block) { 1224 com_err (program_name, 0, _("invalid starting block (%llu): must be less than %llu"), 1225 first_block, last_block); 1226 exit (1); 1227 } 1228 /* ext2 badblocks file can't handle large values */ 1229 if (last_block >> 32) { 1230 com_err(program_name, EOVERFLOW, 1231 _("invalid end block (%llu): must be 32-bit value"), 1232 last_block); 1233 exit(1); 1234 } 1235 if (w_flag) 1236 check_mount(device_name); 1237 1238 gettimeofday(&time_start, 0); 1239 open_flag = O_LARGEFILE | (w_flag ? O_RDWR : O_RDONLY); 1240 dev = open (device_name, open_flag); 1241 if (dev == -1) { 1242 com_err (program_name, errno, _("while trying to open %s"), 1243 device_name); 1244 exit (1); 1245 } 1246 if (host_device_name) { 1247 host_dev = open (host_device_name, open_flag); 1248 if (host_dev == -1) { 1249 com_err (program_name, errno, 1250 _("while trying to open %s"), 1251 host_device_name); 1252 exit (1); 1253 } 1254 } else 1255 host_dev = dev; 1256 if (input_file) { 1257 if (strcmp (input_file, "-") == 0) 1258 in = stdin; 1259 else { 1260 in = fopen (input_file, "r"); 1261 if (in == NULL) 1262 { 1263 com_err (program_name, errno, 1264 _("while trying to open %s"), 1265 input_file); 1266 exit (1); 1267 } 1268 } 1269 } 1270 if (output_file && strcmp (output_file, "-") != 0) 1271 { 1272 out = fopen (output_file, "w"); 1273 if (out == NULL) 1274 { 1275 com_err (program_name, errno, 1276 _("while trying to open %s"), 1277 output_file); 1278 exit (1); 1279 } 1280 } 1281 else 1282 out = stdout; 1283 1284 errcode = ext2fs_badblocks_list_create(&bb_list,0); 1285 if (errcode) { 1286 com_err(program_name, errcode, "%s", 1287 _("while creating in-memory bad blocks list")); 1288 exit (1); 1289 } 1290 1291 if (in) { 1292 for(;;) { 1293 switch (fscanf(in, "%llu\n", &inblk)) { 1294 case 0: 1295 com_err(program_name, 0, "%s", 1296 _("input file - bad format")); 1297 exit (1); 1298 case EOF: 1299 break; 1300 default: 1301 if (inblk >> 32) { 1302 com_err(program_name, 1303 EOVERFLOW, "%s", 1304 _("while adding to in-memory " 1305 "bad block list")); 1306 exit(1); 1307 } 1308 next_bad = inblk; 1309 errcode = ext2fs_badblocks_list_add(bb_list,next_bad); 1310 if (errcode) { 1311 com_err(program_name, errcode, 1312 "%s", 1313 _("while adding to in-memory " 1314 "bad block list")); 1315 exit (1); 1316 } 1317 continue; 1318 } 1319 break; 1320 } 1321 1322 if (in != stdin) 1323 fclose (in); 1324 } 1325 1326 do { 1327 unsigned int bb_count; 1328 1329 bb_count = test_func(dev, last_block, block_size, 1330 first_block, blocks_at_once); 1331 if (bb_count) 1332 passes_clean = 0; 1333 else 1334 ++passes_clean; 1335 1336 if (v_flag) 1337 fprintf(stderr, 1338 _("Pass completed, %u bad blocks found. (%d/%d/%d errors)\n"), 1339 bb_count, num_read_errors, num_write_errors, num_corruption_errors); 1340 1341 } while (passes_clean < num_passes); 1342 1343 close (dev); 1344 if (out != stdout) 1345 fclose (out); 1346 free(t_patts); 1347 return 0; 1348 } 1349