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 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 * and a whole host of other features.
40 #include <sys/ioctl.h>
41 #include <sys/types.h>
48 #include "et/com_err.h"
49 #include "ext2fs/ext2_io.h"
50 #include <linux/ext2_fs.h>
51 #include "ext2fs/ext2fs.h"
53 const char * program_name = "badblocks";
54 const char * done_string = "done \n";
56 int v_flag = 0; /* verbose */
57 int w_flag = 0; /* do r/w test: 0=no, 1=yes, 2=non-destructive */
58 int s_flag = 0; /* show progress of test */
60 static void usage(void)
62 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",
67 static unsigned long currently_testing = 0;
68 static unsigned long num_blocks = 0;
69 static ext2_badblocks_list bb_list = NULL;
71 static blk_t next_bad = 0;
72 static ext2_badblocks_iterate bb_iter = NULL;
74 static void bb_output (unsigned long bad)
78 fprintf (out, "%lu\n", bad);
80 errcode = ext2fs_badblocks_list_add (bb_list, bad);
82 com_err (program_name, errcode, "adding to in-memory bad block list");
87 increment the iteration through the bb_list if
88 an element was just added before the current iteration
89 position. This should not cause next_bad to change. */
90 if (bb_iter && bad < next_bad)
91 ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
94 static void print_status (void)
96 fprintf(stderr, "%9ld/%9ld", currently_testing, num_blocks);
97 fprintf(stderr, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
101 static void alarm_intr (int alnum)
103 signal (SIGALRM, alarm_intr);
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 *terminate_addr = NULL;
114 static void terminate_intr (int signo)
117 longjmp(terminate_addr,1);
121 static void capture_terminate (jmp_buf term_addr)
123 terminate_addr = term_addr;
124 signal (SIGHUP, terminate_intr);
125 signal (SIGINT, terminate_intr);
126 signal (SIGPIPE, terminate_intr);
127 signal (SIGTERM, terminate_intr);
128 signal (SIGUSR1, terminate_intr);
129 signal (SIGUSR2, terminate_intr);
133 * Perform a read of a sequence of blocks; return the number of blocks
134 * successfully sequentially read.
136 static long do_read (int dev, char * buffer, int try, unsigned long block_size,
137 unsigned long current_block)
144 /* Seek to the correct loc. */
145 if (ext2fs_llseek (dev, (ext2_loff_t) current_block * block_size,
146 SEEK_SET) != (ext2_loff_t) current_block * block_size)
147 com_err (program_name, errno, "during seek");
150 got = read (dev, buffer, try * block_size);
155 "Weird value (%ld) in do_read\n", got);
161 * Perform a write of a sequence of blocks; return the number of blocks
162 * successfully sequentially written.
164 static long do_write (int dev, char * buffer, int try, unsigned long block_size,
165 unsigned long current_block)
172 /* Seek to the correct loc. */
173 if (ext2fs_llseek (dev, (ext2_loff_t) current_block * block_size,
174 SEEK_SET) != (ext2_loff_t) current_block * block_size)
175 com_err (program_name, errno, "during seek");
178 got = write (dev, buffer, try * block_size);
183 "Weird value (%ld) in do_write\n", got);
190 static void flush_bufs (int dev, int sync)
193 #if !defined (BLKFLSBUF) && !defined (FDFLUSH)
197 fprintf (stderr, "Flushing buffers\n");
199 if (sync && fdatasync (dev) == -1)
200 com_err (program_name, errno, "during fsync");
203 ioctl (host_dev, BLKFLSBUF, 0); /* In case this is a HD */
206 ioctl (host_dev, FDFLUSH, 0); /* In case this is floppy */
210 static unsigned int test_ro (int dev, unsigned long blocks_count,
211 unsigned long block_size,
212 unsigned long from_count, unsigned long blocks_at_once)
217 unsigned int bb_count = 0;
220 errcode = ext2fs_badblocks_list_iterate_begin(bb_list,&bb_iter);
222 com_err (program_name, errcode, "while beginning bad block list iteration");
226 ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
227 } while (next_bad && next_bad < from_count);
229 blkbuf = malloc (blocks_at_once * block_size);
232 com_err (program_name, ENOMEM, "while allocating buffers");
238 "Checking for bad blocks in read-only mode\n");
239 fprintf (stderr, "From block %lu to %lu\n", from_count, blocks_count);
241 try = blocks_at_once;
242 currently_testing = from_count;
243 num_blocks = blocks_count;
244 if (s_flag || v_flag > 1) {
245 fprintf(stderr, "Checking for bad blocks (read-only test): ");
249 while (currently_testing < blocks_count)
252 if (currently_testing == next_bad) {
253 /* fprintf (out, "%lu\n", nextbad); */
254 ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
258 else if (currently_testing + try > next_bad)
259 try = next_bad - currently_testing;
261 if (currently_testing + try > blocks_count)
262 try = blocks_count - currently_testing;
263 got = do_read (dev, blkbuf, try, block_size, currently_testing);
264 currently_testing += got;
266 try = blocks_at_once;
272 bb_output (currently_testing++), ++bb_count;
277 if (s_flag || v_flag > 1)
278 fprintf(stderr, done_string);
283 ext2fs_badblocks_list_iterate_end(bb_iter);
288 static unsigned int test_rw (int dev, unsigned long blocks_count,
289 unsigned long block_size,
290 unsigned long from_count, unsigned long blocks_at_once)
294 unsigned char pattern[] = {0xaa, 0x55, 0xff, 0x00};
295 unsigned int bb_count = 0;
297 buffer = malloc (2 * block_size);
300 com_err (program_name, ENOMEM, "while allocating buffers");
308 "Checking for bad blocks in read-write mode\n");
309 fprintf(stderr, "From block %lu to %lu\n",
310 from_count, blocks_count);
312 for (i = 0; i < sizeof (pattern); i++) {
313 memset (buffer, pattern[i], block_size);
315 fprintf (stderr, "Writing pattern 0x%08x: ",
317 num_blocks = blocks_count;
318 currently_testing = from_count;
319 if (s_flag && v_flag <= 1)
322 currently_testing < blocks_count;
325 if (ext2fs_llseek (dev, (ext2_loff_t) currently_testing *
326 block_size, SEEK_SET) !=
327 (ext2_loff_t) currently_testing * block_size)
328 com_err (program_name, errno,
329 "during seek on block %d",
333 write (dev, buffer, block_size);
338 fprintf(stderr, done_string);
341 fprintf (stderr, "Reading and comparing: ");
342 num_blocks = blocks_count;
343 currently_testing = from_count;
344 if (s_flag && v_flag <= 1)
347 currently_testing < blocks_count;
350 if (ext2fs_llseek (dev, (ext2_loff_t) currently_testing *
351 block_size, SEEK_SET) !=
352 (ext2_loff_t) currently_testing * block_size)
353 com_err (program_name, errno,
354 "during seek on block %d",
358 if (read (dev, buffer + block_size, block_size) < block_size)
360 ext2fs_badblocks_list_test (bb_list, currently_testing)
361 || ( bb_output (currently_testing), ++bb_count );
363 else if (memcmp (buffer, buffer + block_size, block_size))
365 ext2fs_badblocks_list_test (bb_list, currently_testing)
366 || ( bb_output (currently_testing), ++bb_count );
372 fprintf(stderr, done_string);
379 static unsigned int test_nd (int dev, unsigned long blocks_count,
380 unsigned long block_size,
381 unsigned long from_count, unsigned long blocks_at_once)
388 unsigned long bufblk[blocks_at_once];
389 unsigned long bufblks[blocks_at_once];
390 jmp_buf terminate_env;
391 unsigned int bb_count = 0;
394 errcode = ext2fs_badblocks_list_iterate_begin(bb_list,&bb_iter);
396 com_err (program_name, errcode, "while beginning bad block list iteration");
400 ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
401 } while (next_bad && next_bad < from_count);
403 blkbuf = malloc (3 * blocks_at_once * block_size);
406 com_err (program_name, ENOMEM, "while allocating buffers");
410 /* inititalize the test data randomly: */
412 fprintf (stderr, "Initializing random test data\n");
414 for(ptr = blkbuf + blocks_at_once * block_size;
415 ptr < blkbuf + 2 * blocks_at_once * block_size;
417 (*ptr) = random() % (1 << sizeof(char));
423 "Checking for bad blocks in non-destructive read-write mode\n");
424 fprintf (stderr, "From block %lu to %lu\n", from_count, blocks_count);
427 currently_testing = from_count;
428 num_blocks = blocks_count;
429 if (s_flag || v_flag > 1) {
430 fprintf (stderr, "Checking for bad blocks (non-destructive read-write test): ");
434 if (! setjmp(terminate_env)) {
435 /* set up abend handler */
436 capture_terminate(terminate_env);
438 while (currently_testing < blocks_count)
440 try = blocks_at_once - buf_used;
442 if (currently_testing == next_bad) {
443 /* fprintf (out, "%lu\n", nextbad); */
444 ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
445 bufblk[buf_used] = currently_testing++;
448 else if (currently_testing + try > next_bad)
449 try = next_bad - currently_testing;
451 if (currently_testing + try > blocks_count)
452 try = blocks_count - currently_testing;
453 got = do_read (dev, blkbuf + buf_used * block_size, try,
454 block_size, currently_testing);
456 /* if reading succeeded, write the test data */
460 written = do_write (dev, blkbuf + (buf_used + blocks_at_once) * block_size,
461 got, block_size, currently_testing);
463 com_err (program_name, errno, "during test data write, block %lu",
464 currently_testing + written);
467 bufblk[buf_used] = currently_testing;
468 bufblks[buf_used] = got;
470 currently_testing += got;
472 bb_output (currently_testing++), ++bb_count;
476 if (buf_used == blocks_at_once || currently_testing == blocks_count) {
477 if (s_flag || v_flag > 1)
478 fprintf(stderr, "\n");
481 for (buf_used = 0, currently_testing = bufblk[0];
482 buf_used < blocks_at_once &&
483 currently_testing < blocks_count &&
484 (currently_testing = bufblk[buf_used]) < blocks_count;
485 buf_used += bufblks[buf_used])
487 /* for each contiguous block that we read into the buffer
488 (and wrote test data into afterwards), read it back
489 (looping if necessary, to get past newly discovered
490 unreadable blocks, of which there should be none, but with
491 a hard drive which is unreliable, it has happened), and
492 compare with the test data that was written; output to
493 the bad block list if it doesn't match. */
497 while ((offset = currently_testing - bufblk[buf_used]) <
501 try = bufblks[buf_used] - offset;
502 got = do_read (dev, blkbuf + (2 * blocks_at_once + buf_used + offset) * block_size,
503 try, block_size, currently_testing);
505 /* test the comparison between all the blocks successfully read */
506 for (i = 0; i < got; ++i)
507 if (memcmp (blkbuf + (blocks_at_once + buf_used + offset + i) * block_size,
508 blkbuf + (2 * blocks_at_once + buf_used + offset + i) * block_size,
510 bb_output (currently_testing + i), ++bb_count;
512 currently_testing += got;
514 bb_output (currently_testing++), ++bb_count;
517 /* when done, write back original data */
518 do_write (dev, blkbuf + buf_used * block_size,
519 bufblks[buf_used], block_size, bufblk[buf_used]);
522 /* empty the buffer so it can be reused */
528 if (s_flag || v_flag > 1)
529 fprintf(stderr, "done \n");
532 /* abnormal termination by a signal is handled here */
533 /* logic is: write back the data that is in the buffer,
534 so that we can maintain data integrity on disk. Then
538 fprintf(stderr, "Interrupt caught, cleaning up\n");
540 for (buf_written = 0;
541 buf_written < buf_used;
542 buf_written += bufblks[buf_written])
543 do_write (dev, blkbuf + buf_written * block_size,
544 bufblks[buf_written], block_size, bufblk[buf_written]);
554 ext2fs_badblocks_list_iterate_end(bb_iter);
559 int main (int argc, char ** argv)
564 char * host_device_name = NULL;
565 char * input_file = NULL;
566 char * output_file = NULL;
568 unsigned long block_size = 1024;
569 unsigned long blocks_at_once = 16;
570 unsigned long blocks_count, from_count;
572 int passes_clean = 0;
576 setbuf(stdout, NULL);
577 setbuf(stderr, NULL);
579 program_name = *argv;
580 while ((c = getopt (argc, argv, "b:i:o:svwnc:p:h:")) != EOF) {
583 block_size = strtoul (optarg, &tmp, 0);
584 if (*tmp || block_size > 4096) {
585 com_err (program_name, 0,
586 "bad block size - %s", optarg);
594 output_file = optarg;
610 blocks_at_once = strtoul (optarg, &tmp, 0);
612 com_err (program_name, 0,
613 "bad simultaneous block count - %s", optarg);
618 num_passes = strtoul (optarg, &tmp, 0);
620 com_err (program_name, 0,
621 "bad number of clean passes - %s", optarg);
626 host_device_name = optarg;
632 if (optind > argc - 1)
634 device_name = argv[optind++];
635 if (optind > argc - 1)
637 blocks_count = strtoul (argv[optind], &tmp, 0);
640 com_err (program_name, 0, "bad blocks count - %s", argv[optind]);
643 if (++optind <= argc-1) {
644 from_count = strtoul (argv[optind], &tmp, 0);
645 } else from_count = 0;
646 if (from_count >= blocks_count) {
647 com_err (program_name, 0, "bad blocks range: %lu-%lu",
648 from_count, blocks_count);
651 dev = open (device_name, w_flag ? O_RDWR : O_RDONLY);
654 com_err (program_name, errno, "while trying to open %s",
658 if (host_device_name) {
659 host_dev = open (host_device_name, O_RDONLY);
662 com_err (program_name, errno, "while trying to open %s",
669 if (strcmp (input_file, "-") == 0)
672 in = fopen (input_file, "r");
675 com_err (program_name, errno,"while trying to open %s",
680 if (output_file && strcmp (output_file, "-") != 0)
682 out = fopen (output_file, "w");
685 com_err (program_name, errno,"while trying to open %s",
693 errcode = ext2fs_badblocks_list_create(&bb_list,0);
695 com_err (program_name, errcode, "creating in-memory bad blocks list");
701 switch(fscanf (in, "%lu\n", &next_bad)) {
703 com_err (program_name, 0, "input file - bad format");
708 errcode = ext2fs_badblocks_list_add(bb_list,next_bad);
710 com_err (program_name, errcode, "adding to in-memory bad block list");
723 unsigned int bb_count;
727 (w_flag == 2 ? test_nd
730 (dev, blocks_count, block_size, from_count,
732 ) ? passes_clean = 0 : ++passes_clean;
735 fprintf(stderr,"Pass completed, %u bad blocks found.\n", bb_count);
737 } while (passes_clean < num_passes);