2 * badblocks.c - Bad blocks checker
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)
8 * Copyright 1995, 1996, 1997, 1998, 1999 by Theodore Ts'o
9 * Copyright 1999 by David Beattie
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>
15 * This file may be redistributed under the terms of the GNU Public
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)
47 #include <sys/ioctl.h>
48 #include <sys/types.h>
50 #include "et/com_err.h"
51 #include "ext2fs/ext2_io.h"
52 #include "ext2fs/ext2_fs.h"
53 #include "ext2fs/ext2fs.h"
54 #include "nls-enable.h"
56 const char * program_name = "badblocks";
57 const char * done_string = N_("done \n");
59 static int v_flag = 0; /* verbose */
60 static int w_flag = 0; /* do r/w test: 0=no, 1=yes,
61 * 2=non-destructive */
62 static int s_flag = 0; /* show progress of test */
63 static int force = 0; /* force check of mounted device */
65 static void usage(void)
67 fprintf(stderr, _("Usage: %s [-b block_size] [-i input_file] [-o output_file] [-svwnf]\n [-c blocks_at_once] [-p num_passes] device [last_block [start_count]]\n"),
72 static unsigned long currently_testing = 0;
73 static unsigned long num_blocks = 0;
74 static ext2_badblocks_list bb_list = NULL;
76 static blk_t next_bad = 0;
77 static ext2_badblocks_iterate bb_iter = NULL;
80 * This routine reports a new bad block. If the bad block has already
81 * been seen before, then it returns 0; otherwise it returns 1.
83 static int bb_output (unsigned long bad)
87 if (ext2fs_badblocks_list_test(bb_list, bad))
90 fprintf (out, "%lu\n", bad);
92 errcode = ext2fs_badblocks_list_add (bb_list, bad);
94 com_err (program_name, errcode, "adding to in-memory bad block list");
99 increment the iteration through the bb_list if
100 an element was just added before the current iteration
101 position. This should not cause next_bad to change. */
102 if (bb_iter && bad < next_bad)
103 ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
107 static void print_status(void)
109 fprintf(stderr, "%9ld/%9ld", currently_testing, num_blocks);
110 fprintf(stderr, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
114 static void alarm_intr(int alnum)
116 signal (SIGALRM, alarm_intr);
120 fprintf(stderr, "%9ld/%9ld", currently_testing, num_blocks);
121 fprintf(stderr, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
125 static void *terminate_addr = NULL;
127 static void terminate_intr(int signo)
130 longjmp(terminate_addr,1);
134 static void capture_terminate(jmp_buf term_addr)
136 terminate_addr = term_addr;
137 signal (SIGHUP, terminate_intr);
138 signal (SIGINT, terminate_intr);
139 signal (SIGPIPE, terminate_intr);
140 signal (SIGTERM, terminate_intr);
141 signal (SIGUSR1, terminate_intr);
142 signal (SIGUSR2, terminate_intr);
145 static void uncapture_terminate(void)
147 terminate_addr = NULL;
148 signal (SIGHUP, SIG_DFL);
149 signal (SIGINT, SIG_DFL);
150 signal (SIGPIPE, SIG_DFL);
151 signal (SIGTERM, SIG_DFL);
152 signal (SIGUSR1, SIG_DFL);
153 signal (SIGUSR2, SIG_DFL);
157 * Perform a read of a sequence of blocks; return the number of blocks
158 * successfully sequentially read.
160 static long do_read (int dev, char * buffer, int try, int block_size,
161 unsigned long current_block)
168 /* Seek to the correct loc. */
169 if (ext2fs_llseek (dev, (ext2_loff_t) current_block * block_size,
170 SEEK_SET) != (ext2_loff_t) current_block * block_size)
171 com_err (program_name, errno, _("during seek"));
174 got = read (dev, buffer, try * block_size);
178 fprintf(stderr, _("Weird value (%ld) in do_read\n"), got);
184 * Perform a write of a sequence of blocks; return the number of blocks
185 * successfully sequentially written.
187 static long do_write (int dev, char * buffer, int try, int block_size,
188 unsigned long current_block)
195 /* Seek to the correct loc. */
196 if (ext2fs_llseek (dev, (ext2_loff_t) current_block * block_size,
197 SEEK_SET) != (ext2_loff_t) current_block * block_size)
198 com_err (program_name, errno, _("during seek"));
201 got = write (dev, buffer, try * block_size);
206 "Weird value (%ld) in do_write\n", got);
213 static void flush_bufs(void)
217 retval = ext2fs_sync_device(host_dev, 1);
219 com_err(program_name, retval, _("during ext2fs_sync_device"));
222 static unsigned int test_ro (int dev, unsigned long last_block,
223 int block_size, unsigned long from_count,
224 unsigned long blocks_at_once)
229 unsigned int bb_count = 0;
232 errcode = ext2fs_badblocks_list_iterate_begin(bb_list,&bb_iter);
234 com_err (program_name, errcode,
235 _("while beginning bad block list iteration"));
239 ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
240 } while (next_bad && next_bad < from_count);
242 blkbuf = malloc (blocks_at_once * block_size);
245 com_err (program_name, ENOMEM, _("while allocating buffers"));
250 fprintf(stderr, _("Checking for bad blocks in read-only mode\n"));
251 fprintf (stderr, _("From block %lu to %lu\n"), from_count,
254 try = blocks_at_once;
255 currently_testing = from_count;
256 num_blocks = last_block;
257 if (s_flag || v_flag > 1) {
259 _("Checking for bad blocks (read-only test): "));
263 while (currently_testing < last_block)
266 if (currently_testing == next_bad) {
267 /* fprintf (out, "%lu\n", nextbad); */
268 ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
272 else if (currently_testing + try > next_bad)
273 try = next_bad - currently_testing;
275 if (currently_testing + try > last_block)
276 try = last_block - currently_testing;
277 got = do_read (dev, blkbuf, try, block_size, currently_testing);
278 currently_testing += got;
280 try = blocks_at_once;
286 bb_count += bb_output(currently_testing++);
291 if (s_flag || v_flag > 1)
292 fprintf(stderr, _(done_string));
297 ext2fs_badblocks_list_iterate_end(bb_iter);
302 static unsigned int test_rw (int dev, unsigned long last_block,
303 int block_size, unsigned long from_count,
304 unsigned long blocks_at_once)
308 static unsigned char pattern[] = {0xaa, 0x55, 0xff, 0x00};
309 unsigned int bb_count = 0;
311 buffer = malloc (2 * block_size);
314 com_err (program_name, ENOMEM, _("while allocating buffers"));
322 _("Checking for bad blocks in read-write mode\n"));
323 fprintf(stderr, _("From block %lu to %lu\n"),
324 from_count, last_block);
326 for (i = 0; i < sizeof (pattern); i++) {
327 memset (buffer, pattern[i], block_size);
329 fprintf (stderr, _("Writing pattern 0x%08x: "),
331 num_blocks = last_block;
332 currently_testing = from_count;
333 if (s_flag && v_flag <= 1)
336 currently_testing < last_block;
339 if (ext2fs_llseek (dev, (ext2_loff_t) currently_testing *
340 block_size, SEEK_SET) !=
341 (ext2_loff_t) currently_testing * block_size)
342 com_err (program_name, errno,
343 _("during seek on block %d"),
347 write (dev, buffer, block_size);
352 fprintf(stderr, _(done_string));
355 fprintf (stderr, _("Reading and comparing: "));
356 num_blocks = last_block;
357 currently_testing = from_count;
358 if (s_flag && v_flag <= 1)
361 currently_testing < last_block;
364 if (ext2fs_llseek (dev, (ext2_loff_t) currently_testing *
365 block_size, SEEK_SET) !=
366 (ext2_loff_t) currently_testing * block_size)
367 com_err (program_name, errno,
368 _("during seek on block %d"),
372 if ((read (dev, buffer + block_size, block_size)
374 memcmp(buffer, buffer + block_size, block_size))
375 bb_count += bb_output(currently_testing);
380 fprintf(stderr, _(done_string));
387 struct saved_blk_record {
392 static unsigned int test_nd (int dev, unsigned long last_block,
393 int block_size, unsigned long from_count,
394 unsigned long blocks_at_once)
396 char *blkbuf, *save_ptr, *test_ptr, *read_ptr;
399 long got, used2, written, save_currently_testing;
400 struct saved_blk_record *test_record;
401 /* This is static to prevent being clobbered by the longjmp */
402 static int num_saved;
403 jmp_buf terminate_env;
406 unsigned int bb_count;
408 errcode = ext2fs_badblocks_list_iterate_begin(bb_list,&bb_iter);
410 com_err (program_name, errcode,
411 _("while beginning bad block list iteration"));
415 ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
416 } while (next_bad && next_bad < from_count);
418 blkbuf = malloc (3 * blocks_at_once * block_size);
419 test_record = malloc (blocks_at_once*sizeof(struct saved_blk_record));
420 if (!blkbuf || !test_record) {
421 com_err(program_name, ENOMEM, _("while allocating buffers"));
426 /* inititalize the test data randomly: */
428 fprintf (stderr, _("Initializing random test data\n"));
430 for(ptr = blkbuf + blocks_at_once * block_size;
431 ptr < blkbuf + 2 * blocks_at_once * block_size;
433 (*ptr) = random() % (1 << sizeof(char));
439 _("Checking for bad blocks in non-destructive read-write mode\n"));
440 fprintf (stderr, _("From block %lu to %lu\n"), from_count, last_block);
442 if (s_flag || v_flag > 1) {
443 fprintf(stderr, _("Checking for bad blocks (non-destructive read-write test): "));
447 if (setjmp(terminate_env)) {
449 * Abnormal termination by a signal is handled here.
451 signal (SIGALRM, SIG_IGN);
452 fprintf(stderr, _("\nInterrupt caught, cleaning up\n"));
455 for (i=0; i < num_saved; i++) {
456 do_write(dev, save_ptr, test_record[i].num,
457 block_size, test_record[i].block);
458 save_ptr += test_record[i].num * block_size;
464 /* set up abend handler */
465 capture_terminate(terminate_env);
470 test_ptr = blkbuf + (blocks_at_once * block_size);
471 currently_testing = from_count;
472 num_blocks = last_block;
474 while (currently_testing < last_block) {
475 try = blocks_at_once - buf_used;
477 if (currently_testing == next_bad) {
478 /* fprintf (out, "%lu\n", nextbad); */
479 ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
483 else if (currently_testing + try > next_bad)
484 try = next_bad - currently_testing;
486 if (currently_testing + try > last_block)
487 try = last_block - currently_testing;
488 got = do_read (dev, save_ptr, try, block_size,
491 /* First block must have been bad. */
492 bb_count += bb_output(currently_testing++);
497 * Note the fact that we've saved this much data
498 * *before* we overwrite it with test data
500 test_record[num_saved].block = currently_testing;
501 test_record[num_saved].num = got;
504 /* Write the test data */
505 written = do_write (dev, test_ptr, got, block_size,
508 com_err (program_name, errno,
509 _("during test data write, block %lu"),
510 currently_testing + written);
513 save_ptr += got * block_size;
514 test_ptr += got * block_size;
515 currently_testing += got;
517 bb_count += bb_output(currently_testing++);
521 * If there's room for more blocks to be tested this
522 * around, and we're not done yet testing the disk, go
523 * back and get some more blocks.
525 if ((buf_used != blocks_at_once) &&
526 (currently_testing < last_block))
530 save_currently_testing = currently_testing;
533 * for each contiguous block that we read into the
534 * buffer (and wrote test data into afterwards), read
535 * it back (looping if necessary, to get past newly
536 * discovered unreadable blocks, of which there should
537 * be none, but with a hard drive which is unreliable,
538 * it has happened), and compare with the test data
539 * that was written; output to the bad block list if
544 test_ptr = blkbuf + (blocks_at_once * block_size);
545 read_ptr = blkbuf + (2 * blocks_at_once * block_size);
550 if (used2 >= num_saved)
552 currently_testing = test_record[used2].block;
553 try = test_record[used2].num;
557 got = do_read (dev, read_ptr, try,
558 block_size, currently_testing);
560 /* test the comparison between all the
561 blocks successfully read */
562 for (i = 0; i < got; ++i)
563 if (memcmp (test_ptr+i*block_size,
564 read_ptr+i*block_size, block_size))
565 bb_count += bb_output(currently_testing + i);
567 bb_count += bb_output(currently_testing + got);
571 /* when done, write back original data */
572 do_write (dev, save_ptr, got, block_size,
575 currently_testing += got;
576 save_ptr += got * block_size;
577 test_ptr += got * block_size;
578 read_ptr += got * block_size;
582 /* empty the buffer so it can be reused */
586 test_ptr = blkbuf + (blocks_at_once * block_size);
587 currently_testing = save_currently_testing;
591 uncapture_terminate();
592 if (s_flag || v_flag > 1)
593 fprintf(stderr, _(done_string));
599 ext2fs_badblocks_list_iterate_end(bb_iter);
604 static void check_mount(char *device_name)
609 retval = ext2fs_check_if_mounted(device_name, &mount_flags);
611 com_err("ext2fs_check_if_mount", retval,
612 _("while determining whether %s is mounted."),
616 if (!(mount_flags & EXT2_MF_MOUNTED))
619 fprintf(stderr, _("%s is mounted; "), device_name);
621 fprintf(stderr, _("badblocks forced anyway. "
622 "Hope /etc/mtab is incorrect.\n"));
625 fprintf(stderr, _("it's not safe to run badblocks!\n"));
630 int main (int argc, char ** argv)
635 char * host_device_name = NULL;
636 char * input_file = NULL;
637 char * output_file = NULL;
639 int block_size = 1024;
640 unsigned long blocks_at_once = 16;
641 blk_t last_block, from_count;
643 int passes_clean = 0;
646 unsigned int (*test_func)(int, unsigned long,
650 setbuf(stdout, NULL);
651 setbuf(stderr, NULL);
653 setlocale(LC_MESSAGES, "");
654 bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
655 textdomain(NLS_CAT_NAME);
660 program_name = *argv;
661 while ((c = getopt (argc, argv, "b:fi:o:svwnc:p:h:")) != EOF) {
664 block_size = strtoul (optarg, &tmp, 0);
665 if (*tmp || block_size > 4096) {
666 com_err (program_name, 0,
667 _("bad block size - %s"), optarg);
678 output_file = optarg;
699 blocks_at_once = strtoul (optarg, &tmp, 0);
701 com_err (program_name, 0,
702 "bad simultaneous block count - %s", optarg);
707 num_passes = strtoul (optarg, &tmp, 0);
709 com_err (program_name, 0,
710 "bad number of clean passes - %s", optarg);
715 host_device_name = optarg;
721 if (optind > argc - 1)
723 device_name = argv[optind++];
724 if (optind > argc - 1) {
725 errcode = ext2fs_get_device_size(device_name,
728 if (errcode == EXT2_ET_UNIMPLEMENTED) {
729 com_err(program_name, 0,
730 _("Couldn't determine device size; you "
731 "must specify\nthe size manually\n"));
735 com_err(program_name, errcode,
736 _("while trying to determine device size"));
740 last_block = strtoul (argv[optind], &tmp, 0);
742 com_err (program_name, 0, _("bad blocks count - %s"),
748 if (optind <= argc-1) {
749 from_count = strtoul (argv[optind], &tmp, 0);
751 com_err (program_name, 0, _("bad starting block - %s"),
755 } else from_count = 0;
756 if (from_count >= last_block) {
757 com_err (program_name, 0, _("bad blocks range: %lu-%lu"),
758 from_count, last_block);
762 check_mount(device_name);
764 dev = open (device_name, w_flag ? O_RDWR : O_RDONLY);
767 com_err (program_name, errno, _("while trying to open %s"),
771 if (host_device_name) {
772 host_dev = open (host_device_name, O_RDONLY);
775 com_err (program_name, errno,
776 _("while trying to open %s"),
783 if (strcmp (input_file, "-") == 0)
786 in = fopen (input_file, "r");
789 com_err (program_name, errno,
790 _("while trying to open %s"),
795 if (output_file && strcmp (output_file, "-") != 0)
797 out = fopen (output_file, "w");
800 com_err (program_name, errno,
801 _("while trying to open %s"),
809 errcode = ext2fs_badblocks_list_create(&bb_list,0);
811 com_err (program_name, errcode,
812 _("creating in-memory bad blocks list"));
818 switch(fscanf (in, "%u\n", &next_bad)) {
820 com_err (program_name, 0, "input file - bad format");
825 errcode = ext2fs_badblocks_list_add(bb_list,next_bad);
827 com_err (program_name, errcode, _("adding to in-memory bad block list"));
840 unsigned int bb_count;
842 bb_count = test_func(dev, last_block, block_size,
843 from_count, blocks_at_once);
851 _("Pass completed, %u bad blocks found.\n"),
854 } while (passes_clean < num_passes);