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