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