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