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