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)
32 #define _GNU_SOURCE /* for O_DIRECT */
50 #include <sys/ioctl.h>
51 #include <sys/types.h>
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"
59 const char * program_name = "badblocks";
60 const char * done_string = N_("done \n");
62 static int v_flag = 0; /* verbose */
63 static int w_flag = 0; /* do r/w test: 0=no, 1=yes,
64 * 2=non-destructive */
65 static int s_flag = 0; /* show progress of test */
66 static int force = 0; /* force check of mounted device */
67 static int t_flag = 0; /* number of test patterns */
68 static int t_max = 0; /* allocated test patterns */
69 static unsigned long *t_patts = NULL; /* test patterns */
70 static int current_O_DIRECT = 0; /* Current status of O_DIRECT flag */
74 int sys_page_size = 4096;
76 static void usage(void)
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"),
83 static unsigned long currently_testing = 0;
84 static unsigned long num_blocks = 0;
85 static ext2_badblocks_list bb_list = NULL;
87 static blk_t next_bad = 0;
88 static ext2_badblocks_iterate bb_iter = NULL;
90 static void *allocate_buffer(size_t size)
94 #ifdef HAVE_POSIX_MEMALIGN
95 if (posix_memalign(&ret, sys_page_size, size) < 0)
99 ret = memalign(sys_page_size, size);
103 #endif /* HAVE_VALLOC */
104 #endif /* HAVE_MEMALIGN */
105 #endif /* HAVE_POSIX_MEMALIGN */
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.
117 static int bb_output (unsigned long bad)
121 if (ext2fs_badblocks_list_test(bb_list, bad))
124 fprintf(out, "%lu\n", bad);
127 errcode = ext2fs_badblocks_list_add (bb_list, bad);
129 com_err (program_name, errcode, "adding to in-memory bad block list");
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);
142 static void print_status(void)
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");
149 static void alarm_intr(int alnum)
151 signal (SIGALRM, alarm_intr);
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");
160 static void *terminate_addr = NULL;
162 static void terminate_intr(int signo)
165 longjmp(terminate_addr,1);
169 static void capture_terminate(jmp_buf term_addr)
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);
180 static void uncapture_terminate(void)
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);
191 static void set_o_direct(int dev, unsigned char *buffer, size_t size)
194 int new_flag = O_DIRECT;
197 if ((((unsigned long) buffer & (sys_page_size - 1)) != 0) ||
198 ((size & (sys_page_size - 1)) != 0))
201 if (new_flag != current_O_DIRECT) {
202 printf("%s O_DIRECT\n", new_flag ? "Setting" : "Clearing");
203 flag = fcntl(dev, F_GETFL);
205 flag = (flag & ~O_DIRECT) | new_flag;
206 fcntl(dev, F_SETFL, flag);
208 current_O_DIRECT = new_flag;
214 static void pattern_fill(unsigned char *buffer, unsigned long pattern,
218 unsigned char bpattern[sizeof(pattern)], *ptr;
221 for (ptr = buffer; ptr < buffer + n; ptr++) {
222 (*ptr) = random() % (1 << (8 * sizeof(char)));
225 fprintf(stderr, _("Testing with random pattern: "));
228 for (i = 0; i < sizeof(bpattern); i++) {
231 bpattern[i] = pattern & 0xFF;
232 pattern = pattern >> 8;
235 for (ptr = buffer, i = nb; ptr < buffer + n; ptr++) {
236 *ptr = bpattern[i--];
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, ": ");
250 * Perform a read of a sequence of blocks; return the number of blocks
251 * successfully sequentially read.
253 static long do_read (int dev, unsigned char * buffer, int try, int block_size,
254 unsigned long current_block)
258 set_o_direct(dev, buffer, try * block_size);
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"));
269 got = read (dev, buffer, try * block_size);
273 fprintf(stderr, _("Weird value (%ld) in do_read\n"), got);
279 * Perform a write of a sequence of blocks; return the number of blocks
280 * successfully sequentially written.
282 static long do_write (int dev, unsigned char * buffer, int try, int block_size,
283 unsigned long current_block)
287 set_o_direct(dev, buffer, try * block_size);
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"));
298 got = write (dev, buffer, try * block_size);
303 "Weird value (%ld) in do_write\n", got);
310 static void flush_bufs(void)
314 retval = ext2fs_sync_device(host_dev, 1);
316 com_err(program_name, retval, _("during ext2fs_sync_device"));
319 static unsigned int test_ro (int dev, unsigned long last_block,
320 int block_size, unsigned long from_count,
321 unsigned long blocks_at_once)
323 unsigned char * blkbuf;
326 unsigned int bb_count = 0;
329 errcode = ext2fs_badblocks_list_iterate_begin(bb_list,&bb_iter);
331 com_err (program_name, errcode,
332 _("while beginning bad block list iteration"));
336 ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
337 } while (next_bad && next_bad < from_count);
340 blkbuf = allocate_buffer((blocks_at_once + 1) * block_size);
342 blkbuf = allocate_buffer(blocks_at_once * block_size);
346 com_err (program_name, ENOMEM, _("while allocating buffers"));
350 fprintf (stderr, _("Checking blocks %lu to %lu\n"), from_count,
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);
359 try = blocks_at_once;
360 currently_testing = from_count;
361 num_blocks = last_block;
362 if (!t_flag && (s_flag || v_flag)) {
364 _("Checking for bad blocks (read-only test): "));
368 while (currently_testing < last_block)
371 if (currently_testing == next_bad) {
372 /* fprintf (out, "%lu\n", nextbad); */
373 ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
377 else if (currently_testing + try > next_bad)
378 try = next_bad - currently_testing;
380 if (currently_testing + try > last_block)
381 try = last_block - currently_testing;
382 got = do_read (dev, blkbuf, try, block_size, currently_testing);
384 /* test the comparison between all the
385 blocks successfully read */
387 for (i = 0; i < got; ++i)
388 if (memcmp (blkbuf+i*block_size,
389 blkbuf+blocks_at_once*block_size,
391 bb_count += bb_output(currently_testing + i);
393 currently_testing += got;
395 try = blocks_at_once;
401 bb_count += bb_output(currently_testing++);
406 if (s_flag || v_flag)
407 fputs(done_string, stderr);
412 ext2fs_badblocks_list_iterate_end(bb_iter);
417 static unsigned int test_rw (int dev, unsigned long last_block,
418 int block_size, unsigned long from_count,
419 unsigned long blocks_at_once)
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;
427 buffer = allocate_buffer(2 * blocks_at_once * block_size);
428 read_buffer = buffer + blocks_at_once * block_size;
431 com_err (program_name, ENOMEM, _("while allocating buffers"));
439 _("Checking for bad blocks in read-write mode\n"));
440 fprintf(stderr, _("From block %lu to %lu\n"),
441 from_count, last_block);
448 nr_pattern = sizeof(patterns) / sizeof(patterns[0]);
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)
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,
467 currently_testing += got;
469 try = blocks_at_once;
474 bb_count += bb_output(currently_testing++);
481 fputs(done_string, stderr);
484 fprintf (stderr, _("Reading and comparing: "));
485 num_blocks = last_block;
486 currently_testing = from_count;
487 if (s_flag && v_flag <= 1)
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,
497 bb_count += bb_output(currently_testing++);
500 for (i=0; i < got; i++) {
501 if (memcmp(read_buffer + i * block_size,
502 buffer + i * block_size,
504 bb_count += bb_output(currently_testing+i);
506 currently_testing += got;
514 fputs(done_string, stderr);
517 uncapture_terminate();
522 struct saved_blk_record {
527 static unsigned int test_nd (int dev, unsigned long last_block,
528 int block_size, unsigned long from_count,
529 unsigned long blocks_at_once)
531 unsigned char *blkbuf, *save_ptr, *test_ptr, *read_ptr;
532 unsigned char *test_base, *save_base, *read_base;
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;
544 unsigned int bb_count = 0;
546 errcode = ext2fs_badblocks_list_iterate_begin(bb_list,&bb_iter);
548 com_err (program_name, errcode,
549 _("while beginning bad block list iteration"));
553 ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
554 } while (next_bad && next_bad < from_count);
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"));
564 test_base = blkbuf + (blocks_at_once * block_size);
565 read_base = blkbuf + (2 * blocks_at_once * block_size);
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);
575 if (s_flag || v_flag > 1) {
576 fprintf(stderr, _("Checking for bad blocks (non-destructive read-write test)\n"));
578 if (setjmp(terminate_env)) {
580 * Abnormal termination by a signal is handled here.
582 signal (SIGALRM, SIG_IGN);
583 fprintf(stderr, _("\nInterrupt caught, cleaning up\n"));
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;
595 /* set up abend handler */
596 capture_terminate(terminate_env);
603 nr_pattern = sizeof(patterns) / sizeof(patterns[0]);
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);
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)
618 while (currently_testing < last_block) {
619 got = try = blocks_at_once - buf_used;
621 if (currently_testing == next_bad) {
622 /* fprintf (out, "%lu\n", nextbad); */
623 ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
627 else if (currently_testing + try > next_bad)
628 try = next_bad - currently_testing;
630 if (currently_testing + try > last_block)
631 try = last_block - currently_testing;
632 got = do_read (dev, save_ptr, try, block_size,
635 /* First block must have been bad. */
636 bb_count += bb_output(currently_testing++);
641 * Note the fact that we've saved this much data
642 * *before* we overwrite it with test data
644 test_record[num_saved].block = currently_testing;
645 test_record[num_saved].num = got;
648 /* Write the test data */
649 written = do_write (dev, test_ptr, got, block_size,
652 com_err (program_name, errno,
653 _("during test data write, block %lu"),
654 currently_testing + written);
657 save_ptr += got * block_size;
658 test_ptr += got * block_size;
659 currently_testing += got;
661 bb_count += bb_output(currently_testing++);
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.
669 if ((buf_used != blocks_at_once) &&
670 (currently_testing < last_block))
674 save_currently_testing = currently_testing;
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
687 save_ptr = save_base;
688 test_ptr = test_base;
689 read_ptr = read_base;
694 if (used2 >= num_saved)
696 currently_testing = test_record[used2].block;
697 try = test_record[used2].num;
701 got = do_read (dev, read_ptr, try,
702 block_size, currently_testing);
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);
711 bb_count += bb_output(currently_testing + got);
715 /* write back original data */
716 do_write (dev, save_ptr, got,
717 block_size, currently_testing);
718 save_ptr += got * block_size;
720 currently_testing += got;
721 test_ptr += got * block_size;
722 read_ptr += got * block_size;
726 /* empty the buffer so it can be reused */
729 save_ptr = save_base;
730 test_ptr = test_base;
731 currently_testing = save_currently_testing;
735 if (s_flag || v_flag > 1)
736 fputs(done_string, stderr);
740 uncapture_terminate();
745 ext2fs_badblocks_list_iterate_end(bb_iter);
750 static void check_mount(char *device_name)
755 retval = ext2fs_check_if_mounted(device_name, &mount_flags);
757 com_err("ext2fs_check_if_mount", retval,
758 _("while determining whether %s is mounted."),
762 if (!(mount_flags & EXT2_MF_MOUNTED))
765 fprintf(stderr, _("%s is mounted; "), device_name);
767 fprintf(stderr, _("badblocks forced anyway. "
768 "Hope /etc/mtab is incorrect.\n"));
771 fprintf(stderr, _("it's not safe to run badblocks!\n"));
776 int main (int argc, char ** argv)
781 char * host_device_name = NULL;
782 char * input_file = NULL;
783 char * output_file = NULL;
785 int block_size = 1024;
786 unsigned long blocks_at_once = 16;
787 blk_t last_block, from_count;
789 int passes_clean = 0;
792 unsigned long pattern;
793 unsigned int (*test_func)(int, unsigned long,
799 setbuf(stdout, NULL);
800 setbuf(stderr, NULL);
802 setlocale(LC_MESSAGES, "");
803 setlocale(LC_CTYPE, "");
804 bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
805 textdomain(NLS_CAT_NAME);
807 srandom((unsigned int)time(NULL)); /* simple randomness is enough */
810 /* Determine the system page size if possible */
812 #if (!defined(_SC_PAGESIZE) && defined(_SC_PAGE_SIZE))
813 #define _SC_PAGESIZE _SC_PAGE_SIZE
816 sysval = sysconf(_SC_PAGESIZE);
818 sys_page_size = sysval;
819 #endif /* _SC_PAGESIZE */
820 #endif /* HAVE_SYSCONF */
823 program_name = *argv;
824 while ((c = getopt (argc, argv, "b:fi:o:svwnc:p:h:t:")) != EOF) {
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);
841 output_file = optarg;
862 blocks_at_once = strtoul (optarg, &tmp, 0);
864 com_err (program_name, 0,
865 "bad simultaneous block count - %s", optarg);
870 num_passes = strtoul (optarg, &tmp, 0);
872 com_err (program_name, 0,
873 "bad number of clean passes - %s", optarg);
878 host_device_name = optarg;
881 if (t_flag + 1 > t_max) {
882 unsigned long *t_patts_new;
884 t_patts_new = realloc(t_patts, t_max + T_INC);
886 com_err(program_name, ENOMEM,
887 _("can't allocate memory for "
888 "test_pattern - %s"),
892 t_patts = t_patts_new;
895 if (!strcmp(optarg, "r") || !strcmp(optarg,"random")) {
896 t_patts[t_flag++] = ~0;
898 pattern = strtoul(optarg, &tmp, 0);
900 com_err(program_name, 0,
901 _("invalid test_pattern: %s\n"),
907 t_patts[t_flag++] = pattern;
916 com_err(program_name, 0,
917 _("Maximum of one test_pattern may be specified "
918 "in read-only mode"));
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"));
928 if (optind > argc - 1)
930 device_name = argv[optind++];
931 if (optind > argc - 1) {
932 errcode = ext2fs_get_device_size(device_name,
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"));
942 com_err(program_name, errcode,
943 _("while trying to determine device size"));
947 last_block = strtoul (argv[optind], &tmp, 0);
949 com_err (program_name, 0, _("bad blocks count - %s"),
955 if (optind <= argc-1) {
956 from_count = strtoul (argv[optind], &tmp, 0);
958 com_err (program_name, 0, _("bad starting block - %s"),
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);
969 check_mount(device_name);
971 open_flag = w_flag ? O_RDWR : O_RDONLY;
972 dev = open (device_name, open_flag);
974 com_err (program_name, errno, _("while trying to open %s"),
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"),
989 if (strcmp (input_file, "-") == 0)
992 in = fopen (input_file, "r");
995 com_err (program_name, errno,
996 _("while trying to open %s"),
1002 if (output_file && strcmp (output_file, "-") != 0)
1004 out = fopen (output_file, "w");
1007 com_err (program_name, errno,
1008 _("while trying to open %s"),
1016 errcode = ext2fs_badblocks_list_create(&bb_list,0);
1018 com_err (program_name, errcode,
1019 _("creating in-memory bad blocks list"));
1025 switch(fscanf (in, "%u\n", &next_bad)) {
1027 com_err (program_name, 0, "input file - bad format");
1032 errcode = ext2fs_badblocks_list_add(bb_list,next_bad);
1034 com_err (program_name, errcode, _("adding to in-memory bad block list"));
1047 unsigned int bb_count;
1049 bb_count = test_func(dev, last_block, block_size,
1050 from_count, blocks_at_once);
1058 _("Pass completed, %u bad blocks found.\n"),
1061 } while (passes_clean < num_passes);