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