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 */
71 static int exclusive_ok = 0;
75 int sys_page_size = 4096;
77 static void usage(void)
79 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"),
84 static void exclusive_usage(void)
87 _("%s: The -n and -w options are mutually exclusive.\n\n"),
92 static unsigned long currently_testing = 0;
93 static unsigned long num_blocks = 0;
94 static ext2_badblocks_list bb_list = NULL;
96 static blk_t next_bad = 0;
97 static ext2_badblocks_iterate bb_iter = NULL;
99 static void *allocate_buffer(size_t size)
103 #ifdef HAVE_POSIX_MEMALIGN
104 if (posix_memalign(&ret, sys_page_size, size) < 0)
108 ret = memalign(sys_page_size, size);
112 #endif /* HAVE_VALLOC */
113 #endif /* HAVE_MEMALIGN */
114 #endif /* HAVE_POSIX_MEMALIGN */
123 * This routine reports a new bad block. If the bad block has already
124 * been seen before, then it returns 0; otherwise it returns 1.
126 static int bb_output (unsigned long bad)
130 if (ext2fs_badblocks_list_test(bb_list, bad))
133 fprintf(out, "%lu\n", bad);
136 errcode = ext2fs_badblocks_list_add (bb_list, bad);
138 com_err (program_name, errcode, "adding to in-memory bad block list");
143 increment the iteration through the bb_list if
144 an element was just added before the current iteration
145 position. This should not cause next_bad to change. */
146 if (bb_iter && bad < next_bad)
147 ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
151 static void print_status(void)
153 fprintf(stderr, "%15ld/%15ld", currently_testing, num_blocks);
154 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);
158 static void alarm_intr(int alnum EXT2FS_ATTR((unused)))
160 signal (SIGALRM, alarm_intr);
167 static void *terminate_addr = NULL;
169 static void terminate_intr(int signo EXT2FS_ATTR((unused)))
172 longjmp(terminate_addr,1);
176 static void capture_terminate(jmp_buf term_addr)
178 terminate_addr = term_addr;
179 signal (SIGHUP, terminate_intr);
180 signal (SIGINT, terminate_intr);
181 signal (SIGPIPE, terminate_intr);
182 signal (SIGTERM, terminate_intr);
183 signal (SIGUSR1, terminate_intr);
184 signal (SIGUSR2, terminate_intr);
187 static void uncapture_terminate(void)
189 terminate_addr = NULL;
190 signal (SIGHUP, SIG_DFL);
191 signal (SIGINT, SIG_DFL);
192 signal (SIGPIPE, SIG_DFL);
193 signal (SIGTERM, SIG_DFL);
194 signal (SIGUSR1, SIG_DFL);
195 signal (SIGUSR2, SIG_DFL);
198 static void set_o_direct(int dev, unsigned char *buffer, size_t size,
199 unsigned long current_block)
202 int new_flag = O_DIRECT;
205 if ((((unsigned long) buffer & (sys_page_size - 1)) != 0) ||
206 ((size & (sys_page_size - 1)) != 0) ||
207 ((current_block & ((sys_page_size >> 9)-1)) != 0))
210 if (new_flag != current_O_DIRECT) {
211 /* printf("%s O_DIRECT\n", new_flag ? "Setting" : "Clearing"); */
212 flag = fcntl(dev, F_GETFL);
214 flag = (flag & ~O_DIRECT) | new_flag;
215 fcntl(dev, F_SETFL, flag);
217 current_O_DIRECT = new_flag;
223 static void pattern_fill(unsigned char *buffer, unsigned long pattern,
227 unsigned char bpattern[sizeof(pattern)], *ptr;
229 if (pattern == (unsigned long) ~0) {
230 for (ptr = buffer; ptr < buffer + n; ptr++) {
231 (*ptr) = random() % (1 << (8 * sizeof(char)));
234 fputs(_("Testing with random pattern: "), stderr);
237 for (i = 0; i < sizeof(bpattern); i++) {
240 bpattern[i] = pattern & 0xFF;
241 pattern = pattern >> 8;
244 for (ptr = buffer, i = nb; ptr < buffer + n; ptr++) {
251 if (s_flag | v_flag) {
252 fputs(_("Testing with pattern 0x"), stderr);
253 for (i = 0; i <= nb; i++)
254 fprintf(stderr, "%02x", buffer[i]);
261 * Perform a read of a sequence of blocks; return the number of blocks
262 * successfully sequentially read.
264 static long do_read (int dev, unsigned char * buffer, int try, int block_size,
265 unsigned long current_block)
269 set_o_direct(dev, buffer, try * block_size, current_block);
274 /* Seek to the correct loc. */
275 if (ext2fs_llseek (dev, (ext2_loff_t) current_block * block_size,
276 SEEK_SET) != (ext2_loff_t) current_block * block_size)
277 com_err (program_name, errno, _("during seek"));
280 got = read (dev, buffer, try * block_size);
284 fprintf(stderr, _("Weird value (%ld) in do_read\n"), got);
290 * Perform a write of a sequence of blocks; return the number of blocks
291 * successfully sequentially written.
293 static long do_write (int dev, unsigned char * buffer, int try, int block_size,
294 unsigned long current_block)
298 set_o_direct(dev, buffer, try * block_size, current_block);
303 /* Seek to the correct loc. */
304 if (ext2fs_llseek (dev, (ext2_loff_t) current_block * block_size,
305 SEEK_SET) != (ext2_loff_t) current_block * block_size)
306 com_err (program_name, errno, _("during seek"));
309 got = write (dev, buffer, try * block_size);
313 fprintf(stderr, "Weird value (%ld) in do_write\n", got);
320 static void flush_bufs(void)
324 retval = ext2fs_sync_device(host_dev, 1);
326 com_err(program_name, retval, _("during ext2fs_sync_device"));
329 static unsigned int test_ro (int dev, unsigned long last_block,
330 int block_size, unsigned long from_count,
331 unsigned long blocks_at_once)
333 unsigned char * blkbuf;
336 unsigned int bb_count = 0;
339 errcode = ext2fs_badblocks_list_iterate_begin(bb_list,&bb_iter);
341 com_err (program_name, errcode,
342 _("while beginning bad block list iteration"));
346 ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
347 } while (next_bad && next_bad < from_count);
350 blkbuf = allocate_buffer((blocks_at_once + 1) * block_size);
352 blkbuf = allocate_buffer(blocks_at_once * block_size);
356 com_err (program_name, ENOMEM, _("while allocating buffers"));
360 fprintf (stderr, _("Checking blocks %lu to %lu\n"), from_count,
364 fputs(_("Checking for bad blocks in read-only mode\n"), stderr);
365 pattern_fill(blkbuf + blocks_at_once * block_size,
366 t_patts[0], block_size);
369 try = blocks_at_once;
370 currently_testing = from_count;
371 num_blocks = last_block - 1;
372 if (!t_flag && (s_flag || v_flag)) {
373 fputs(_("Checking for bad blocks (read-only test): "), stderr);
377 while (currently_testing < last_block)
380 if (currently_testing == next_bad) {
381 /* fprintf (out, "%lu\n", nextbad); */
382 ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
386 else if (currently_testing + try > next_bad)
387 try = next_bad - currently_testing;
389 if (currently_testing + try > last_block)
390 try = last_block - currently_testing;
391 got = do_read (dev, blkbuf, try, block_size, currently_testing);
393 /* test the comparison between all the
394 blocks successfully read */
396 for (i = 0; i < got; ++i)
397 if (memcmp (blkbuf+i*block_size,
398 blkbuf+blocks_at_once*block_size,
400 bb_count += bb_output(currently_testing + i);
402 currently_testing += got;
404 try = blocks_at_once;
405 /* recover page-aligned offset for O_DIRECT */
406 if ( blocks_at_once >= (unsigned long) (sys_page_size >> 9)
407 && (currently_testing % (sys_page_size >> 9)!= 0))
408 try -= (sys_page_size >> 9)
410 % (sys_page_size >> 9));
416 bb_count += bb_output(currently_testing++);
421 if (s_flag || v_flag)
422 fputs(_(done_string), stderr);
427 ext2fs_badblocks_list_iterate_end(bb_iter);
432 static unsigned int test_rw (int dev, unsigned long last_block,
433 int block_size, unsigned long from_count,
434 unsigned long blocks_at_once)
436 unsigned char *buffer, *read_buffer;
437 const unsigned long patterns[] = {0xaa, 0x55, 0xff, 0x00};
438 const unsigned long *pattern;
439 int i, try, got, nr_pattern, pat_idx;
440 unsigned int bb_count = 0;
442 buffer = allocate_buffer(2 * blocks_at_once * block_size);
443 read_buffer = buffer + blocks_at_once * block_size;
446 com_err (program_name, ENOMEM, _("while allocating buffers"));
453 fputs(_("Checking for bad blocks in read-write mode\n"),
455 fprintf(stderr, _("From block %lu to %lu\n"),
456 from_count, last_block);
463 nr_pattern = sizeof(patterns) / sizeof(patterns[0]);
465 for (pat_idx = 0; pat_idx < nr_pattern; pat_idx++) {
466 pattern_fill(buffer, pattern[pat_idx],
467 blocks_at_once * block_size);
468 num_blocks = last_block - 1;
469 currently_testing = from_count;
470 if (s_flag && v_flag <= 1)
473 try = blocks_at_once;
474 while (currently_testing < last_block) {
475 if (currently_testing + try > last_block)
476 try = last_block - currently_testing;
477 got = do_write(dev, buffer, try, block_size,
482 currently_testing += got;
484 try = blocks_at_once;
485 /* recover page-aligned offset for O_DIRECT */
486 if ( blocks_at_once >= (unsigned long) (sys_page_size >> 9)
487 && (currently_testing %
488 (sys_page_size >> 9)!= 0))
489 try -= (sys_page_size >> 9)
491 % (sys_page_size >> 9));
496 bb_count += bb_output(currently_testing++);
503 fputs(_(done_string), stderr);
506 fputs(_("Reading and comparing: "), stderr);
507 num_blocks = last_block;
508 currently_testing = from_count;
509 if (s_flag && v_flag <= 1)
512 try = blocks_at_once;
513 while (currently_testing < last_block) {
514 if (currently_testing + try > last_block)
515 try = last_block - currently_testing;
516 got = do_read (dev, read_buffer, try, block_size,
519 bb_count += bb_output(currently_testing++);
522 for (i=0; i < got; i++) {
523 if (memcmp(read_buffer + i * block_size,
524 buffer + i * block_size,
526 bb_count += bb_output(currently_testing+i);
528 currently_testing += got;
529 /* recover page-aligned offset for O_DIRECT */
530 if ( blocks_at_once >= (unsigned long) (sys_page_size >> 9)
531 && (currently_testing % (sys_page_size >> 9)!= 0))
532 try = blocks_at_once - (sys_page_size >> 9)
534 % (sys_page_size >> 9));
536 try = blocks_at_once;
544 fputs(_(done_string), stderr);
547 uncapture_terminate();
552 struct saved_blk_record {
557 static unsigned int test_nd (int dev, unsigned long last_block,
558 int block_size, unsigned long from_count,
559 unsigned long blocks_at_once)
561 unsigned char *blkbuf, *save_ptr, *test_ptr, *read_ptr;
562 unsigned char *test_base, *save_base, *read_base;
564 const unsigned long patterns[] = { ~0 };
565 const unsigned long *pattern;
566 int nr_pattern, pat_idx;
567 long got, used2, written, save_currently_testing;
568 struct saved_blk_record *test_record;
569 /* This is static to prevent being clobbered by the longjmp */
570 static int num_saved;
571 jmp_buf terminate_env;
573 unsigned long buf_used;
574 static unsigned int bb_count;
577 errcode = ext2fs_badblocks_list_iterate_begin(bb_list,&bb_iter);
579 com_err (program_name, errcode,
580 _("while beginning bad block list iteration"));
584 ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
585 } while (next_bad && next_bad < from_count);
587 blkbuf = allocate_buffer(3 * blocks_at_once * block_size);
588 test_record = malloc (blocks_at_once*sizeof(struct saved_blk_record));
589 if (!blkbuf || !test_record) {
590 com_err(program_name, ENOMEM, _("while allocating buffers"));
595 test_base = blkbuf + (blocks_at_once * block_size);
596 read_base = blkbuf + (2 * blocks_at_once * block_size);
602 fputs(_("Checking for bad blocks in non-destructive read-write mode\n"), stderr);
603 fprintf (stderr, _("From block %lu to %lu\n"), from_count, last_block);
605 if (s_flag || v_flag > 1) {
606 fputs(_("Checking for bad blocks (non-destructive read-write test)\n"), stderr);
608 if (setjmp(terminate_env)) {
610 * Abnormal termination by a signal is handled here.
612 signal (SIGALRM, SIG_IGN);
613 fputs(_("\nInterrupt caught, cleaning up\n"), stderr);
615 save_ptr = save_base;
616 for (i=0; i < num_saved; i++) {
617 do_write(dev, save_ptr, test_record[i].num,
618 block_size, test_record[i].block);
619 save_ptr += test_record[i].num * block_size;
625 /* set up abend handler */
626 capture_terminate(terminate_env);
633 nr_pattern = sizeof(patterns) / sizeof(patterns[0]);
635 for (pat_idx = 0; pat_idx < nr_pattern; pat_idx++) {
636 pattern_fill(test_base, pattern[pat_idx],
637 blocks_at_once * block_size);
641 save_ptr = save_base;
642 test_ptr = test_base;
643 currently_testing = from_count;
644 num_blocks = last_block - 1;
645 if (s_flag && v_flag <= 1)
648 while (currently_testing < last_block) {
649 got = try = blocks_at_once - buf_used;
651 if (currently_testing == next_bad) {
652 /* fprintf (out, "%lu\n", nextbad); */
653 ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
657 else if (currently_testing + try > next_bad)
658 try = next_bad - currently_testing;
660 if (currently_testing + try > last_block)
661 try = last_block - currently_testing;
662 got = do_read (dev, save_ptr, try, block_size,
665 /* First block must have been bad. */
666 bb_count += bb_output(currently_testing++);
671 * Note the fact that we've saved this much data
672 * *before* we overwrite it with test data
674 test_record[num_saved].block = currently_testing;
675 test_record[num_saved].num = got;
678 /* Write the test data */
679 written = do_write (dev, test_ptr, got, block_size,
682 com_err (program_name, errno,
683 _("during test data write, block %lu"),
684 currently_testing + written);
687 save_ptr += got * block_size;
688 test_ptr += got * block_size;
689 currently_testing += got;
691 bb_count += bb_output(currently_testing++);
695 * If there's room for more blocks to be tested this
696 * around, and we're not done yet testing the disk, go
697 * back and get some more blocks.
699 if ((buf_used != blocks_at_once) &&
700 (currently_testing < last_block))
704 save_currently_testing = currently_testing;
707 * for each contiguous block that we read into the
708 * buffer (and wrote test data into afterwards), read
709 * it back (looping if necessary, to get past newly
710 * discovered unreadable blocks, of which there should
711 * be none, but with a hard drive which is unreliable,
712 * it has happened), and compare with the test data
713 * that was written; output to the bad block list if
717 save_ptr = save_base;
718 test_ptr = test_base;
719 read_ptr = read_base;
724 if (used2 >= num_saved)
726 currently_testing = test_record[used2].block;
727 try = test_record[used2].num;
731 got = do_read (dev, read_ptr, try,
732 block_size, currently_testing);
734 /* test the comparison between all the
735 blocks successfully read */
736 for (i = 0; i < got; ++i)
737 if (memcmp (test_ptr+i*block_size,
738 read_ptr+i*block_size, block_size))
739 bb_count += bb_output(currently_testing + i);
741 bb_count += bb_output(currently_testing + got);
745 /* write back original data */
746 do_write (dev, save_ptr, got,
747 block_size, currently_testing);
748 save_ptr += got * block_size;
750 currently_testing += got;
751 test_ptr += got * block_size;
752 read_ptr += got * block_size;
756 /* empty the buffer so it can be reused */
759 save_ptr = save_base;
760 test_ptr = test_base;
761 currently_testing = save_currently_testing;
765 if (s_flag || v_flag > 1)
766 fputs(_(done_string), stderr);
770 uncapture_terminate();
775 ext2fs_badblocks_list_iterate_end(bb_iter);
780 static void check_mount(char *device_name)
785 retval = ext2fs_check_if_mounted(device_name, &mount_flags);
787 com_err("ext2fs_check_if_mount", retval,
788 _("while determining whether %s is mounted."),
792 if (mount_flags & EXT2_MF_MOUNTED) {
793 fprintf(stderr, _("%s is mounted; "), device_name);
795 fputs(_("badblocks forced anyway. "
796 "Hope /etc/mtab is incorrect.\n"), stderr);
800 fputs(_("it's not safe to run badblocks!\n"), stderr);
804 if ((mount_flags & EXT2_MF_BUSY) && !exclusive_ok) {
805 fprintf(stderr, _("%s is apparently in use by the system; "),
808 fputs(_("badblocks forced anyway.\n"), stderr);
810 goto abort_badblocks;
816 int main (int argc, char ** argv)
821 char * host_device_name = NULL;
822 char * input_file = NULL;
823 char * output_file = NULL;
825 int block_size = 1024;
826 unsigned long blocks_at_once = 64;
827 blk_t last_block, from_count;
829 int passes_clean = 0;
832 unsigned long pattern;
833 unsigned int (*test_func)(int, unsigned long,
839 setbuf(stdout, NULL);
840 setbuf(stderr, NULL);
842 setlocale(LC_MESSAGES, "");
843 setlocale(LC_CTYPE, "");
844 bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
845 textdomain(NLS_CAT_NAME);
847 srandom((unsigned int)time(NULL)); /* simple randomness is enough */
850 /* Determine the system page size if possible */
852 #if (!defined(_SC_PAGESIZE) && defined(_SC_PAGE_SIZE))
853 #define _SC_PAGESIZE _SC_PAGE_SIZE
856 sysval = sysconf(_SC_PAGESIZE);
858 sys_page_size = sysval;
859 #endif /* _SC_PAGESIZE */
860 #endif /* HAVE_SYSCONF */
863 program_name = *argv;
864 while ((c = getopt (argc, argv, "b:fi:o:svwnc:p:h:t:X")) != EOF) {
867 block_size = strtoul (optarg, &tmp, 0);
868 if (*tmp || block_size > 4096) {
869 com_err (program_name, 0,
870 _("bad block size - %s"), optarg);
881 output_file = optarg;
902 blocks_at_once = strtoul (optarg, &tmp, 0);
904 com_err (program_name, 0,
905 "bad simultaneous block count - %s", optarg);
910 num_passes = strtoul (optarg, &tmp, 0);
912 com_err (program_name, 0,
913 "bad number of clean passes - %s", optarg);
918 host_device_name = optarg;
921 if (t_flag + 1 > t_max) {
922 unsigned long *t_patts_new;
924 t_patts_new = realloc(t_patts, t_max + T_INC);
926 com_err(program_name, ENOMEM,
927 _("can't allocate memory for "
928 "test_pattern - %s"),
932 t_patts = t_patts_new;
935 if (!strcmp(optarg, "r") || !strcmp(optarg,"random")) {
936 t_patts[t_flag++] = ~0;
938 pattern = strtoul(optarg, &tmp, 0);
940 com_err(program_name, 0,
941 _("invalid test_pattern: %s\n"),
945 if (pattern == (unsigned long) ~0)
947 t_patts[t_flag++] = pattern;
959 com_err(program_name, 0,
960 _("Maximum of one test_pattern may be specified "
961 "in read-only mode"));
964 if (t_patts && (t_patts[0] == (unsigned long) ~0)) {
965 com_err(program_name, 0,
966 _("Random test_pattern is not allowed "
967 "in read-only mode"));
971 if (optind > argc - 1)
973 device_name = argv[optind++];
974 if (optind > argc - 1) {
975 errcode = ext2fs_get_device_size(device_name,
978 if (errcode == EXT2_ET_UNIMPLEMENTED) {
979 com_err(program_name, 0,
980 _("Couldn't determine device size; you "
981 "must specify\nthe size manually\n"));
985 com_err(program_name, errcode,
986 _("while trying to determine device size"));
990 last_block = strtoul (argv[optind], &tmp, 0) + 1;
992 com_err (program_name, 0, _("invalid blocks count - %s"),
998 if (optind <= argc-1) {
999 from_count = strtoul (argv[optind], &tmp, 0);
1001 com_err (program_name, 0, _("invalid starting block - %s"),
1005 } else from_count = 0;
1006 if (from_count >= last_block) {
1007 com_err (program_name, 0, _("invalid blocks range: %lu-%lu"),
1008 (unsigned long) from_count, (unsigned long) last_block);
1012 check_mount(device_name);
1014 open_flag = w_flag ? O_RDWR : O_RDONLY;
1015 dev = open (device_name, open_flag);
1017 com_err (program_name, errno, _("while trying to open %s"),
1021 if (host_device_name) {
1022 host_dev = open (host_device_name, open_flag);
1023 if (host_dev == -1) {
1024 com_err (program_name, errno,
1025 _("while trying to open %s"),
1032 if (strcmp (input_file, "-") == 0)
1035 in = fopen (input_file, "r");
1038 com_err (program_name, errno,
1039 _("while trying to open %s"),
1045 if (output_file && strcmp (output_file, "-") != 0)
1047 out = fopen (output_file, "w");
1050 com_err (program_name, errno,
1051 _("while trying to open %s"),
1059 errcode = ext2fs_badblocks_list_create(&bb_list,0);
1061 com_err (program_name, errcode,
1062 _("while creating in-memory bad blocks list"));
1068 switch(fscanf (in, "%u\n", &next_bad)) {
1070 com_err (program_name, 0, "input file - bad format");
1075 errcode = ext2fs_badblocks_list_add(bb_list,next_bad);
1077 com_err (program_name, errcode, _("while adding to in-memory bad block list"));
1090 unsigned int bb_count;
1092 bb_count = test_func(dev, last_block, block_size,
1093 from_count, blocks_at_once);
1101 _("Pass completed, %u bad blocks found.\n"),
1104 } while (passes_clean < num_passes);