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