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