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