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 #ifndef _GNU_SOURCE
     33 #define _GNU_SOURCE /* for O_DIRECT */
     34 #endif
     35 
     36 #include <errno.h>
     37 #include <fcntl.h>
     38 #ifdef HAVE_GETOPT_H
     39 #include <getopt.h>
     40 #else
     41 extern char *optarg;
     42 extern int optind;
     43 #endif
     44 #include <signal.h>
     45 #include <stdio.h>
     46 #include <stdlib.h>
     47 #include <string.h>
     48 #include <unistd.h>
     49 #include <setjmp.h>
     50 #include <time.h>
     51 #include <limits.h>
     52 
     53 #include <sys/time.h>
     54 #include <sys/ioctl.h>
     55 #include <sys/types.h>
     56 
     57 #include "et/com_err.h"
     58 #include "ext2fs/ext2_io.h"
     59 #include "ext2fs/ext2_fs.h"
     60 #include "ext2fs/ext2fs.h"
     61 #include "nls-enable.h"
     62 
     63 #ifndef O_LARGEFILE
     64 #define O_LARGEFILE 0
     65 #endif
     66 
     67 static const char * program_name = "badblocks";
     68 static const char * done_string = N_("done                                                 \n");
     69 
     70 static int v_flag;			/* verbose */
     71 static int w_flag;			/* do r/w test: 0=no, 1=yes,
     72 					 * 2=non-destructive */
     73 static int s_flag;			/* show progress of test */
     74 static int force;			/* force check of mounted device */
     75 static int t_flag;			/* number of test patterns */
     76 static int t_max;			/* allocated test patterns */
     77 static unsigned int *t_patts;		/* test patterns */
     78 static int use_buffered_io;
     79 static int exclusive_ok;
     80 static unsigned int max_bb;		/* Abort test if more than this number of bad blocks has been encountered */
     81 static unsigned int d_flag;		/* delay factor between reads */
     82 static struct timeval time_start;
     83 
     84 #define T_INC 32
     85 
     86 static unsigned int sys_page_size = 4096;
     87 
     88 static void usage(void)
     89 {
     90 	fprintf(stderr, _(
     91 "Usage: %s [-b block_size] [-i input_file] [-o output_file] [-svwnf]\n"
     92 "       [-c blocks_at_once] [-d delay_factor_between_reads] [-e max_bad_blocks]\n"
     93 "       [-p num_passes] [-t test_pattern [-t test_pattern [...]]]\n"
     94 "       device [last_block [first_block]]\n"),
     95 		 program_name);
     96 	exit (1);
     97 }
     98 
     99 static void exclusive_usage(void)
    100 {
    101 	fprintf(stderr,
    102 		_("%s: The -n and -w options are mutually exclusive.\n\n"),
    103 		program_name);
    104 	exit(1);
    105 }
    106 
    107 static blk_t currently_testing = 0;
    108 static blk_t num_blocks = 0;
    109 static blk_t num_read_errors = 0;
    110 static blk_t num_write_errors = 0;
    111 static blk_t num_corruption_errors = 0;
    112 static ext2_badblocks_list bb_list = NULL;
    113 static FILE *out;
    114 static blk_t next_bad = 0;
    115 static ext2_badblocks_iterate bb_iter = NULL;
    116 
    117 enum error_types { READ_ERROR, WRITE_ERROR, CORRUPTION_ERROR };
    118 
    119 static void *allocate_buffer(size_t size)
    120 {
    121 	void	*ret = 0;
    122 
    123 #ifdef HAVE_POSIX_MEMALIGN
    124 	if (posix_memalign(&ret, sys_page_size, size) < 0)
    125 		ret = 0;
    126 #else
    127 #ifdef HAVE_MEMALIGN
    128 	ret = memalign(sys_page_size, size);
    129 #else
    130 #ifdef HAVE_VALLOC
    131 	ret = valloc(size);
    132 #endif /* HAVE_VALLOC */
    133 #endif /* HAVE_MEMALIGN */
    134 #endif /* HAVE_POSIX_MEMALIGN */
    135 
    136 	if (!ret)
    137 		ret = malloc(size);
    138 
    139 	return ret;
    140 }
    141 
    142 /*
    143  * This routine reports a new bad block.  If the bad block has already
    144  * been seen before, then it returns 0; otherwise it returns 1.
    145  */
    146 static int bb_output (blk_t bad, enum error_types error_type)
    147 {
    148 	errcode_t errcode;
    149 
    150 	if (ext2fs_badblocks_list_test(bb_list, bad))
    151 		return 0;
    152 
    153 	fprintf(out, "%lu\n", (unsigned long) bad);
    154 	fflush(out);
    155 
    156 	errcode = ext2fs_badblocks_list_add (bb_list, bad);
    157 	if (errcode) {
    158 		com_err (program_name, errcode, "adding to in-memory bad block list");
    159 		exit (1);
    160 	}
    161 
    162 	/* kludge:
    163 	   increment the iteration through the bb_list if
    164 	   an element was just added before the current iteration
    165 	   position.  This should not cause next_bad to change. */
    166 	if (bb_iter && bad < next_bad)
    167 		ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
    168 
    169 	if (error_type == READ_ERROR) {
    170 	  num_read_errors++;
    171 	} else if (error_type == WRITE_ERROR) {
    172 	  num_write_errors++;
    173 	} else if (error_type == CORRUPTION_ERROR) {
    174 	  num_corruption_errors++;
    175 	}
    176 	return 1;
    177 }
    178 
    179 static char *time_diff_format(struct timeval *tv1,
    180 			      struct timeval *tv2, char *buf)
    181 {
    182         time_t	diff = (tv1->tv_sec - tv2->tv_sec);
    183 	int	hr,min,sec;
    184 
    185 	sec = diff % 60;
    186 	diff /= 60;
    187 	min = diff % 60;
    188 	hr = diff / 60;
    189 
    190 	if (hr)
    191 		sprintf(buf, "%d:%02d:%02d", hr, min, sec);
    192 	else
    193 		sprintf(buf, "%d:%02d", min, sec);
    194 	return buf;
    195 }
    196 
    197 static float calc_percent(unsigned long current, unsigned long total) {
    198 	float percent = 0.0;
    199 	if (total <= 0)
    200 		return percent;
    201 	if (current >= total) {
    202 		percent = 100.0;
    203 	} else {
    204 		percent=(100.0*(float)current/(float)total);
    205 	}
    206 	return percent;
    207 }
    208 
    209 static void print_status(void)
    210 {
    211 	struct timeval time_end;
    212 	char diff_buf[32], line_buf[128];
    213 	int len;
    214 
    215 	gettimeofday(&time_end, 0);
    216 	len = snprintf(line_buf, sizeof(line_buf),
    217 		       _("%6.2f%% done, %s elapsed. "
    218 		         "(%d/%d/%d errors)"),
    219 		       calc_percent((unsigned long) currently_testing,
    220 				    (unsigned long) num_blocks),
    221 		       time_diff_format(&time_end, &time_start, diff_buf),
    222 		       num_read_errors,
    223 		       num_write_errors,
    224 		       num_corruption_errors);
    225 #ifdef HAVE_MBSTOWCS
    226 	len = mbstowcs(NULL, line_buf, sizeof(line_buf));
    227 #endif
    228 	fputs(line_buf, stderr);
    229 	memset(line_buf, '\b', len);
    230 	line_buf[len] = 0;
    231 	fputs(line_buf, stderr);
    232 	fflush (stderr);
    233 }
    234 
    235 static void alarm_intr(int alnum EXT2FS_ATTR((unused)))
    236 {
    237 	signal (SIGALRM, alarm_intr);
    238 	alarm(1);
    239 	if (!num_blocks)
    240 		return;
    241 	print_status();
    242 }
    243 
    244 static void *terminate_addr = NULL;
    245 
    246 static void terminate_intr(int signo EXT2FS_ATTR((unused)))
    247 {
    248 	fflush(out);
    249 	fprintf(stderr, "\n\nInterrupted at block %llu\n",
    250 		(unsigned long long) currently_testing);
    251 	fflush(stderr);
    252 	if (terminate_addr)
    253 		longjmp(terminate_addr,1);
    254 	exit(1);
    255 }
    256 
    257 static void capture_terminate(jmp_buf term_addr)
    258 {
    259 	terminate_addr = term_addr;
    260 	signal (SIGHUP, terminate_intr);
    261 	signal (SIGINT, terminate_intr);
    262 	signal (SIGPIPE, terminate_intr);
    263 	signal (SIGTERM, terminate_intr);
    264 	signal (SIGUSR1, terminate_intr);
    265 	signal (SIGUSR2, terminate_intr);
    266 }
    267 
    268 static void uncapture_terminate(void)
    269 {
    270 	terminate_addr = NULL;
    271 	signal (SIGHUP, SIG_DFL);
    272 	signal (SIGINT, SIG_DFL);
    273 	signal (SIGPIPE, SIG_DFL);
    274 	signal (SIGTERM, SIG_DFL);
    275 	signal (SIGUSR1, SIG_DFL);
    276 	signal (SIGUSR2, SIG_DFL);
    277 }
    278 
    279 /* Linux requires that O_DIRECT I/Os be 512-byte sector aligned */
    280 
    281 #define O_DIRECT_SIZE 512
    282 
    283 static void set_o_direct(int dev, unsigned char *buffer, size_t size,
    284 			 ext2_loff_t offset)
    285 {
    286 #ifdef O_DIRECT
    287 	static int current_O_DIRECT;	/* Current status of O_DIRECT flag */
    288 	int new_flag = O_DIRECT;
    289 	int flag;
    290 
    291 	if ((use_buffered_io != 0) ||
    292 	    (((unsigned long) buffer & (sys_page_size - 1)) != 0) ||
    293 	    ((size & (sys_page_size - 1)) != 0) ||
    294 	    ((offset & (O_DIRECT_SIZE - 1)) != 0))
    295 		new_flag = 0;
    296 
    297 	if (new_flag != current_O_DIRECT) {
    298 	     /* printf("%s O_DIRECT\n", new_flag ? "Setting" : "Clearing"); */
    299 		flag = fcntl(dev, F_GETFL);
    300 		if (flag > 0) {
    301 			flag = (flag & ~O_DIRECT) | new_flag;
    302 			fcntl(dev, F_SETFL, flag);
    303 		}
    304 		current_O_DIRECT = new_flag;
    305 	}
    306 #endif
    307 }
    308 
    309 
    310 static void pattern_fill(unsigned char *buffer, unsigned int pattern,
    311 			 size_t n)
    312 {
    313 	unsigned int	i, nb;
    314 	unsigned char	bpattern[sizeof(pattern)], *ptr;
    315 
    316 	if (pattern == (unsigned int) ~0) {
    317 		for (ptr = buffer; ptr < buffer + n; ptr++) {
    318 			(*ptr) = random() % (1 << (8 * sizeof(char)));
    319 		}
    320 		if (s_flag | v_flag)
    321 			fputs(_("Testing with random pattern: "), stderr);
    322 	} else {
    323 		bpattern[0] = 0;
    324 		for (i = 0; i < sizeof(bpattern); i++) {
    325 			if (pattern == 0)
    326 				break;
    327 			bpattern[i] = pattern & 0xFF;
    328 			pattern = pattern >> 8;
    329 		}
    330 		nb = i ? (i-1) : 0;
    331 		for (ptr = buffer, i = nb; ptr < buffer + n; ptr++) {
    332 			*ptr = bpattern[i];
    333 			if (i == 0)
    334 				i = nb;
    335 			else
    336 				i--;
    337 		}
    338 		if (s_flag | v_flag) {
    339 			fputs(_("Testing with pattern 0x"), stderr);
    340 			for (i = 0; i <= nb; i++)
    341 				fprintf(stderr, "%02x", buffer[i]);
    342 			fputs(": ", stderr);
    343 		}
    344 	}
    345 }
    346 
    347 /*
    348  * Perform a read of a sequence of blocks; return the number of blocks
    349  *    successfully sequentially read.
    350  */
    351 static int do_read (int dev, unsigned char * buffer, int try, int block_size,
    352 		    blk_t current_block)
    353 {
    354 	long got;
    355 	struct timeval tv1, tv2;
    356 #define NANOSEC (1000000000L)
    357 #define MILISEC (1000L)
    358 
    359 #if 0
    360 	printf("do_read: block %d, try %d\n", current_block, try);
    361 #endif
    362 	set_o_direct(dev, buffer, try * block_size,
    363 		     ((ext2_loff_t) current_block) * block_size);
    364 
    365 	if (v_flag > 1)
    366 		print_status();
    367 
    368 	/* Seek to the correct loc. */
    369 	if (ext2fs_llseek (dev, (ext2_loff_t) current_block * block_size,
    370 			 SEEK_SET) != (ext2_loff_t) current_block * block_size)
    371 		com_err (program_name, errno, "%s", _("during seek"));
    372 
    373 	/* Try the read */
    374 	if (d_flag)
    375 		gettimeofday(&tv1, NULL);
    376 	got = read (dev, buffer, try * block_size);
    377 	if (d_flag)
    378 		gettimeofday(&tv2, NULL);
    379 	if (got < 0)
    380 		got = 0;
    381 	if (got & 511)
    382 		fprintf(stderr, _("Weird value (%ld) in do_read\n"), got);
    383 	got /= block_size;
    384 	if (d_flag && got == try) {
    385 #ifdef HAVE_NANOSLEEP
    386 		struct timespec ts;
    387 		ts.tv_sec = tv2.tv_sec - tv1.tv_sec;
    388 		ts.tv_nsec = (tv2.tv_usec - tv1.tv_usec) * MILISEC;
    389 		if (ts.tv_nsec < 0) {
    390 			ts.tv_nsec += NANOSEC;
    391 			ts.tv_sec -= 1;
    392 		}
    393 		/* increase/decrease the sleep time based on d_flag value */
    394 		ts.tv_sec = ts.tv_sec * d_flag / 100;
    395 		ts.tv_nsec = ts.tv_nsec * d_flag / 100;
    396 		if (ts.tv_nsec > NANOSEC) {
    397 			ts.tv_sec += ts.tv_nsec / NANOSEC;
    398 			ts.tv_nsec %= NANOSEC;
    399 		}
    400 		if (ts.tv_sec || ts.tv_nsec)
    401 			nanosleep(&ts, NULL);
    402 #else
    403 #ifdef HAVE_USLEEP
    404 		struct timeval tv;
    405 		tv.tv_sec = tv2.tv_sec - tv1.tv_sec;
    406 		tv.tv_usec = tv2.tv_usec - tv1.tv_usec;
    407 		tv.tv_sec = tv.tv_sec * d_flag / 100;
    408 		tv.tv_usec = tv.tv_usec * d_flag / 100;
    409 		if (tv.tv_usec > 1000000) {
    410 			tv.tv_sec += tv.tv_usec / 1000000;
    411 			tv.tv_usec %= 1000000;
    412 		}
    413 		if (tv.tv_sec)
    414 			sleep(tv.tv_sec);
    415 		if (tv.tv_usec)
    416 			usleep(tv.tv_usec);
    417 #endif
    418 #endif
    419 	}
    420 	return got;
    421 }
    422 
    423 /*
    424  * Perform a write of a sequence of blocks; return the number of blocks
    425  *    successfully sequentially written.
    426  */
    427 static int do_write(int dev, unsigned char * buffer, int try, int block_size,
    428 		    unsigned long current_block)
    429 {
    430 	long got;
    431 
    432 #if 0
    433 	printf("do_write: block %lu, try %d\n", current_block, try);
    434 #endif
    435 	set_o_direct(dev, buffer, try * block_size,
    436 		     ((ext2_loff_t) current_block) * block_size);
    437 
    438 	if (v_flag > 1)
    439 		print_status();
    440 
    441 	/* Seek to the correct loc. */
    442 	if (ext2fs_llseek (dev, (ext2_loff_t) current_block * block_size,
    443 			 SEEK_SET) != (ext2_loff_t) current_block * block_size)
    444 		com_err (program_name, errno, "%s", _("during seek"));
    445 
    446 	/* Try the write */
    447 	got = write (dev, buffer, try * block_size);
    448 	if (got < 0)
    449 		got = 0;
    450 	if (got & 511)
    451 		fprintf(stderr, "Weird value (%ld) in do_write\n", got);
    452 	got /= block_size;
    453 	return got;
    454 }
    455 
    456 static int host_dev;
    457 
    458 static void flush_bufs(void)
    459 {
    460 	errcode_t	retval;
    461 
    462 #ifdef O_DIRECT
    463 	if (!use_buffered_io)
    464 		return;
    465 #endif
    466 	retval = ext2fs_sync_device(host_dev, 1);
    467 	if (retval)
    468 		com_err(program_name, retval, "%s",
    469 			_("during ext2fs_sync_device"));
    470 }
    471 
    472 static unsigned int test_ro (int dev, blk_t last_block,
    473 			     int block_size, blk_t first_block,
    474 			     unsigned int blocks_at_once)
    475 {
    476 	unsigned char * blkbuf;
    477 	int try;
    478 	int got;
    479 	unsigned int bb_count = 0;
    480 	errcode_t errcode;
    481 	blk_t recover_block = ~0;
    482 
    483 	/* set up abend handler */
    484 	capture_terminate(NULL);
    485 
    486 	errcode = ext2fs_badblocks_list_iterate_begin(bb_list,&bb_iter);
    487 	if (errcode) {
    488 		com_err(program_name, errcode, "%s",
    489 			_("while beginning bad block list iteration"));
    490 		exit (1);
    491 	}
    492 	do {
    493 		ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
    494 	} while (next_bad && next_bad < first_block);
    495 
    496 	if (t_flag) {
    497 		blkbuf = allocate_buffer((blocks_at_once + 1) * block_size);
    498 	} else {
    499 		blkbuf = allocate_buffer(blocks_at_once * block_size);
    500 	}
    501 	if (!blkbuf)
    502 	{
    503 		com_err(program_name, ENOMEM, "%s",
    504 			_("while allocating buffers"));
    505 		exit (1);
    506 	}
    507 	if (v_flag) {
    508 		fprintf(stderr, _("Checking blocks %lu to %lu\n"),
    509 			(unsigned long)first_block,
    510 			(unsigned long)last_block - 1);
    511 	}
    512 	if (t_flag) {
    513 		fputs(_("Checking for bad blocks in read-only mode\n"), stderr);
    514 		pattern_fill(blkbuf + blocks_at_once * block_size,
    515 			     t_patts[0], block_size);
    516 	}
    517 	flush_bufs();
    518 	try = blocks_at_once;
    519 	currently_testing = first_block;
    520 	num_blocks = last_block - 1;
    521 	if (!t_flag && (s_flag || v_flag))
    522 		fputs(_("Checking for bad blocks (read-only test): "), stderr);
    523 	if (s_flag && v_flag <= 1)
    524 		alarm_intr(SIGALRM);
    525 	while (currently_testing < last_block)
    526 	{
    527 		if (max_bb && bb_count >= max_bb) {
    528 			if (s_flag || v_flag) {
    529 				fputs(_("Too many bad blocks, aborting test\n"), stderr);
    530 			}
    531 			break;
    532 		}
    533 		if (next_bad) {
    534 			if (currently_testing == next_bad) {
    535 				/* fprintf (out, "%lu\n", nextbad); */
    536 				ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
    537 				currently_testing++;
    538 				continue;
    539 			}
    540 			else if (currently_testing + try > next_bad)
    541 				try = next_bad - currently_testing;
    542 		}
    543 		if (currently_testing + try > last_block)
    544 			try = last_block - currently_testing;
    545 		got = do_read (dev, blkbuf, try, block_size, currently_testing);
    546 		if (t_flag) {
    547 			/* test the comparison between all the
    548 			   blocks successfully read  */
    549 			int i;
    550 			for (i = 0; i < got; ++i)
    551 				if (memcmp (blkbuf+i*block_size,
    552 					    blkbuf+blocks_at_once*block_size,
    553 					    block_size))
    554 					bb_count += bb_output(currently_testing + i, CORRUPTION_ERROR);
    555 		}
    556 		if (got == 0 && try == 1)
    557 			bb_count += bb_output(currently_testing++, READ_ERROR);
    558 		currently_testing += got;
    559 		if (got != try) {
    560 			try = 1;
    561 			if (recover_block == ~0U)
    562 				recover_block = currently_testing - got +
    563 					blocks_at_once;
    564 			continue;
    565 		} else if (currently_testing == recover_block) {
    566 			try = blocks_at_once;
    567 			recover_block = ~0;
    568 		}
    569 	}
    570 	num_blocks = 0;
    571 	alarm(0);
    572 	if (s_flag || v_flag)
    573 		fputs(_(done_string), stderr);
    574 
    575 	fflush (stderr);
    576 	free (blkbuf);
    577 
    578 	ext2fs_badblocks_list_iterate_end(bb_iter);
    579 
    580 	uncapture_terminate();
    581 
    582 	return bb_count;
    583 }
    584 
    585 static unsigned int test_rw (int dev, blk_t last_block,
    586 			     int block_size, blk_t first_block,
    587 			     unsigned int blocks_at_once)
    588 {
    589 	unsigned char *buffer, *read_buffer;
    590 	const unsigned int patterns[] = {0xaa, 0x55, 0xff, 0x00};
    591 	const unsigned int *pattern;
    592 	int i, try, got, nr_pattern, pat_idx;
    593 	unsigned int bb_count = 0;
    594 	blk_t recover_block = ~0;
    595 
    596 	/* set up abend handler */
    597 	capture_terminate(NULL);
    598 
    599 	buffer = allocate_buffer(2 * blocks_at_once * block_size);
    600 	read_buffer = buffer + blocks_at_once * block_size;
    601 
    602 	if (!buffer) {
    603 		com_err(program_name, ENOMEM, "%s",
    604 			_("while allocating buffers"));
    605 		exit (1);
    606 	}
    607 
    608 	flush_bufs();
    609 
    610 	if (v_flag) {
    611 		fputs(_("Checking for bad blocks in read-write mode\n"),
    612 		      stderr);
    613 		fprintf(stderr, _("From block %lu to %lu\n"),
    614 			(unsigned long) first_block,
    615 			(unsigned long) last_block - 1);
    616 	}
    617 	if (t_flag) {
    618 		pattern = t_patts;
    619 		nr_pattern = t_flag;
    620 	} else {
    621 		pattern = patterns;
    622 		nr_pattern = sizeof(patterns) / sizeof(patterns[0]);
    623 	}
    624 	for (pat_idx = 0; pat_idx < nr_pattern; pat_idx++) {
    625 		pattern_fill(buffer, pattern[pat_idx],
    626 			     blocks_at_once * block_size);
    627 		num_blocks = last_block - 1;
    628 		currently_testing = first_block;
    629 		if (s_flag && v_flag <= 1)
    630 			alarm_intr(SIGALRM);
    631 
    632 		try = blocks_at_once;
    633 		while (currently_testing < last_block) {
    634 			if (max_bb && bb_count >= max_bb) {
    635 				if (s_flag || v_flag) {
    636 					fputs(_("Too many bad blocks, aborting test\n"), stderr);
    637 				}
    638 				break;
    639 			}
    640 			if (currently_testing + try > last_block)
    641 				try = last_block - currently_testing;
    642 			got = do_write(dev, buffer, try, block_size,
    643 					currently_testing);
    644 			if (v_flag > 1)
    645 				print_status();
    646 
    647 			if (got == 0 && try == 1)
    648 				bb_count += bb_output(currently_testing++, WRITE_ERROR);
    649 			currently_testing += got;
    650 			if (got != try) {
    651 				try = 1;
    652 				if (recover_block == ~0U)
    653 					recover_block = currently_testing -
    654 						got + blocks_at_once;
    655 				continue;
    656 			} else if (currently_testing == recover_block) {
    657 				try = blocks_at_once;
    658 				recover_block = ~0;
    659 			}
    660 		}
    661 
    662 		num_blocks = 0;
    663 		alarm (0);
    664 		if (s_flag | v_flag)
    665 			fputs(_(done_string), stderr);
    666 		flush_bufs();
    667 		if (s_flag | v_flag)
    668 			fputs(_("Reading and comparing: "), stderr);
    669 		num_blocks = last_block;
    670 		currently_testing = first_block;
    671 		if (s_flag && v_flag <= 1)
    672 			alarm_intr(SIGALRM);
    673 
    674 		try = blocks_at_once;
    675 		while (currently_testing < last_block) {
    676 			if (max_bb && bb_count >= max_bb) {
    677 				if (s_flag || v_flag) {
    678 					fputs(_("Too many bad blocks, aborting test\n"), stderr);
    679 				}
    680 				break;
    681 			}
    682 			if (currently_testing + try > last_block)
    683 				try = last_block - currently_testing;
    684 			got = do_read (dev, read_buffer, try, block_size,
    685 				       currently_testing);
    686 			if (got == 0 && try == 1)
    687 				bb_count += bb_output(currently_testing++, READ_ERROR);
    688 			currently_testing += got;
    689 			if (got != try) {
    690 				try = 1;
    691 				if (recover_block == ~0U)
    692 					recover_block = currently_testing -
    693 						got + blocks_at_once;
    694 				continue;
    695 			} else if (currently_testing == recover_block) {
    696 				try = blocks_at_once;
    697 				recover_block = ~0U;
    698 			}
    699 			for (i=0; i < got; i++) {
    700 				if (memcmp(read_buffer + i * block_size,
    701 					   buffer + i * block_size,
    702 					   block_size))
    703 					bb_count += bb_output(currently_testing+i, CORRUPTION_ERROR);
    704 			}
    705 			if (v_flag > 1)
    706 				print_status();
    707 		}
    708 
    709 		num_blocks = 0;
    710 		alarm (0);
    711 		if (s_flag | v_flag)
    712 			fputs(_(done_string), stderr);
    713 		flush_bufs();
    714 	}
    715 	uncapture_terminate();
    716 	free(buffer);
    717 	return bb_count;
    718 }
    719 
    720 struct saved_blk_record {
    721 	blk_t	block;
    722 	int	num;
    723 };
    724 
    725 static unsigned int test_nd (int dev, blk_t last_block,
    726 			     int block_size, blk_t first_block,
    727 			     unsigned int blocks_at_once)
    728 {
    729 	unsigned char *blkbuf, *save_ptr, *test_ptr, *read_ptr;
    730 	unsigned char *test_base, *save_base, *read_base;
    731 	int try, i;
    732 	const unsigned int patterns[] = { ~0 };
    733 	const unsigned int *pattern;
    734 	int nr_pattern, pat_idx;
    735 	int got, used2, written;
    736 	blk_t save_currently_testing;
    737 	struct saved_blk_record *test_record;
    738 	/* This is static to prevent being clobbered by the longjmp */
    739 	static int num_saved;
    740 	jmp_buf terminate_env;
    741 	errcode_t errcode;
    742 	unsigned long buf_used;
    743 	static unsigned int bb_count;
    744 	unsigned int granularity = blocks_at_once;
    745 	blk_t recover_block = ~0U;
    746 
    747 	bb_count = 0;
    748 	errcode = ext2fs_badblocks_list_iterate_begin(bb_list,&bb_iter);
    749 	if (errcode) {
    750 		com_err(program_name, errcode, "%s",
    751 			_("while beginning bad block list iteration"));
    752 		exit (1);
    753 	}
    754 	do {
    755 		ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
    756 	} while (next_bad && next_bad < first_block);
    757 
    758 	blkbuf = allocate_buffer(3 * blocks_at_once * block_size);
    759 	test_record = malloc(blocks_at_once * sizeof(struct saved_blk_record));
    760 	if (!blkbuf || !test_record) {
    761 		com_err(program_name, ENOMEM, "%s",
    762 			_("while allocating buffers"));
    763 		exit (1);
    764 	}
    765 
    766 	save_base = blkbuf;
    767 	test_base = blkbuf + (blocks_at_once * block_size);
    768 	read_base = blkbuf + (2 * blocks_at_once * block_size);
    769 
    770 	num_saved = 0;
    771 
    772 	flush_bufs();
    773 	if (v_flag) {
    774 	    fputs(_("Checking for bad blocks in non-destructive read-write mode\n"), stderr);
    775 	    fprintf (stderr, _("From block %lu to %lu\n"),
    776 		     (unsigned long) first_block,
    777 		     (unsigned long) last_block - 1);
    778 	}
    779 	if (s_flag || v_flag > 1) {
    780 		fputs(_("Checking for bad blocks (non-destructive read-write test)\n"), stderr);
    781 	}
    782 	if (setjmp(terminate_env)) {
    783 		/*
    784 		 * Abnormal termination by a signal is handled here.
    785 		 */
    786 		signal (SIGALRM, SIG_IGN);
    787 		fputs(_("\nInterrupt caught, cleaning up\n"), stderr);
    788 
    789 		save_ptr = save_base;
    790 		for (i=0; i < num_saved; i++) {
    791 			do_write(dev, save_ptr, test_record[i].num,
    792 				 block_size, test_record[i].block);
    793 			save_ptr += test_record[i].num * block_size;
    794 		}
    795 		fflush (out);
    796 		exit(1);
    797 	}
    798 
    799 	/* set up abend handler */
    800 	capture_terminate(terminate_env);
    801 
    802 	if (t_flag) {
    803 		pattern = t_patts;
    804 		nr_pattern = t_flag;
    805 	} else {
    806 		pattern = patterns;
    807 		nr_pattern = sizeof(patterns) / sizeof(patterns[0]);
    808 	}
    809 	for (pat_idx = 0; pat_idx < nr_pattern; pat_idx++) {
    810 		pattern_fill(test_base, pattern[pat_idx],
    811 			     blocks_at_once * block_size);
    812 
    813 		buf_used = 0;
    814 		bb_count = 0;
    815 		save_ptr = save_base;
    816 		test_ptr = test_base;
    817 		currently_testing = first_block;
    818 		num_blocks = last_block - 1;
    819 		if (s_flag && v_flag <= 1)
    820 			alarm_intr(SIGALRM);
    821 
    822 		while (currently_testing < last_block) {
    823 			if (max_bb && bb_count >= max_bb) {
    824 				if (s_flag || v_flag) {
    825 					fputs(_("Too many bad blocks, aborting test\n"), stderr);
    826 				}
    827 				break;
    828 			}
    829 			got = try = granularity - buf_used;
    830 			if (next_bad) {
    831 				if (currently_testing == next_bad) {
    832 					/* fprintf (out, "%lu\n", nextbad); */
    833 					ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
    834 					currently_testing++;
    835 					goto check_for_more;
    836 				}
    837 				else if (currently_testing + try > next_bad)
    838 					try = next_bad - currently_testing;
    839 			}
    840 			if (currently_testing + try > last_block)
    841 				try = last_block - currently_testing;
    842 			got = do_read (dev, save_ptr, try, block_size,
    843 				       currently_testing);
    844 			if (got == 0) {
    845 				if (recover_block == ~0U)
    846 					recover_block = currently_testing +
    847 						blocks_at_once;
    848 				if (granularity != 1) {
    849 					granularity = 1;
    850 					continue;
    851 				}
    852 				/* First block must have been bad. */
    853 				bb_count += bb_output(currently_testing++, READ_ERROR);
    854 				goto check_for_more;
    855 			}
    856 
    857 			/*
    858 			 * Note the fact that we've saved this much data
    859 			 * *before* we overwrite it with test data
    860 			 */
    861 			test_record[num_saved].block = currently_testing;
    862 			test_record[num_saved].num = got;
    863 			num_saved++;
    864 
    865 			/* Write the test data */
    866 			written = do_write (dev, test_ptr, got, block_size,
    867 					    currently_testing);
    868 			if (written != got)
    869 				com_err (program_name, errno,
    870 					 _("during test data write, block %lu"),
    871 					 (unsigned long) currently_testing +
    872 					 written);
    873 
    874 			buf_used += got;
    875 			save_ptr += got * block_size;
    876 			test_ptr += got * block_size;
    877 			currently_testing += got;
    878 			if (got != try) {
    879 				try = 1;
    880 				if (recover_block == ~0U)
    881 					recover_block = currently_testing -
    882 						got + blocks_at_once;
    883 				continue;
    884 			}
    885 
    886 		check_for_more:
    887 			/*
    888 			 * If there's room for more blocks to be tested this
    889 			 * around, and we're not done yet testing the disk, go
    890 			 * back and get some more blocks.
    891 			 */
    892 			if ((buf_used != granularity) &&
    893 			    (currently_testing < last_block))
    894 				continue;
    895 
    896 			if (currently_testing >= recover_block) {
    897 				granularity = blocks_at_once;
    898 				recover_block = ~0;
    899 			}
    900 
    901 			flush_bufs();
    902 			save_currently_testing = currently_testing;
    903 
    904 			/*
    905 			 * for each contiguous block that we read into the
    906 			 * buffer (and wrote test data into afterwards), read
    907 			 * it back (looping if necessary, to get past newly
    908 			 * discovered unreadable blocks, of which there should
    909 			 * be none, but with a hard drive which is unreliable,
    910 			 * it has happened), and compare with the test data
    911 			 * that was written; output to the bad block list if
    912 			 * it doesn't match.
    913 			 */
    914 			used2 = 0;
    915 			save_ptr = save_base;
    916 			test_ptr = test_base;
    917 			read_ptr = read_base;
    918 			try = 0;
    919 
    920 			while (1) {
    921 				if (try == 0) {
    922 					if (used2 >= num_saved)
    923 						break;
    924 					currently_testing = test_record[used2].block;
    925 					try = test_record[used2].num;
    926 					used2++;
    927 				}
    928 
    929 				got = do_read (dev, read_ptr, try,
    930 					       block_size, currently_testing);
    931 
    932 				/* test the comparison between all the
    933 				   blocks successfully read  */
    934 				for (i = 0; i < got; ++i)
    935 					if (memcmp (test_ptr+i*block_size,
    936 						    read_ptr+i*block_size, block_size))
    937 						bb_count += bb_output(currently_testing + i, CORRUPTION_ERROR);
    938 				if (got < try) {
    939 					bb_count += bb_output(currently_testing + got, READ_ERROR);
    940 					got++;
    941 				}
    942 
    943 				/* write back original data */
    944 				do_write (dev, save_ptr, got,
    945 					  block_size, currently_testing);
    946 				save_ptr += got * block_size;
    947 
    948 				currently_testing += got;
    949 				test_ptr += got * block_size;
    950 				read_ptr += got * block_size;
    951 				try -= got;
    952 			}
    953 
    954 			/* empty the buffer so it can be reused */
    955 			num_saved = 0;
    956 			buf_used = 0;
    957 			save_ptr = save_base;
    958 			test_ptr = test_base;
    959 			currently_testing = save_currently_testing;
    960 		}
    961 		num_blocks = 0;
    962 		alarm(0);
    963 		if (s_flag || v_flag > 1)
    964 			fputs(_(done_string), stderr);
    965 
    966 		flush_bufs();
    967 	}
    968 	uncapture_terminate();
    969 	fflush(stderr);
    970 	free(blkbuf);
    971 	free(test_record);
    972 
    973 	ext2fs_badblocks_list_iterate_end(bb_iter);
    974 
    975 	return bb_count;
    976 }
    977 
    978 static void check_mount(char *device_name)
    979 {
    980 	errcode_t	retval;
    981 	int		mount_flags;
    982 
    983 	retval = ext2fs_check_if_mounted(device_name, &mount_flags);
    984 	if (retval) {
    985 		com_err("ext2fs_check_if_mount", retval,
    986 			_("while determining whether %s is mounted."),
    987 			device_name);
    988 		return;
    989 	}
    990 	if (mount_flags & EXT2_MF_MOUNTED) {
    991 		fprintf(stderr, _("%s is mounted; "), device_name);
    992 		if (force) {
    993 			fputs(_("badblocks forced anyway.  "
    994 				"Hope /etc/mtab is incorrect.\n"), stderr);
    995 			return;
    996 		}
    997 	abort_badblocks:
    998 		fputs(_("it's not safe to run badblocks!\n"), stderr);
    999 		exit(1);
   1000 	}
   1001 
   1002 	if ((mount_flags & EXT2_MF_BUSY) && !exclusive_ok) {
   1003 		fprintf(stderr, _("%s is apparently in use by the system; "),
   1004 			device_name);
   1005 		if (force)
   1006 			fputs(_("badblocks forced anyway.\n"), stderr);
   1007 		else
   1008 			goto abort_badblocks;
   1009 	}
   1010 
   1011 }
   1012 
   1013 /*
   1014  * This function will convert a string to an unsigned long, printing
   1015  * an error message if it fails, and returning success or failure in err.
   1016  */
   1017 static unsigned int parse_uint(const char *str, const char *descr)
   1018 {
   1019 	char		*tmp;
   1020 	unsigned long	ret;
   1021 
   1022 	errno = 0;
   1023 	ret = strtoul(str, &tmp, 0);
   1024 	if (*tmp || errno || (ret > UINT_MAX) ||
   1025 	    (ret == ULONG_MAX && errno == ERANGE)) {
   1026 		com_err (program_name, 0, _("invalid %s - %s"), descr, str);
   1027 		exit (1);
   1028 	}
   1029 	return ret;
   1030 }
   1031 
   1032 int main (int argc, char ** argv)
   1033 {
   1034 	int c;
   1035 	char * device_name;
   1036 	char * host_device_name = NULL;
   1037 	char * input_file = NULL;
   1038 	char * output_file = NULL;
   1039 	FILE * in = NULL;
   1040 	int block_size = 1024;
   1041 	unsigned int blocks_at_once = 64;
   1042 	blk64_t last_block, first_block;
   1043 	int num_passes = 0;
   1044 	int passes_clean = 0;
   1045 	int dev;
   1046 	errcode_t errcode;
   1047 	unsigned int pattern;
   1048 	unsigned int (*test_func)(int, blk_t,
   1049 				  int, blk_t,
   1050 				  unsigned int);
   1051 	int open_flag;
   1052 	long sysval;
   1053 	blk64_t inblk;
   1054 
   1055 	setbuf(stdout, NULL);
   1056 	setbuf(stderr, NULL);
   1057 #ifdef ENABLE_NLS
   1058 	setlocale(LC_MESSAGES, "");
   1059 	setlocale(LC_CTYPE, "");
   1060 	bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
   1061 	textdomain(NLS_CAT_NAME);
   1062 	set_com_err_gettext(gettext);
   1063 #endif
   1064 	srandom((unsigned int)time(NULL));  /* simple randomness is enough */
   1065 	test_func = test_ro;
   1066 
   1067 	/* Determine the system page size if possible */
   1068 #ifdef HAVE_SYSCONF
   1069 #if (!defined(_SC_PAGESIZE) && defined(_SC_PAGE_SIZE))
   1070 #define _SC_PAGESIZE _SC_PAGE_SIZE
   1071 #endif
   1072 #ifdef _SC_PAGESIZE
   1073 	sysval = sysconf(_SC_PAGESIZE);
   1074 	if (sysval > 0)
   1075 		sys_page_size = sysval;
   1076 #endif /* _SC_PAGESIZE */
   1077 #endif /* HAVE_SYSCONF */
   1078 
   1079 	if (argc && *argv)
   1080 		program_name = *argv;
   1081 	while ((c = getopt (argc, argv, "b:d:e:fi:o:svwnc:p:h:t:BX")) != EOF) {
   1082 		switch (c) {
   1083 		case 'b':
   1084 			block_size = parse_uint(optarg, "block size");
   1085 			break;
   1086 		case 'f':
   1087 			force++;
   1088 			break;
   1089 		case 'i':
   1090 			input_file = optarg;
   1091 			break;
   1092 		case 'o':
   1093 			output_file = optarg;
   1094 			break;
   1095 		case 's':
   1096 			s_flag = 1;
   1097 			break;
   1098 		case 'v':
   1099 			v_flag++;
   1100 			break;
   1101 		case 'w':
   1102 			if (w_flag)
   1103 				exclusive_usage();
   1104 			test_func = test_rw;
   1105 			w_flag = 1;
   1106 			break;
   1107 		case 'n':
   1108 			if (w_flag)
   1109 				exclusive_usage();
   1110 			test_func = test_nd;
   1111 			w_flag = 2;
   1112 			break;
   1113 		case 'c':
   1114 			blocks_at_once = parse_uint(optarg, "blocks at once");
   1115 			break;
   1116 		case 'e':
   1117 			max_bb = parse_uint(optarg, "max bad block count");
   1118 			break;
   1119 		case 'd':
   1120 			d_flag = parse_uint(optarg, "read delay factor");
   1121 			break;
   1122 		case 'p':
   1123 			num_passes = parse_uint(optarg,
   1124 						"number of clean passes");
   1125 			break;
   1126 		case 'h':
   1127 			host_device_name = optarg;
   1128 			break;
   1129 		case 't':
   1130 			if (t_flag + 1 > t_max) {
   1131 				unsigned int *t_patts_new;
   1132 
   1133 				t_patts_new = realloc(t_patts, sizeof(int) *
   1134 						      (t_max + T_INC));
   1135 				if (!t_patts_new) {
   1136 					com_err(program_name, ENOMEM,
   1137 						_("can't allocate memory for "
   1138 						  "test_pattern - %s"),
   1139 						optarg);
   1140 					exit(1);
   1141 				}
   1142 				t_patts = t_patts_new;
   1143 				t_max += T_INC;
   1144 			}
   1145 			if (!strcmp(optarg, "r") || !strcmp(optarg,"random")) {
   1146 				t_patts[t_flag++] = ~0;
   1147 			} else {
   1148 				pattern = parse_uint(optarg, "test pattern");
   1149 				if (pattern == (unsigned int) ~0)
   1150 					pattern = 0xffff;
   1151 				t_patts[t_flag++] = pattern;
   1152 			}
   1153 			break;
   1154 		case 'B':
   1155 			use_buffered_io = 1;
   1156 			break;
   1157 		case 'X':
   1158 			exclusive_ok++;
   1159 			break;
   1160 		default:
   1161 			usage();
   1162 		}
   1163 	}
   1164 	if (!w_flag) {
   1165 		if (t_flag > 1) {
   1166 			com_err(program_name, 0, "%s",
   1167 				_("Maximum of one test_pattern may be "
   1168 				  "specified in read-only mode"));
   1169 			exit(1);
   1170 		}
   1171 		if (t_patts && (t_patts[0] == (unsigned int) ~0)) {
   1172 			com_err(program_name, 0, "%s",
   1173 				_("Random test_pattern is not allowed "
   1174 				  "in read-only mode"));
   1175 			exit(1);
   1176 		}
   1177 	}
   1178 	if (optind > argc - 1)
   1179 		usage();
   1180 	device_name = argv[optind++];
   1181 	if (optind > argc - 1) {
   1182 		errcode = ext2fs_get_device_size2(device_name,
   1183 						 block_size,
   1184 						 &last_block);
   1185 		if (errcode == EXT2_ET_UNIMPLEMENTED) {
   1186 			com_err(program_name, 0, "%s",
   1187 				_("Couldn't determine device size; you "
   1188 				  "must specify\nthe size manually\n"));
   1189 			exit(1);
   1190 		}
   1191 		if (errcode) {
   1192 			com_err(program_name, errcode, "%s",
   1193 				_("while trying to determine device size"));
   1194 			exit(1);
   1195 		}
   1196 	} else {
   1197 		errno = 0;
   1198 		last_block = parse_uint(argv[optind], _("last block"));
   1199 		last_block++;
   1200 		optind++;
   1201 	}
   1202 	if (optind <= argc-1) {
   1203 		errno = 0;
   1204 		first_block = parse_uint(argv[optind], _("first block"));
   1205 	} else first_block = 0;
   1206 	if (first_block >= last_block) {
   1207 	    com_err (program_name, 0, _("invalid starting block (%llu): must be less than %llu"),
   1208 		     first_block, last_block);
   1209 	    exit (1);
   1210 	}
   1211 	/* ext2 badblocks file can't handle large values */
   1212 	if (last_block >> 32) {
   1213 		com_err(program_name, EOVERFLOW,
   1214 			_("invalid end block (%llu): must be 32-bit value"),
   1215 			last_block);
   1216 		exit(1);
   1217 	}
   1218 	if (w_flag)
   1219 		check_mount(device_name);
   1220 
   1221 	gettimeofday(&time_start, 0);
   1222 	open_flag = O_LARGEFILE | (w_flag ? O_RDWR : O_RDONLY);
   1223 	dev = open (device_name, open_flag);
   1224 	if (dev == -1) {
   1225 		com_err (program_name, errno, _("while trying to open %s"),
   1226 			 device_name);
   1227 		exit (1);
   1228 	}
   1229 	if (host_device_name) {
   1230 		host_dev = open (host_device_name, open_flag);
   1231 		if (host_dev == -1) {
   1232 			com_err (program_name, errno,
   1233 				 _("while trying to open %s"),
   1234 				 host_device_name);
   1235 			exit (1);
   1236 		}
   1237 	} else
   1238 		host_dev = dev;
   1239 	if (input_file) {
   1240 		if (strcmp (input_file, "-") == 0)
   1241 			in = stdin;
   1242 		else {
   1243 			in = fopen (input_file, "r");
   1244 			if (in == NULL)
   1245 			{
   1246 				com_err (program_name, errno,
   1247 					 _("while trying to open %s"),
   1248 					 input_file);
   1249 				exit (1);
   1250 			}
   1251 		}
   1252 	}
   1253 	if (output_file && strcmp (output_file, "-") != 0)
   1254 	{
   1255 		out = fopen (output_file, "w");
   1256 		if (out == NULL)
   1257 		{
   1258 			com_err (program_name, errno,
   1259 				 _("while trying to open %s"),
   1260 				 output_file);
   1261 			exit (1);
   1262 		}
   1263 	}
   1264 	else
   1265 		out = stdout;
   1266 
   1267 	errcode = ext2fs_badblocks_list_create(&bb_list,0);
   1268 	if (errcode) {
   1269 		com_err(program_name, errcode, "%s",
   1270 			_("while creating in-memory bad blocks list"));
   1271 		exit (1);
   1272 	}
   1273 
   1274 	if (in) {
   1275 		for(;;) {
   1276 			switch (fscanf(in, "%llu\n", &inblk)) {
   1277 				case 0:
   1278 					com_err(program_name, 0, "%s",
   1279 						_("input file - bad format"));
   1280 					exit (1);
   1281 				case EOF:
   1282 					break;
   1283 				default:
   1284 					if (inblk >> 32) {
   1285 						com_err(program_name,
   1286 							EOVERFLOW, "%s",
   1287 						_("while adding to in-memory "
   1288 						  "bad block list"));
   1289 						exit(1);
   1290 					}
   1291 					next_bad = inblk;
   1292 					errcode = ext2fs_badblocks_list_add(bb_list,next_bad);
   1293 					if (errcode) {
   1294 						com_err(program_name, errcode,
   1295 							"%s",
   1296 						_("while adding to in-memory "
   1297 						  "bad block list"));
   1298 						exit (1);
   1299 					}
   1300 					continue;
   1301 			}
   1302 			break;
   1303 		}
   1304 
   1305 		if (in != stdin)
   1306 			fclose (in);
   1307 	}
   1308 
   1309 	do {
   1310 		unsigned int bb_count;
   1311 
   1312 		bb_count = test_func(dev, last_block, block_size,
   1313 				     first_block, blocks_at_once);
   1314 		if (bb_count)
   1315 			passes_clean = 0;
   1316 		else
   1317 			++passes_clean;
   1318 
   1319 		if (v_flag)
   1320 			fprintf(stderr,
   1321 				_("Pass completed, %u bad blocks found. (%d/%d/%d errors)\n"),
   1322 				bb_count, num_read_errors, num_write_errors, num_corruption_errors);
   1323 
   1324 	} while (passes_clean < num_passes);
   1325 
   1326 	close (dev);
   1327 	if (out != stdout)
   1328 		fclose (out);
   1329 	free(t_patts);
   1330 	return 0;
   1331 }
   1332