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