Home | History | Annotate | Download | only in misc
      1 /*
      2  * badblocks.c		- Bad blocks checker
      3  *
      4  * Copyright (C) 1992, 1993, 1994  Remy Card <card (at) masi.ibp.fr>
      5  *                                 Laboratoire MASI, Institut Blaise Pascal
      6  *                                 Universite Pierre et Marie Curie (Paris VI)
      7  *
      8  * Copyright 1995, 1996, 1997, 1998, 1999 by Theodore Ts'o
      9  * Copyright 1999 by David Beattie
     10  *
     11  * This file is based on the minix file system programs fsck and mkfs
     12  * written and copyrighted by Linus Torvalds <Linus.Torvalds (at) cs.helsinki.fi>
     13  *
     14  * %Begin-Header%
     15  * This file may be redistributed under the terms of the GNU Public
     16  * License.
     17  * %End-Header%
     18  */
     19 
     20 /*
     21  * History:
     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)
     30  */
     31 
     32 #define _GNU_SOURCE /* for O_DIRECT */
     33 
     34 #include <errno.h>
     35 #include <fcntl.h>
     36 #ifdef HAVE_GETOPT_H
     37 #include <getopt.h>
     38 #else
     39 extern char *optarg;
     40 extern int optind;
     41 #endif
     42 #include <signal.h>
     43 #include <stdio.h>
     44 #include <stdlib.h>
     45 #include <string.h>
     46 #include <unistd.h>
     47 #include <setjmp.h>
     48 #include <time.h>
     49 #include <limits.h>
     50 
     51 #include <sys/ioctl.h>
     52 #include <sys/types.h>
     53 
     54 #include "et/com_err.h"
     55 #include "ext2fs/ext2_io.h"
     56 #include "ext2fs/ext2_fs.h"
     57 #include "ext2fs/ext2fs.h"
     58 #include "nls-enable.h"
     59 
     60 const char * program_name = "badblocks";
     61 const char * done_string = N_("done                                \n");
     62 
     63 static int v_flag = 0;			/* verbose */
     64 static int w_flag = 0;			/* do r/w test: 0=no, 1=yes,
     65 					 * 2=non-destructive */
     66 static int s_flag = 0;			/* show progress of test */
     67 static int force = 0;			/* force check of mounted device */
     68 static int t_flag = 0;			/* number of test patterns */
     69 static int t_max = 0;			/* allocated test patterns */
     70 static unsigned long *t_patts = NULL;	/* test patterns */
     71 static int current_O_DIRECT = 0;	/* Current status of O_DIRECT flag */
     72 static int exclusive_ok = 0;
     73 
     74 #define T_INC 32
     75 
     76 int sys_page_size = 4096;
     77 
     78 static void usage(void)
     79 {
     80 	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"),
     81 		 program_name);
     82 	exit (1);
     83 }
     84 
     85 static void exclusive_usage(void)
     86 {
     87 	fprintf(stderr,
     88 		_("%s: The -n and -w options are mutually exclusive.\n\n"),
     89 		program_name);
     90 	exit(1);
     91 }
     92 
     93 static unsigned long currently_testing = 0;
     94 static unsigned long num_blocks = 0;
     95 static ext2_badblocks_list bb_list = NULL;
     96 static FILE *out;
     97 static blk_t next_bad = 0;
     98 static ext2_badblocks_iterate bb_iter = NULL;
     99 
    100 static void *allocate_buffer(size_t size)
    101 {
    102 	void	*ret = 0;
    103 
    104 #ifdef HAVE_POSIX_MEMALIGN
    105 	if (posix_memalign(&ret, sys_page_size, size) < 0)
    106 		ret = 0;
    107 #else
    108 #ifdef HAVE_MEMALIGN
    109 	ret = memalign(sys_page_size, size);
    110 #else
    111 #ifdef HAVE_VALLOC
    112 	ret = valloc(size);
    113 #endif /* HAVE_VALLOC */
    114 #endif /* HAVE_MEMALIGN */
    115 #endif /* HAVE_POSIX_MEMALIGN */
    116 
    117 	if (!ret)
    118 		ret = malloc(size);
    119 
    120 	return ret;
    121 }
    122 
    123 /*
    124  * This routine reports a new bad block.  If the bad block has already
    125  * been seen before, then it returns 0; otherwise it returns 1.
    126  */
    127 static int bb_output (unsigned long bad)
    128 {
    129 	errcode_t errcode;
    130 
    131 	if (ext2fs_badblocks_list_test(bb_list, bad))
    132 		return 0;
    133 
    134 	fprintf(out, "%lu\n", bad);
    135 	fflush(out);
    136 
    137 	errcode = ext2fs_badblocks_list_add (bb_list, bad);
    138 	if (errcode) {
    139 		com_err (program_name, errcode, "adding to in-memory bad block list");
    140 		exit (1);
    141 	}
    142 
    143 	/* kludge:
    144 	   increment the iteration through the bb_list if
    145 	   an element was just added before the current iteration
    146 	   position.  This should not cause next_bad to change. */
    147 	if (bb_iter && bad < next_bad)
    148 		ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
    149 	return 1;
    150 }
    151 
    152 static void print_status(void)
    153 {
    154 	fprintf(stderr, "%15ld/%15ld", currently_testing, num_blocks);
    155 	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);
    156 	fflush (stderr);
    157 }
    158 
    159 static void alarm_intr(int alnum EXT2FS_ATTR((unused)))
    160 {
    161 	signal (SIGALRM, alarm_intr);
    162 	alarm(1);
    163 	if (!num_blocks)
    164 		return;
    165 	print_status();
    166 }
    167 
    168 static void *terminate_addr = NULL;
    169 
    170 static void terminate_intr(int signo EXT2FS_ATTR((unused)))
    171 {
    172 	if (terminate_addr)
    173 		longjmp(terminate_addr,1);
    174 	exit(1);
    175 }
    176 
    177 static void capture_terminate(jmp_buf term_addr)
    178 {
    179 	terminate_addr = term_addr;
    180 	signal (SIGHUP, terminate_intr);
    181 	signal (SIGINT, terminate_intr);
    182 	signal (SIGPIPE, terminate_intr);
    183 	signal (SIGTERM, terminate_intr);
    184 	signal (SIGUSR1, terminate_intr);
    185 	signal (SIGUSR2, terminate_intr);
    186 }
    187 
    188 static void uncapture_terminate(void)
    189 {
    190 	terminate_addr = NULL;
    191 	signal (SIGHUP, SIG_DFL);
    192 	signal (SIGINT, SIG_DFL);
    193 	signal (SIGPIPE, SIG_DFL);
    194 	signal (SIGTERM, SIG_DFL);
    195 	signal (SIGUSR1, SIG_DFL);
    196 	signal (SIGUSR2, SIG_DFL);
    197 }
    198 
    199 static void set_o_direct(int dev, unsigned char *buffer, size_t size,
    200 			 unsigned long current_block)
    201 {
    202 #ifdef O_DIRECT
    203 	int new_flag = O_DIRECT;
    204 	int flag;
    205 
    206 	if ((((unsigned long) buffer & (sys_page_size - 1)) != 0) ||
    207 	    ((size & (sys_page_size - 1)) != 0) ||
    208 	    ((current_block & ((sys_page_size >> 9)-1)) != 0))
    209 		new_flag = 0;
    210 
    211 	if (new_flag != current_O_DIRECT) {
    212 	     /* printf("%s O_DIRECT\n", new_flag ? "Setting" : "Clearing"); */
    213 		flag = fcntl(dev, F_GETFL);
    214 		if (flag > 0) {
    215 			flag = (flag & ~O_DIRECT) | new_flag;
    216 			fcntl(dev, F_SETFL, flag);
    217 		}
    218 		current_O_DIRECT = new_flag;
    219 	}
    220 #endif
    221 }
    222 
    223 
    224 static void pattern_fill(unsigned char *buffer, unsigned long pattern,
    225 			 size_t n)
    226 {
    227 	unsigned int	i, nb;
    228 	unsigned char	bpattern[sizeof(pattern)], *ptr;
    229 
    230 	if (pattern == (unsigned long) ~0) {
    231 		for (ptr = buffer; ptr < buffer + n; ptr++) {
    232 			(*ptr) = random() % (1 << (8 * sizeof(char)));
    233 		}
    234 		if (s_flag | v_flag)
    235 			fputs(_("Testing with random pattern: "), stderr);
    236 	} else {
    237 		bpattern[0] = 0;
    238 		for (i = 0; i < sizeof(bpattern); i++) {
    239 			if (pattern == 0)
    240 				break;
    241 			bpattern[i] = pattern & 0xFF;
    242 			pattern = pattern >> 8;
    243 		}
    244 		nb = i ? (i-1) : 0;
    245 		for (ptr = buffer, i = nb; ptr < buffer + n; ptr++) {
    246 			*ptr = bpattern[i];
    247 			if (i == 0)
    248 				i = nb;
    249 			else
    250 				i--;
    251 		}
    252 		if (s_flag | v_flag) {
    253 			fputs(_("Testing with pattern 0x"), stderr);
    254 			for (i = 0; i <= nb; i++)
    255 				fprintf(stderr, "%02x", buffer[i]);
    256 			fputs(": ", stderr);
    257 		}
    258 	}
    259 }
    260 
    261 /*
    262  * Perform a read of a sequence of blocks; return the number of blocks
    263  *    successfully sequentially read.
    264  */
    265 static long do_read (int dev, unsigned char * buffer, int try, int block_size,
    266 		     unsigned long current_block)
    267 {
    268 	long got;
    269 
    270 	set_o_direct(dev, buffer, try * block_size, current_block);
    271 
    272 	if (v_flag > 1)
    273 		print_status();
    274 
    275 	/* Seek to the correct loc. */
    276 	if (ext2fs_llseek (dev, (ext2_loff_t) current_block * block_size,
    277 			 SEEK_SET) != (ext2_loff_t) current_block * block_size)
    278 		com_err (program_name, errno, _("during seek"));
    279 
    280 	/* Try the read */
    281 	got = read (dev, buffer, try * block_size);
    282 	if (got < 0)
    283 		got = 0;
    284 	if (got & 511)
    285 		fprintf(stderr, _("Weird value (%ld) in do_read\n"), got);
    286 	got /= block_size;
    287 	return got;
    288 }
    289 
    290 /*
    291  * Perform a write of a sequence of blocks; return the number of blocks
    292  *    successfully sequentially written.
    293  */
    294 static long do_write (int dev, unsigned char * buffer, int try, int block_size,
    295 		     unsigned long current_block)
    296 {
    297 	long got;
    298 
    299 	set_o_direct(dev, buffer, try * block_size, current_block);
    300 
    301 	if (v_flag > 1)
    302 		print_status();
    303 
    304 	/* Seek to the correct loc. */
    305 	if (ext2fs_llseek (dev, (ext2_loff_t) current_block * block_size,
    306 			 SEEK_SET) != (ext2_loff_t) current_block * block_size)
    307 		com_err (program_name, errno, _("during seek"));
    308 
    309 	/* Try the write */
    310 	got = write (dev, buffer, try * block_size);
    311 	if (got < 0)
    312 		got = 0;
    313 	if (got & 511)
    314 		fprintf(stderr, "Weird value (%ld) in do_write\n", got);
    315 	got /= block_size;
    316 	return got;
    317 }
    318 
    319 static int host_dev;
    320 
    321 static void flush_bufs(void)
    322 {
    323 	errcode_t	retval;
    324 
    325 	retval = ext2fs_sync_device(host_dev, 1);
    326 	if (retval)
    327 		com_err(program_name, retval, _("during ext2fs_sync_device"));
    328 }
    329 
    330 static unsigned int test_ro (int dev, unsigned long last_block,
    331 			     int block_size, unsigned long from_count,
    332 			     unsigned long blocks_at_once)
    333 {
    334 	unsigned char * blkbuf;
    335 	int try;
    336 	long got;
    337 	unsigned int bb_count = 0;
    338 	errcode_t errcode;
    339 
    340 	errcode = ext2fs_badblocks_list_iterate_begin(bb_list,&bb_iter);
    341 	if (errcode) {
    342 		com_err (program_name, errcode,
    343 			 _("while beginning bad block list iteration"));
    344 		exit (1);
    345 	}
    346 	do {
    347 		ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
    348 	} while (next_bad && next_bad < from_count);
    349 
    350 	if (t_flag) {
    351 		blkbuf = allocate_buffer((blocks_at_once + 1) * block_size);
    352 	} else {
    353 		blkbuf = allocate_buffer(blocks_at_once * block_size);
    354 	}
    355 	if (!blkbuf)
    356 	{
    357 		com_err (program_name, ENOMEM, _("while allocating buffers"));
    358 		exit (1);
    359 	}
    360 	if (v_flag) {
    361 	    fprintf (stderr, _("Checking blocks %lu to %lu\n"), from_count,
    362 		     last_block - 1);
    363 	}
    364 	if (t_flag) {
    365 		fputs(_("Checking for bad blocks in read-only mode\n"), stderr);
    366 		pattern_fill(blkbuf + blocks_at_once * block_size,
    367 			     t_patts[0], block_size);
    368 	}
    369 	flush_bufs();
    370 	try = blocks_at_once;
    371 	currently_testing = from_count;
    372 	num_blocks = last_block - 1;
    373 	if (!t_flag && (s_flag || v_flag)) {
    374 		fputs(_("Checking for bad blocks (read-only test): "), stderr);
    375 		if (v_flag <= 1)
    376 			alarm_intr(SIGALRM);
    377 	}
    378 	while (currently_testing < last_block)
    379 	{
    380 		if (next_bad) {
    381 			if (currently_testing == next_bad) {
    382 				/* fprintf (out, "%lu\n", nextbad); */
    383 				ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
    384 				currently_testing++;
    385 				continue;
    386 			}
    387 			else if (currently_testing + try > next_bad)
    388 				try = next_bad - currently_testing;
    389 		}
    390 		if (currently_testing + try > last_block)
    391 			try = last_block - currently_testing;
    392 		got = do_read (dev, blkbuf, try, block_size, currently_testing);
    393 		if (t_flag) {
    394 			/* test the comparison between all the
    395 			   blocks successfully read  */
    396 			int i;
    397 			for (i = 0; i < got; ++i)
    398 				if (memcmp (blkbuf+i*block_size,
    399 					    blkbuf+blocks_at_once*block_size,
    400 					    block_size))
    401 					bb_count += bb_output(currently_testing + i);
    402 		}
    403 		currently_testing += got;
    404 		if (got == try) {
    405 			try = blocks_at_once;
    406 			/* recover page-aligned offset for O_DIRECT */
    407 			if ( blocks_at_once >= (unsigned long) (sys_page_size >> 9)
    408 			     && (currently_testing % (sys_page_size >> 9)!= 0))
    409 				try -= (sys_page_size >> 9)
    410 					- (currently_testing
    411 					   % (sys_page_size >> 9));
    412 			continue;
    413 		}
    414 		else
    415 			try = 1;
    416 		if (got == 0) {
    417 			bb_count += bb_output(currently_testing++);
    418 		}
    419 	}
    420 	num_blocks = 0;
    421 	alarm(0);
    422 	if (s_flag || v_flag)
    423 		fputs(_(done_string), stderr);
    424 
    425 	fflush (stderr);
    426 	free (blkbuf);
    427 
    428 	ext2fs_badblocks_list_iterate_end(bb_iter);
    429 
    430 	return bb_count;
    431 }
    432 
    433 static unsigned int test_rw (int dev, unsigned long last_block,
    434 			     int block_size, unsigned long from_count,
    435 			     unsigned long blocks_at_once)
    436 {
    437 	unsigned char *buffer, *read_buffer;
    438 	const unsigned long patterns[] = {0xaa, 0x55, 0xff, 0x00};
    439 	const unsigned long *pattern;
    440 	int i, try, got, nr_pattern, pat_idx;
    441 	unsigned int bb_count = 0;
    442 
    443 	buffer = allocate_buffer(2 * blocks_at_once * block_size);
    444 	read_buffer = buffer + blocks_at_once * block_size;
    445 
    446 	if (!buffer) {
    447 		com_err (program_name, ENOMEM, _("while allocating buffers"));
    448 		exit (1);
    449 	}
    450 
    451 	flush_bufs();
    452 
    453 	if (v_flag) {
    454 		fputs(_("Checking for bad blocks in read-write mode\n"),
    455 		      stderr);
    456 		fprintf(stderr, _("From block %lu to %lu\n"),
    457 			 from_count, last_block);
    458 	}
    459 	if (t_flag) {
    460 		pattern = t_patts;
    461 		nr_pattern = t_flag;
    462 	} else {
    463 		pattern = patterns;
    464 		nr_pattern = sizeof(patterns) / sizeof(patterns[0]);
    465 	}
    466 	for (pat_idx = 0; pat_idx < nr_pattern; pat_idx++) {
    467 		pattern_fill(buffer, pattern[pat_idx],
    468 			     blocks_at_once * block_size);
    469 		num_blocks = last_block - 1;
    470 		currently_testing = from_count;
    471 		if (s_flag && v_flag <= 1)
    472 			alarm_intr(SIGALRM);
    473 
    474 		try = blocks_at_once;
    475 		while (currently_testing < last_block) {
    476 			if (currently_testing + try > last_block)
    477 				try = last_block - currently_testing;
    478 			got = do_write(dev, buffer, try, block_size,
    479 					currently_testing);
    480 			if (v_flag > 1)
    481 				print_status();
    482 
    483 			currently_testing += got;
    484 			if (got == try) {
    485 				try = blocks_at_once;
    486 				/* recover page-aligned offset for O_DIRECT */
    487 				if ( blocks_at_once >= (unsigned long) (sys_page_size >> 9)
    488 				     && (currently_testing %
    489 					 (sys_page_size >> 9)!= 0))
    490 					try -= (sys_page_size >> 9)
    491 						- (currently_testing
    492 						   % (sys_page_size >> 9));
    493 				continue;
    494 			} else
    495 				try = 1;
    496 			if (got == 0) {
    497 				bb_count += bb_output(currently_testing++);
    498 			}
    499 		}
    500 
    501 		num_blocks = 0;
    502 		alarm (0);
    503 		if (s_flag | v_flag)
    504 			fputs(_(done_string), stderr);
    505 		flush_bufs();
    506 		if (s_flag | v_flag)
    507 			fputs(_("Reading and comparing: "), stderr);
    508 		num_blocks = last_block;
    509 		currently_testing = from_count;
    510 		if (s_flag && v_flag <= 1)
    511 			alarm_intr(SIGALRM);
    512 
    513 		try = blocks_at_once;
    514 		while (currently_testing < last_block) {
    515 			if (currently_testing + try > last_block)
    516 				try = last_block - currently_testing;
    517 			got = do_read (dev, read_buffer, try, block_size,
    518 				       currently_testing);
    519 			if (got == 0) {
    520 				bb_count += bb_output(currently_testing++);
    521 				continue;
    522 			}
    523 			for (i=0; i < got; i++) {
    524 				if (memcmp(read_buffer + i * block_size,
    525 					   buffer + i * block_size,
    526 					   block_size))
    527 					bb_count += bb_output(currently_testing+i);
    528 			}
    529 			currently_testing += got;
    530 			/* recover page-aligned offset for O_DIRECT */
    531 			if ( blocks_at_once >= (unsigned long) (sys_page_size >> 9)
    532 			     && (currently_testing % (sys_page_size >> 9)!= 0))
    533 				try = blocks_at_once - (sys_page_size >> 9)
    534 					- (currently_testing
    535 					   % (sys_page_size >> 9));
    536 			else
    537 				try = blocks_at_once;
    538 			if (v_flag > 1)
    539 				print_status();
    540 		}
    541 
    542 		num_blocks = 0;
    543 		alarm (0);
    544 		if (s_flag | v_flag)
    545 			fputs(_(done_string), stderr);
    546 		flush_bufs();
    547 	}
    548 	uncapture_terminate();
    549 	free(buffer);
    550 	return bb_count;
    551 }
    552 
    553 struct saved_blk_record {
    554 	blk_t	block;
    555 	int	num;
    556 };
    557 
    558 static unsigned int test_nd (int dev, unsigned long last_block,
    559 			     int block_size, unsigned long from_count,
    560 			     unsigned long blocks_at_once)
    561 {
    562 	unsigned char *blkbuf, *save_ptr, *test_ptr, *read_ptr;
    563 	unsigned char *test_base, *save_base, *read_base;
    564 	int try, i;
    565 	const unsigned long patterns[] = { ~0 };
    566 	const unsigned long *pattern;
    567 	int nr_pattern, pat_idx;
    568 	long got, used2, written, save_currently_testing;
    569 	struct saved_blk_record *test_record;
    570 	/* This is static to prevent being clobbered by the longjmp */
    571 	static int num_saved;
    572 	jmp_buf terminate_env;
    573 	errcode_t errcode;
    574 	unsigned long buf_used;
    575 	static unsigned int bb_count;
    576 
    577 	bb_count = 0;
    578 	errcode = ext2fs_badblocks_list_iterate_begin(bb_list,&bb_iter);
    579 	if (errcode) {
    580 		com_err (program_name, errcode,
    581 			 _("while beginning bad block list iteration"));
    582 		exit (1);
    583 	}
    584 	do {
    585 		ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
    586 	} while (next_bad && next_bad < from_count);
    587 
    588 	blkbuf = allocate_buffer(3 * blocks_at_once * block_size);
    589 	test_record = malloc (blocks_at_once*sizeof(struct saved_blk_record));
    590 	if (!blkbuf || !test_record) {
    591 		com_err(program_name, ENOMEM, _("while allocating buffers"));
    592 		exit (1);
    593 	}
    594 
    595 	save_base = blkbuf;
    596 	test_base = blkbuf + (blocks_at_once * block_size);
    597 	read_base = blkbuf + (2 * blocks_at_once * block_size);
    598 
    599 	num_saved = 0;
    600 
    601 	flush_bufs();
    602 	if (v_flag) {
    603 	    fputs(_("Checking for bad blocks in non-destructive read-write mode\n"), stderr);
    604 	    fprintf (stderr, _("From block %lu to %lu\n"), from_count, last_block);
    605 	}
    606 	if (s_flag || v_flag > 1) {
    607 		fputs(_("Checking for bad blocks (non-destructive read-write test)\n"), stderr);
    608 	}
    609 	if (setjmp(terminate_env)) {
    610 		/*
    611 		 * Abnormal termination by a signal is handled here.
    612 		 */
    613 		signal (SIGALRM, SIG_IGN);
    614 		fputs(_("\nInterrupt caught, cleaning up\n"), stderr);
    615 
    616 		save_ptr = save_base;
    617 		for (i=0; i < num_saved; i++) {
    618 			do_write(dev, save_ptr, test_record[i].num,
    619 				 block_size, test_record[i].block);
    620 			save_ptr += test_record[i].num * block_size;
    621 		}
    622 		fflush (out);
    623 		exit(1);
    624 	}
    625 
    626 	/* set up abend handler */
    627 	capture_terminate(terminate_env);
    628 
    629 	if (t_flag) {
    630 		pattern = t_patts;
    631 		nr_pattern = t_flag;
    632 	} else {
    633 		pattern = patterns;
    634 		nr_pattern = sizeof(patterns) / sizeof(patterns[0]);
    635 	}
    636 	for (pat_idx = 0; pat_idx < nr_pattern; pat_idx++) {
    637 		pattern_fill(test_base, pattern[pat_idx],
    638 			     blocks_at_once * block_size);
    639 
    640 		buf_used = 0;
    641 		bb_count = 0;
    642 		save_ptr = save_base;
    643 		test_ptr = test_base;
    644 		currently_testing = from_count;
    645 		num_blocks = last_block - 1;
    646 		if (s_flag && v_flag <= 1)
    647 			alarm_intr(SIGALRM);
    648 
    649 		while (currently_testing < last_block) {
    650 			got = try = blocks_at_once - buf_used;
    651 			if (next_bad) {
    652 				if (currently_testing == next_bad) {
    653 					/* fprintf (out, "%lu\n", nextbad); */
    654 					ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
    655 					currently_testing++;
    656 					goto check_for_more;
    657 				}
    658 				else if (currently_testing + try > next_bad)
    659 					try = next_bad - currently_testing;
    660 			}
    661 			if (currently_testing + try > last_block)
    662 				try = last_block - currently_testing;
    663 			got = do_read (dev, save_ptr, try, block_size,
    664 				       currently_testing);
    665 			if (got == 0) {
    666 				/* First block must have been bad. */
    667 				bb_count += bb_output(currently_testing++);
    668 				goto check_for_more;
    669 			}
    670 
    671 			/*
    672 			 * Note the fact that we've saved this much data
    673 			 * *before* we overwrite it with test data
    674 			 */
    675 			test_record[num_saved].block = currently_testing;
    676 			test_record[num_saved].num = got;
    677 			num_saved++;
    678 
    679 			/* Write the test data */
    680 			written = do_write (dev, test_ptr, got, block_size,
    681 					    currently_testing);
    682 			if (written != got)
    683 				com_err (program_name, errno,
    684 					 _("during test data write, block %lu"),
    685 					 currently_testing + written);
    686 
    687 			buf_used += got;
    688 			save_ptr += got * block_size;
    689 			test_ptr += got * block_size;
    690 			currently_testing += got;
    691 			if (got != try)
    692 				bb_count += bb_output(currently_testing++);
    693 
    694 		check_for_more:
    695 			/*
    696 			 * If there's room for more blocks to be tested this
    697 			 * around, and we're not done yet testing the disk, go
    698 			 * back and get some more blocks.
    699 			 */
    700 			if ((buf_used != blocks_at_once) &&
    701 			    (currently_testing < last_block))
    702 				continue;
    703 
    704 			flush_bufs();
    705 			save_currently_testing = currently_testing;
    706 
    707 			/*
    708 			 * for each contiguous block that we read into the
    709 			 * buffer (and wrote test data into afterwards), read
    710 			 * it back (looping if necessary, to get past newly
    711 			 * discovered unreadable blocks, of which there should
    712 			 * be none, but with a hard drive which is unreliable,
    713 			 * it has happened), and compare with the test data
    714 			 * that was written; output to the bad block list if
    715 			 * it doesn't match.
    716 			 */
    717 			used2 = 0;
    718 			save_ptr = save_base;
    719 			test_ptr = test_base;
    720 			read_ptr = read_base;
    721 			try = 0;
    722 
    723 			while (1) {
    724 				if (try == 0) {
    725 					if (used2 >= num_saved)
    726 						break;
    727 					currently_testing = test_record[used2].block;
    728 					try = test_record[used2].num;
    729 					used2++;
    730 				}
    731 
    732 				got = do_read (dev, read_ptr, try,
    733 					       block_size, currently_testing);
    734 
    735 				/* test the comparison between all the
    736 				   blocks successfully read  */
    737 				for (i = 0; i < got; ++i)
    738 					if (memcmp (test_ptr+i*block_size,
    739 						    read_ptr+i*block_size, block_size))
    740 						bb_count += bb_output(currently_testing + i);
    741 				if (got < try) {
    742 					bb_count += bb_output(currently_testing + got);
    743 					got++;
    744 				}
    745 
    746 				/* write back original data */
    747 				do_write (dev, save_ptr, got,
    748 					  block_size, currently_testing);
    749 				save_ptr += got * block_size;
    750 
    751 				currently_testing += got;
    752 				test_ptr += got * block_size;
    753 				read_ptr += got * block_size;
    754 				try -= got;
    755 			}
    756 
    757 			/* empty the buffer so it can be reused */
    758 			num_saved = 0;
    759 			buf_used = 0;
    760 			save_ptr = save_base;
    761 			test_ptr = test_base;
    762 			currently_testing = save_currently_testing;
    763 		}
    764 		num_blocks = 0;
    765 		alarm(0);
    766 		if (s_flag || v_flag > 1)
    767 			fputs(_(done_string), stderr);
    768 
    769 		flush_bufs();
    770 	}
    771 	uncapture_terminate();
    772 	fflush(stderr);
    773 	free(blkbuf);
    774 	free(test_record);
    775 
    776 	ext2fs_badblocks_list_iterate_end(bb_iter);
    777 
    778 	return bb_count;
    779 }
    780 
    781 static void check_mount(char *device_name)
    782 {
    783 	errcode_t	retval;
    784 	int		mount_flags;
    785 
    786 	retval = ext2fs_check_if_mounted(device_name, &mount_flags);
    787 	if (retval) {
    788 		com_err("ext2fs_check_if_mount", retval,
    789 			_("while determining whether %s is mounted."),
    790 			device_name);
    791 		return;
    792 	}
    793 	if (mount_flags & EXT2_MF_MOUNTED) {
    794 		fprintf(stderr, _("%s is mounted; "), device_name);
    795 		if (force) {
    796 			fputs(_("badblocks forced anyway.  "
    797 				"Hope /etc/mtab is incorrect.\n"), stderr);
    798 			return;
    799 		}
    800 	abort_badblocks:
    801 		fputs(_("it's not safe to run badblocks!\n"), stderr);
    802 		exit(1);
    803 	}
    804 
    805 	if ((mount_flags & EXT2_MF_BUSY) && !exclusive_ok) {
    806 		fprintf(stderr, _("%s is apparently in use by the system; "),
    807 			device_name);
    808 		if (force)
    809 			fputs(_("badblocks forced anyway.\n"), stderr);
    810 		else
    811 			goto abort_badblocks;
    812 	}
    813 
    814 }
    815 
    816 
    817 int main (int argc, char ** argv)
    818 {
    819 	int c;
    820 	char * tmp;
    821 	char * device_name;
    822 	char * host_device_name = NULL;
    823 	char * input_file = NULL;
    824 	char * output_file = NULL;
    825 	FILE * in = NULL;
    826 	int block_size = 1024;
    827 	unsigned long blocks_at_once = 64;
    828 	blk_t last_block, from_count;
    829 	int num_passes = 0;
    830 	int passes_clean = 0;
    831 	int dev;
    832 	errcode_t errcode;
    833 	unsigned long pattern;
    834 	unsigned int (*test_func)(int, unsigned long,
    835 				  int, unsigned long,
    836 				  unsigned long);
    837 	int open_flag = 0;
    838 	long sysval;
    839 
    840 	setbuf(stdout, NULL);
    841 	setbuf(stderr, NULL);
    842 #ifdef ENABLE_NLS
    843 	setlocale(LC_MESSAGES, "");
    844 	setlocale(LC_CTYPE, "");
    845 	bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
    846 	textdomain(NLS_CAT_NAME);
    847 #endif
    848 	srandom((unsigned int)time(NULL));  /* simple randomness is enough */
    849 	test_func = test_ro;
    850 
    851 	/* Determine the system page size if possible */
    852 #ifdef HAVE_SYSCONF
    853 #if (!defined(_SC_PAGESIZE) && defined(_SC_PAGE_SIZE))
    854 #define _SC_PAGESIZE _SC_PAGE_SIZE
    855 #endif
    856 #ifdef _SC_PAGESIZE
    857 	sysval = sysconf(_SC_PAGESIZE);
    858 	if (sysval > 0)
    859 		sys_page_size = sysval;
    860 #endif /* _SC_PAGESIZE */
    861 #endif /* HAVE_SYSCONF */
    862 
    863 	if (argc && *argv)
    864 		program_name = *argv;
    865 	while ((c = getopt (argc, argv, "b:fi:o:svwnc:p:h:t:X")) != EOF) {
    866 		switch (c) {
    867 		case 'b':
    868 			block_size = strtoul (optarg, &tmp, 0);
    869 			if (*tmp || block_size > 4096) {
    870 				com_err (program_name, 0,
    871 					 _("bad block size - %s"), optarg);
    872 				exit (1);
    873 			}
    874 			break;
    875 		case 'f':
    876 			force++;
    877 			break;
    878 		case 'i':
    879 			input_file = optarg;
    880 			break;
    881 		case 'o':
    882 			output_file = optarg;
    883 			break;
    884 		case 's':
    885 			s_flag = 1;
    886 			break;
    887 		case 'v':
    888 			v_flag++;
    889 			break;
    890 		case 'w':
    891 			if (w_flag)
    892 				exclusive_usage();
    893 			test_func = test_rw;
    894 			w_flag = 1;
    895 			break;
    896 		case 'n':
    897 			if (w_flag)
    898 				exclusive_usage();
    899 			test_func = test_nd;
    900 			w_flag = 2;
    901 			break;
    902 		case 'c':
    903 			blocks_at_once = strtoul (optarg, &tmp, 0);
    904 			if (*tmp) {
    905 				com_err (program_name, 0,
    906 					 "bad simultaneous block count - %s", optarg);
    907 				exit (1);
    908 			}
    909 			break;
    910 		case 'p':
    911 			num_passes = strtoul (optarg, &tmp, 0);
    912 			if (*tmp) {
    913 				com_err (program_name, 0,
    914 				    "bad number of clean passes - %s", optarg);
    915 				exit (1);
    916 			}
    917 			break;
    918 		case 'h':
    919 			host_device_name = optarg;
    920 			break;
    921 		case 't':
    922 			if (t_flag + 1 > t_max) {
    923 				unsigned long *t_patts_new;
    924 
    925 				t_patts_new = realloc(t_patts, t_max + T_INC);
    926 				if (!t_patts_new) {
    927 					com_err(program_name, ENOMEM,
    928 						_("can't allocate memory for "
    929 						  "test_pattern - %s"),
    930 						optarg);
    931 					exit(1);
    932 				}
    933 				t_patts = t_patts_new;
    934 				t_max += T_INC;
    935 			}
    936 			if (!strcmp(optarg, "r") || !strcmp(optarg,"random")) {
    937 				t_patts[t_flag++] = ~0;
    938 			} else {
    939 				pattern = strtoul(optarg, &tmp, 0);
    940 				if (*tmp) {
    941 					com_err(program_name, 0,
    942 					_("invalid test_pattern: %s\n"),
    943 						optarg);
    944 					exit(1);
    945 				}
    946 				if (pattern == (unsigned long) ~0)
    947 					pattern = 0xffff;
    948 				t_patts[t_flag++] = pattern;
    949 			}
    950 			break;
    951 		case 'X':
    952 			exclusive_ok++;
    953 			break;
    954 		default:
    955 			usage();
    956 		}
    957 	}
    958 	if (!w_flag) {
    959 		if (t_flag > 1) {
    960 			com_err(program_name, 0,
    961 			_("Maximum of one test_pattern may be specified "
    962 			  "in read-only mode"));
    963 			exit(1);
    964 		}
    965 		if (t_patts && (t_patts[0] == (unsigned long) ~0)) {
    966 			com_err(program_name, 0,
    967 			_("Random test_pattern is not allowed "
    968 			  "in read-only mode"));
    969 			exit(1);
    970 		}
    971 	}
    972 	if (optind > argc - 1)
    973 		usage();
    974 	device_name = argv[optind++];
    975 	if (optind > argc - 1) {
    976 		errcode = ext2fs_get_device_size(device_name,
    977 						 block_size,
    978 						 &last_block);
    979 		if (errcode == EXT2_ET_UNIMPLEMENTED) {
    980 			com_err(program_name, 0,
    981 				_("Couldn't determine device size; you "
    982 				  "must specify\nthe size manually\n"));
    983 			exit(1);
    984 		}
    985 		if (errcode) {
    986 			com_err(program_name, errcode,
    987 				_("while trying to determine device size"));
    988 			exit(1);
    989 		}
    990 	} else {
    991 		errno = 0;
    992 		last_block = strtoul (argv[optind], &tmp, 0);
    993 		printf("last_block = %d (%s)\n", last_block, argv[optind]);
    994 		if (*tmp || errno ||
    995 		    (last_block == ULONG_MAX && errno == ERANGE)) {
    996 			com_err (program_name, 0, _("invalid blocks count - %s"),
    997 				 argv[optind]);
    998 			exit (1);
    999 		}
   1000 		last_block++;
   1001 		optind++;
   1002 	}
   1003 	if (optind <= argc-1) {
   1004 		errno = 0;
   1005 		from_count = strtoul (argv[optind], &tmp, 0);
   1006 		printf("from_count = %d\n", from_count);
   1007 		if (*tmp || errno ||
   1008 		    (from_count == ULONG_MAX && errno == ERANGE)) {
   1009 			com_err (program_name, 0, _("invalid starting block - %s"),
   1010 				 argv[optind]);
   1011 			exit (1);
   1012 		}
   1013 	} else from_count = 0;
   1014 	if (from_count >= last_block) {
   1015 	    com_err (program_name, 0, _("invalid starting block (%d): must be less than %lu"),
   1016 		     (unsigned long) from_count, (unsigned long) last_block);
   1017 	    exit (1);
   1018 	}
   1019 	if (w_flag)
   1020 		check_mount(device_name);
   1021 
   1022 	open_flag = w_flag ? O_RDWR : O_RDONLY;
   1023 	dev = open (device_name, open_flag);
   1024 	if (dev == -1) {
   1025 		com_err (program_name, errno, _("while trying to open %s"),
   1026 			 device_name);
   1027 		exit (1);
   1028 	}
   1029 	if (host_device_name) {
   1030 		host_dev = open (host_device_name, open_flag);
   1031 		if (host_dev == -1) {
   1032 			com_err (program_name, errno,
   1033 				 _("while trying to open %s"),
   1034 				 host_device_name);
   1035 			exit (1);
   1036 		}
   1037 	} else
   1038 		host_dev = dev;
   1039 	if (input_file) {
   1040 		if (strcmp (input_file, "-") == 0)
   1041 			in = stdin;
   1042 		else {
   1043 			in = fopen (input_file, "r");
   1044 			if (in == NULL)
   1045 			{
   1046 				com_err (program_name, errno,
   1047 					 _("while trying to open %s"),
   1048 					 input_file);
   1049 				exit (1);
   1050 			}
   1051 		}
   1052 	}
   1053 	if (output_file && strcmp (output_file, "-") != 0)
   1054 	{
   1055 		out = fopen (output_file, "w");
   1056 		if (out == NULL)
   1057 		{
   1058 			com_err (program_name, errno,
   1059 				 _("while trying to open %s"),
   1060 				 output_file);
   1061 			exit (1);
   1062 		}
   1063 	}
   1064 	else
   1065 		out = stdout;
   1066 
   1067 	errcode = ext2fs_badblocks_list_create(&bb_list,0);
   1068 	if (errcode) {
   1069 		com_err (program_name, errcode,
   1070 			 _("while creating in-memory bad blocks list"));
   1071 		exit (1);
   1072 	}
   1073 
   1074 	if (in) {
   1075 		for(;;) {
   1076 			switch(fscanf (in, "%u\n", &next_bad)) {
   1077 				case 0:
   1078 					com_err (program_name, 0, "input file - bad format");
   1079 					exit (1);
   1080 				case EOF:
   1081 					break;
   1082 				default:
   1083 					errcode = ext2fs_badblocks_list_add(bb_list,next_bad);
   1084 					if (errcode) {
   1085 						com_err (program_name, errcode, _("while adding to in-memory bad block list"));
   1086 						exit (1);
   1087 					}
   1088 					continue;
   1089 			}
   1090 			break;
   1091 		}
   1092 
   1093 		if (in != stdin)
   1094 			fclose (in);
   1095 	}
   1096 
   1097 	do {
   1098 		unsigned int bb_count;
   1099 
   1100 		bb_count = test_func(dev, last_block, block_size,
   1101 				     from_count, blocks_at_once);
   1102 		if (bb_count)
   1103 			passes_clean = 0;
   1104 		else
   1105 			++passes_clean;
   1106 
   1107 		if (v_flag)
   1108 			fprintf(stderr,
   1109 				_("Pass completed, %u bad blocks found.\n"),
   1110 				bb_count);
   1111 
   1112 	} while (passes_clean < num_passes);
   1113 
   1114 	close (dev);
   1115 	if (out != stdout)
   1116 		fclose (out);
   1117 	if (t_patts)
   1118 		free(t_patts);
   1119 	return 0;
   1120 }
   1121 
   1122