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