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