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