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