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