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)
44 #include <sys/ioctl.h>
45 #include <sys/types.h>
51 #include "et/com_err.h"
52 #include "ext2fs/ext2_io.h"
53 #include <linux/ext2_fs.h>
54 #include "ext2fs/ext2fs.h"
56 const char * program_name = "badblocks";
57 const char * done_string = "done \n";
59 int v_flag = 0; /* verbose */
60 int w_flag = 0; /* do r/w test: 0=no, 1=yes, 2=non-destructive */
61 int s_flag = 0; /* show progress of test */
63 static void usage(void)
65 fprintf (stderr, "Usage: %s [-b block_size] [-i input_file] [-o output_file] [-svwn]\n [-c blocks_at_once] [-p num_passes] device blocks_count [start_count]\n",
70 static unsigned long currently_testing = 0;
71 static unsigned long num_blocks = 0;
72 static ext2_badblocks_list bb_list = NULL;
74 static blk_t next_bad = 0;
75 static ext2_badblocks_iterate bb_iter = NULL;
78 * This routine reports a new bad block. If the bad block has already
79 * been seen before, then it returns 0; otherwise it returns 1.
81 static int bb_output (unsigned long bad)
85 if (ext2fs_badblocks_list_test(bb_list, bad))
88 fprintf (out, "%lu\n", bad);
90 errcode = ext2fs_badblocks_list_add (bb_list, bad);
92 com_err (program_name, errcode, "adding to in-memory bad block list");
97 increment the iteration through the bb_list if
98 an element was just added before the current iteration
99 position. This should not cause next_bad to change. */
100 if (bb_iter && bad < next_bad)
101 ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
105 static void print_status (void)
107 fprintf(stderr, "%9ld/%9ld", currently_testing, num_blocks);
108 fprintf(stderr, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
112 static void alarm_intr (int alnum)
114 signal (SIGALRM, alarm_intr);
118 fprintf(stderr, "%9ld/%9ld", currently_testing, num_blocks);
119 fprintf(stderr, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
123 static void *terminate_addr = NULL;
125 static void terminate_intr (int signo)
128 longjmp(terminate_addr,1);
132 static void capture_terminate (jmp_buf term_addr)
134 terminate_addr = term_addr;
135 signal (SIGHUP, terminate_intr);
136 signal (SIGINT, terminate_intr);
137 signal (SIGPIPE, terminate_intr);
138 signal (SIGTERM, terminate_intr);
139 signal (SIGUSR1, terminate_intr);
140 signal (SIGUSR2, terminate_intr);
144 * Perform a read of a sequence of blocks; return the number of blocks
145 * successfully sequentially read.
147 static long do_read (int dev, char * buffer, int try, int block_size,
148 unsigned long current_block)
155 /* Seek to the correct loc. */
156 if (ext2fs_llseek (dev, (ext2_loff_t) current_block * block_size,
157 SEEK_SET) != (ext2_loff_t) current_block * block_size)
158 com_err (program_name, errno, "during seek");
161 got = read (dev, buffer, try * block_size);
166 "Weird value (%ld) in do_read\n", got);
172 * Perform a write of a sequence of blocks; return the number of blocks
173 * successfully sequentially written.
175 static long do_write (int dev, char * buffer, int try, int block_size,
176 unsigned long current_block)
183 /* Seek to the correct loc. */
184 if (ext2fs_llseek (dev, (ext2_loff_t) current_block * block_size,
185 SEEK_SET) != (ext2_loff_t) current_block * block_size)
186 com_err (program_name, errno, "during seek");
189 got = write (dev, buffer, try * block_size);
194 "Weird value (%ld) in do_write\n", got);
201 static void flush_bufs (int dev, int sync)
204 #if !defined (BLKFLSBUF) && !defined (FDFLUSH)
208 fprintf (stderr, "Flushing buffers\n");
210 if (sync && fdatasync (dev) == -1)
211 com_err (program_name, errno, "during fsync");
214 ioctl (host_dev, BLKFLSBUF, 0); /* In case this is a HD */
217 ioctl (host_dev, FDFLUSH, 0); /* In case this is floppy */
221 static unsigned int test_ro (int dev, unsigned long blocks_count,
222 int block_size, unsigned long from_count,
223 unsigned long blocks_at_once)
228 unsigned int bb_count = 0;
231 errcode = ext2fs_badblocks_list_iterate_begin(bb_list,&bb_iter);
233 com_err (program_name, errcode, "while beginning bad block list iteration");
237 ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
238 } while (next_bad && next_bad < from_count);
240 blkbuf = malloc (blocks_at_once * block_size);
243 com_err (program_name, ENOMEM, "while allocating buffers");
249 "Checking for bad blocks in read-only mode\n");
250 fprintf (stderr, "From block %lu to %lu\n", from_count, blocks_count);
252 try = blocks_at_once;
253 currently_testing = from_count;
254 num_blocks = blocks_count;
255 if (s_flag || v_flag > 1) {
256 fprintf(stderr, "Checking for bad blocks (read-only test): ");
260 while (currently_testing < blocks_count)
263 if (currently_testing == next_bad) {
264 /* fprintf (out, "%lu\n", nextbad); */
265 ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
269 else if (currently_testing + try > next_bad)
270 try = next_bad - currently_testing;
272 if (currently_testing + try > blocks_count)
273 try = blocks_count - currently_testing;
274 got = do_read (dev, blkbuf, try, block_size, currently_testing);
275 currently_testing += got;
277 try = blocks_at_once;
283 bb_count += bb_output(currently_testing++);
288 if (s_flag || v_flag > 1)
289 fprintf(stderr, done_string);
294 ext2fs_badblocks_list_iterate_end(bb_iter);
299 static unsigned int test_rw (int dev, unsigned long blocks_count,
300 int block_size, unsigned long from_count,
301 unsigned long blocks_at_once)
305 unsigned char pattern[] = {0xaa, 0x55, 0xff, 0x00};
306 unsigned int bb_count = 0;
308 buffer = malloc (2 * block_size);
311 com_err (program_name, ENOMEM, "while allocating buffers");
319 "Checking for bad blocks in read-write mode\n");
320 fprintf(stderr, "From block %lu to %lu\n",
321 from_count, blocks_count);
323 for (i = 0; i < sizeof (pattern); i++) {
324 memset (buffer, pattern[i], block_size);
326 fprintf (stderr, "Writing pattern 0x%08x: ",
328 num_blocks = blocks_count;
329 currently_testing = from_count;
330 if (s_flag && v_flag <= 1)
333 currently_testing < blocks_count;
336 if (ext2fs_llseek (dev, (ext2_loff_t) currently_testing *
337 block_size, SEEK_SET) !=
338 (ext2_loff_t) currently_testing * block_size)
339 com_err (program_name, errno,
340 "during seek on block %d",
344 write (dev, buffer, block_size);
349 fprintf(stderr, done_string);
352 fprintf (stderr, "Reading and comparing: ");
353 num_blocks = blocks_count;
354 currently_testing = from_count;
355 if (s_flag && v_flag <= 1)
358 currently_testing < blocks_count;
361 if (ext2fs_llseek (dev, (ext2_loff_t) currently_testing *
362 block_size, SEEK_SET) !=
363 (ext2_loff_t) currently_testing * block_size)
364 com_err (program_name, errno,
365 "during seek on block %d",
369 if ((read (dev, buffer + block_size, block_size)
371 memcmp(buffer, buffer + block_size, block_size))
372 bb_count = bb_output(currently_testing);
377 fprintf(stderr, done_string);
384 static unsigned int test_nd (int dev, unsigned long blocks_count,
385 int block_size, unsigned long from_count,
386 unsigned long blocks_at_once)
388 char *blkbuf, *save_ptr, *test_ptr, *read_ptr;
392 unsigned long *bufblk;
393 unsigned long *bufblks;
394 jmp_buf terminate_env;
396 /* These are static to prevent being clobbered by the longjmp */
397 static long buf_used;
398 static unsigned int bb_count;
403 errcode = ext2fs_badblocks_list_iterate_begin(bb_list,&bb_iter);
405 com_err (program_name, errcode,
406 "while beginning bad block list iteration");
410 ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
411 } while (next_bad && next_bad < from_count);
413 blkbuf = malloc (3 * blocks_at_once * block_size);
414 bufblk = malloc (blocks_at_once * sizeof(unsigned long));
415 bufblks = malloc (blocks_at_once * sizeof(unsigned long));
416 if (!blkbuf || !bufblk || !bufblks) {
417 com_err (program_name, ENOMEM, "while allocating buffers");
421 /* inititalize the test data randomly: */
423 fprintf (stderr, "Initializing random test data\n");
425 for(ptr = blkbuf + blocks_at_once * block_size;
426 ptr < blkbuf + 2 * blocks_at_once * block_size;
428 (*ptr) = random() % (1 << sizeof(char));
434 "Checking for bad blocks in non-destructive read-write mode\n");
435 fprintf (stderr, "From block %lu to %lu\n", from_count, blocks_count);
437 if (s_flag || v_flag > 1) {
438 fprintf (stderr, "Checking for bad blocks (non-destructive read-write test): ");
442 if (! setjmp(terminate_env)) {
443 /* set up abend handler */
444 capture_terminate(terminate_env);
446 buf_used = 0; save_ptr = blkbuf;
447 test_ptr = blkbuf + (blocks_at_once * block_size);
448 currently_testing = from_count;
449 num_blocks = blocks_count;
451 while (currently_testing < blocks_count) {
452 try = blocks_at_once - buf_used;
454 if (currently_testing == next_bad) {
455 /* fprintf (out, "%lu\n", nextbad); */
456 ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
457 bufblk[buf_used] = currently_testing++;
460 else if (currently_testing + try > next_bad)
461 try = next_bad - currently_testing;
463 if (currently_testing + try > blocks_count)
464 try = blocks_count - currently_testing;
465 got = do_read (dev, save_ptr, try, block_size,
468 /* if reading succeeded, write the test data */
472 written = do_write (dev, test_ptr, got,
476 com_err (program_name, errno,
477 "during test data write, block %lu",
478 currently_testing + written);
481 bufblk[buf_used] = currently_testing;
482 bufblks[buf_used] = got;
484 save_ptr += got * block_size;
485 test_ptr += got * block_size;
486 currently_testing += got;
488 bb_count += bb_output(currently_testing++);
492 * If there's room for more blocks to be
493 * tested this around, and we're not done yet
494 * testing the disk, go back and get some
497 if ((buf_used != blocks_at_once) &&
498 (currently_testing != blocks_count))
504 * for each contiguous block that we read into
505 * the buffer (and wrote test data into
506 * afterwards), read it back (looping if
507 * necessary, to get past newly discovered
508 * unreadable blocks, of which there should be
509 * none, but with a hard drive which is
510 * unreliable, it has happened), and compare
511 * with the test data that was written; output
512 * to the bad block list if it doesn't match.
516 test_ptr = blkbuf + (blocks_at_once * block_size);
517 read_ptr = blkbuf + (2 * blocks_at_once * block_size);
518 currently_testing = bufblk[0];
520 while (currently_testing < blocks_count) {
522 got = do_read (dev, read_ptr, try,
523 block_size, currently_testing);
525 /* test the comparison between all the
526 blocks successfully read */
527 for (i = 0; i < got; ++i)
528 if (memcmp (test_ptr, read_ptr,
530 bb_count += bb_output(currently_testing + i);
532 bb_count += bb_output(currently_testing);
536 /* when done, write back original data */
537 do_write (dev, save_ptr, got, block_size,
540 currently_testing += got;
541 save_ptr += bufblks[got] * block_size;
542 test_ptr += bufblks[got] * block_size;
543 read_ptr += bufblks[got] * block_size;
547 used2 += bufblks[used2];
548 if (used2 >= blocks_at_once)
550 currently_testing = bufblk[used2];
551 try = bufblks[used2];
555 /* empty the buffer so it can be reused */
560 if (s_flag || v_flag > 1)
561 fprintf(stderr, "done \n");
564 /* abnormal termination by a signal is handled here */
565 /* logic is: write back the data that is in the buffer,
566 so that we can maintain data integrity on disk. Then
570 fprintf(stderr, "Interrupt caught, cleaning up\n");
572 for (buf_written = 0;
573 buf_written < buf_used;
574 buf_written += bufblks[buf_written])
575 do_write (dev, blkbuf + buf_written * block_size,
576 bufblks[buf_written], block_size,
577 bufblk[buf_written]);
588 ext2fs_badblocks_list_iterate_end(bb_iter);
593 int main (int argc, char ** argv)
598 char * host_device_name = NULL;
599 char * input_file = NULL;
600 char * output_file = NULL;
602 int block_size = 1024;
603 unsigned long blocks_at_once = 16;
604 unsigned long blocks_count, from_count;
606 int passes_clean = 0;
610 setbuf(stdout, NULL);
611 setbuf(stderr, NULL);
613 program_name = *argv;
614 while ((c = getopt (argc, argv, "b:i:o:svwnc:p:h:")) != EOF) {
617 block_size = strtoul (optarg, &tmp, 0);
618 if (*tmp || block_size > 4096) {
619 com_err (program_name, 0,
620 "bad block size - %s", optarg);
628 output_file = optarg;
644 blocks_at_once = strtoul (optarg, &tmp, 0);
646 com_err (program_name, 0,
647 "bad simultaneous block count - %s", optarg);
652 num_passes = strtoul (optarg, &tmp, 0);
654 com_err (program_name, 0,
655 "bad number of clean passes - %s", optarg);
660 host_device_name = optarg;
666 if (optind > argc - 1)
668 device_name = argv[optind++];
669 if (optind > argc - 1)
671 blocks_count = strtoul (argv[optind], &tmp, 0);
674 com_err (program_name, 0, "bad blocks count - %s", argv[optind]);
677 if (++optind <= argc-1) {
678 from_count = strtoul (argv[optind], &tmp, 0);
679 } else from_count = 0;
680 if (from_count >= blocks_count) {
681 com_err (program_name, 0, "bad blocks range: %lu-%lu",
682 from_count, blocks_count);
685 dev = open (device_name, w_flag ? O_RDWR : O_RDONLY);
688 com_err (program_name, errno, "while trying to open %s",
692 if (host_device_name) {
693 host_dev = open (host_device_name, O_RDONLY);
696 com_err (program_name, errno, "while trying to open %s",
703 if (strcmp (input_file, "-") == 0)
706 in = fopen (input_file, "r");
709 com_err (program_name, errno,"while trying to open %s",
714 if (output_file && strcmp (output_file, "-") != 0)
716 out = fopen (output_file, "w");
719 com_err (program_name, errno,"while trying to open %s",
727 errcode = ext2fs_badblocks_list_create(&bb_list,0);
729 com_err (program_name, errcode, "creating in-memory bad blocks list");
735 switch(fscanf (in, "%lu\n", &next_bad)) {
737 com_err (program_name, 0, "input file - bad format");
742 errcode = ext2fs_badblocks_list_add(bb_list,next_bad);
744 com_err (program_name, errcode, "adding to in-memory bad block list");
757 unsigned int bb_count;
761 (w_flag == 2 ? test_nd
764 (dev, blocks_count, block_size, from_count,
766 ) ? passes_clean = 0 : ++passes_clean;
769 fprintf(stderr,"Pass completed, %u bad blocks found.\n", bb_count);
771 } while (passes_clean < num_passes);