badblocks.c revision cd130a0887b5e5e9436533bbcda4c17ec5202788
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#include <errno.h>
33#include <fcntl.h>
34#ifdef HAVE_GETOPT_H
35#include <getopt.h>
36#else
37extern char *optarg;
38extern int optind;
39#endif
40#include <signal.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <unistd.h>
45#include <setjmp.h>
46
47#include <sys/ioctl.h>
48#include <sys/types.h>
49
50#include "et/com_err.h"
51#include "ext2fs/ext2_io.h"
52#include <linux/ext2_fs.h>
53#include "ext2fs/ext2fs.h"
54#include "nls-enable.h"
55
56const char * program_name = "badblocks";
57const char * done_string = N_("done                        \n");
58
59static int v_flag = 0;			/* verbose */
60static int w_flag = 0;			/* do r/w test: 0=no, 1=yes,
61					 * 2=non-destructive */
62static int s_flag = 0;			/* show progress of test */
63static int force = 0;			/* force check of mounted device */
64
65static void usage(void)
66{
67	fprintf(stderr, _("Usage: %s [-b block_size] [-i input_file] [-o output_file] [-svwnf]\n [-c blocks_at_once] [-p num_passes] device [last_block [start_count]]\n"),
68		 program_name);
69	exit (1);
70}
71
72static unsigned long currently_testing = 0;
73static unsigned long num_blocks = 0;
74static ext2_badblocks_list bb_list = NULL;
75static FILE *out;
76static blk_t next_bad = 0;
77static ext2_badblocks_iterate bb_iter = NULL;
78
79/*
80 * This routine reports a new bad block.  If the bad block has already
81 * been seen before, then it returns 0; otherwise it returns 1.
82 */
83static int bb_output (unsigned long bad)
84{
85	errcode_t errcode;
86
87	if (ext2fs_badblocks_list_test(bb_list, bad))
88		return 0;
89
90	fprintf (out, "%lu\n", bad);
91
92	errcode = ext2fs_badblocks_list_add (bb_list, bad);
93	if (errcode) {
94		com_err (program_name, errcode, "adding to in-memory bad block list");
95		exit (1);
96	}
97
98	/* kludge:
99	   increment the iteration through the bb_list if
100	   an element was just added before the current iteration
101	   position.  This should not cause next_bad to change. */
102	if (bb_iter && bad < next_bad)
103		ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
104	return 1;
105}
106
107static void print_status(void)
108{
109	fprintf(stderr, "%9ld/%9ld", currently_testing, num_blocks);
110	fprintf(stderr, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
111	fflush (stderr);
112}
113
114static void alarm_intr(int alnum)
115{
116	signal (SIGALRM, alarm_intr);
117	alarm(1);
118	if (!num_blocks)
119		return;
120	fprintf(stderr, "%9ld/%9ld", currently_testing, num_blocks);
121	fprintf(stderr, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
122	fflush (stderr);
123}
124
125static void *terminate_addr = NULL;
126
127static void terminate_intr(int signo)
128{
129	if (terminate_addr)
130		longjmp(terminate_addr,1);
131	exit(1);
132}
133
134static void capture_terminate(jmp_buf term_addr)
135{
136	terminate_addr = term_addr;
137	signal (SIGHUP, terminate_intr);
138	signal (SIGINT, terminate_intr);
139	signal (SIGPIPE, terminate_intr);
140	signal (SIGTERM, terminate_intr);
141	signal (SIGUSR1, terminate_intr);
142	signal (SIGUSR2, terminate_intr);
143}
144
145static void uncapture_terminate(void)
146{
147	terminate_addr = NULL;
148	signal (SIGHUP, SIG_DFL);
149	signal (SIGINT, SIG_DFL);
150	signal (SIGPIPE, SIG_DFL);
151	signal (SIGTERM, SIG_DFL);
152	signal (SIGUSR1, SIG_DFL);
153	signal (SIGUSR2, SIG_DFL);
154}
155
156/*
157 * Perform a read of a sequence of blocks; return the number of blocks
158 *    successfully sequentially read.
159 */
160static long do_read (int dev, char * buffer, int try, int block_size,
161		     unsigned long current_block)
162{
163	long got;
164
165	if (v_flag > 1)
166		print_status();
167
168	/* Seek to the correct loc. */
169	if (ext2fs_llseek (dev, (ext2_loff_t) current_block * block_size,
170			 SEEK_SET) != (ext2_loff_t) current_block * block_size)
171		com_err (program_name, errno, _("during seek"));
172
173	/* Try the read */
174	got = read (dev, buffer, try * block_size);
175	if (got < 0)
176		got = 0;
177	if (got & 511)
178		fprintf(stderr, _("Weird value (%ld) in do_read\n"), got);
179	got /= block_size;
180	return got;
181}
182
183/*
184 * Perform a write of a sequence of blocks; return the number of blocks
185 *    successfully sequentially written.
186 */
187static long do_write (int dev, char * buffer, int try, int block_size,
188		     unsigned long current_block)
189{
190	long got;
191
192	if (v_flag > 1)
193		print_status();
194
195	/* Seek to the correct loc. */
196	if (ext2fs_llseek (dev, (ext2_loff_t) current_block * block_size,
197			 SEEK_SET) != (ext2_loff_t) current_block * block_size)
198		com_err (program_name, errno, _("during seek"));
199
200	/* Try the write */
201	got = write (dev, buffer, try * block_size);
202	if (got < 0)
203		got = 0;
204	if (got & 511)
205		fprintf (stderr,
206			 "Weird value (%ld) in do_write\n", got);
207	got /= block_size;
208	return got;
209}
210
211static int host_dev;
212
213static void flush_bufs(void)
214{
215	errcode_t	retval;
216
217	retval = ext2fs_sync_device(host_dev, 1);
218	if (retval)
219		com_err(program_name, retval, _("during ext2fs_sync_device"));
220}
221
222static unsigned int test_ro (int dev, unsigned long last_block,
223			     int block_size, unsigned long from_count,
224			     unsigned long blocks_at_once)
225{
226	char * blkbuf;
227	int try;
228	long got;
229	unsigned int bb_count = 0;
230	errcode_t errcode;
231
232	errcode = ext2fs_badblocks_list_iterate_begin(bb_list,&bb_iter);
233	if (errcode) {
234		com_err (program_name, errcode,
235			 _("while beginning bad block list iteration"));
236		exit (1);
237	}
238	do {
239		ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
240	} while (next_bad && next_bad < from_count);
241
242	blkbuf = malloc (blocks_at_once * block_size);
243	if (!blkbuf)
244	{
245		com_err (program_name, ENOMEM, _("while allocating buffers"));
246		exit (1);
247	}
248	flush_bufs();
249	if (v_flag) {
250	    fprintf(stderr, _("Checking for bad blocks in read-only mode\n"));
251	    fprintf (stderr, _("From block %lu to %lu\n"), from_count,
252		     last_block);
253	}
254	try = blocks_at_once;
255	currently_testing = from_count;
256	num_blocks = last_block;
257	if (s_flag || v_flag > 1) {
258		fprintf(stderr,
259			_("Checking for bad blocks (read-only test): "));
260		if (v_flag <= 1)
261			alarm_intr(SIGALRM);
262	}
263	while (currently_testing < last_block)
264	{
265		if (next_bad) {
266			if (currently_testing == next_bad) {
267				/* fprintf (out, "%lu\n", nextbad); */
268				ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
269				currently_testing++;
270				continue;
271			}
272			else if (currently_testing + try > next_bad)
273				try = next_bad - currently_testing;
274		}
275		if (currently_testing + try > last_block)
276			try = last_block - currently_testing;
277		got = do_read (dev, blkbuf, try, block_size, currently_testing);
278		currently_testing += got;
279		if (got == try) {
280			try = blocks_at_once;
281			continue;
282		}
283		else
284			try = 1;
285		if (got == 0) {
286			bb_count += bb_output(currently_testing++);
287		}
288	}
289	num_blocks = 0;
290	alarm(0);
291	if (s_flag || v_flag > 1)
292		fprintf(stderr, _(done_string));
293
294	fflush (stderr);
295	free (blkbuf);
296
297	ext2fs_badblocks_list_iterate_end(bb_iter);
298
299	return bb_count;
300}
301
302static unsigned int test_rw (int dev, unsigned long last_block,
303			     int block_size, unsigned long from_count,
304			     unsigned long blocks_at_once)
305{
306	int i;
307	char * buffer;
308	static unsigned char pattern[] = {0xaa, 0x55, 0xff, 0x00};
309	unsigned int bb_count = 0;
310
311	buffer = malloc (2 * block_size);
312	if (!buffer)
313	{
314		com_err (program_name, ENOMEM, _("while allocating buffers"));
315		exit (1);
316	}
317
318	flush_bufs();
319
320	if (v_flag) {
321		fprintf(stderr,
322			_("Checking for bad blocks in read-write mode\n"));
323		fprintf(stderr, _("From block %lu to %lu\n"),
324			 from_count, last_block);
325	}
326	for (i = 0; i < sizeof (pattern); i++) {
327		memset (buffer, pattern[i], block_size);
328		if (s_flag | v_flag)
329			fprintf (stderr, _("Writing pattern 0x%08x: "),
330				 *((int *) buffer));
331		num_blocks = last_block;
332		currently_testing = from_count;
333		if (s_flag && v_flag <= 1)
334			alarm_intr(SIGALRM);
335		for (;
336		     currently_testing < last_block;
337		     currently_testing++)
338		{
339			if (ext2fs_llseek (dev, (ext2_loff_t) currently_testing *
340					 block_size, SEEK_SET) !=
341			    (ext2_loff_t) currently_testing * block_size)
342				com_err (program_name, errno,
343					 _("during seek on block %d"),
344					 currently_testing);
345			if (v_flag > 1)
346				print_status();
347			write (dev, buffer, block_size);
348		}
349		num_blocks = 0;
350		alarm (0);
351		if (s_flag | v_flag)
352			fprintf(stderr, _(done_string));
353		flush_bufs();
354		if (s_flag | v_flag)
355			fprintf (stderr, _("Reading and comparing: "));
356		num_blocks = last_block;
357		currently_testing = from_count;
358		if (s_flag && v_flag <= 1)
359			alarm_intr(SIGALRM);
360		for (;
361		     currently_testing < last_block;
362		     currently_testing++)
363		{
364			if (ext2fs_llseek (dev, (ext2_loff_t) currently_testing *
365					 block_size, SEEK_SET) !=
366			    (ext2_loff_t) currently_testing * block_size)
367				com_err (program_name, errno,
368					 _("during seek on block %d"),
369					 currently_testing);
370			if (v_flag > 1)
371				print_status();
372			if ((read (dev, buffer + block_size, block_size)
373			     != block_size) ||
374			    memcmp(buffer, buffer + block_size, block_size))
375				bb_count += bb_output(currently_testing);
376		}
377		num_blocks = 0;
378		alarm (0);
379		if (s_flag | v_flag)
380			fprintf(stderr, _(done_string));
381		flush_bufs();
382	}
383
384	return bb_count;
385}
386
387struct saved_blk_record {
388	blk_t	block;
389	int	num;
390};
391
392static unsigned int test_nd (int dev, unsigned long last_block,
393			     int block_size, unsigned long from_count,
394			     unsigned long blocks_at_once)
395{
396	char *blkbuf, *save_ptr, *test_ptr, *read_ptr;
397	char * ptr;
398	int try, i;
399	long got, used2, written, save_currently_testing;
400	struct saved_blk_record *test_record;
401	/* This is static to prevent being clobbered by the longjmp */
402	static int num_saved;
403	jmp_buf terminate_env;
404	errcode_t errcode;
405	long buf_used;
406	unsigned int bb_count;
407
408	errcode = ext2fs_badblocks_list_iterate_begin(bb_list,&bb_iter);
409	if (errcode) {
410		com_err (program_name, errcode,
411			 _("while beginning bad block list iteration"));
412		exit (1);
413	}
414	do {
415		ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
416	} while (next_bad && next_bad < from_count);
417
418	blkbuf = malloc (3 * blocks_at_once * block_size);
419	test_record = malloc (blocks_at_once*sizeof(struct saved_blk_record));
420	if (!blkbuf || !test_record) {
421		com_err(program_name, ENOMEM, _("while allocating buffers"));
422		exit (1);
423	}
424	num_saved = 0;
425
426	/* inititalize the test data randomly: */
427	if (v_flag) {
428		fprintf (stderr, _("Initializing random test data\n"));
429	}
430	for(ptr = blkbuf + blocks_at_once * block_size;
431	    ptr < blkbuf + 2 * blocks_at_once * block_size;
432	    ++ptr) {
433		(*ptr) = random() % (1 << sizeof(char));
434	}
435
436	flush_bufs();
437	if (v_flag) {
438	    fprintf (stderr,
439		     _("Checking for bad blocks in non-destructive read-write mode\n"));
440	    fprintf (stderr, _("From block %lu to %lu\n"), from_count, last_block);
441	}
442	if (s_flag || v_flag > 1) {
443		fprintf(stderr, _("Checking for bad blocks (non-destructive read-write test): "));
444		if (v_flag <= 1)
445			alarm_intr(SIGALRM);
446	}
447	if (setjmp(terminate_env)) {
448		/*
449		 * Abnormal termination by a signal is handled here.
450		 */
451		signal (SIGALRM, SIG_IGN);
452		fprintf(stderr, _("\nInterrupt caught, cleaning up\n"));
453
454		save_ptr = blkbuf;
455		for (i=0; i < num_saved; i++) {
456			do_write(dev, save_ptr, test_record[i].num,
457				 block_size, test_record[i].block);
458			save_ptr += test_record[i].num * block_size;
459		}
460		fflush (out);
461		exit(1);
462	}
463
464	/* set up abend handler */
465	capture_terminate(terminate_env);
466
467	buf_used = 0;
468	bb_count = 0;
469	save_ptr = blkbuf;
470	test_ptr = blkbuf + (blocks_at_once * block_size);
471	currently_testing = from_count;
472	num_blocks = last_block;
473
474	while (currently_testing < last_block) {
475		try = blocks_at_once - buf_used;
476		if (next_bad) {
477			if (currently_testing == next_bad) {
478				/* fprintf (out, "%lu\n", nextbad); */
479				ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
480				currently_testing++;
481				goto check_for_more;
482			}
483			else if (currently_testing + try > next_bad)
484				try = next_bad - currently_testing;
485		}
486		if (currently_testing + try > last_block)
487			try = last_block - currently_testing;
488		got = do_read (dev, save_ptr, try, block_size,
489			       currently_testing);
490		if (got == 0) {
491			/* First block must have been bad. */
492			bb_count += bb_output(currently_testing++);
493			goto check_for_more;
494		}
495
496		/*
497		 * Note the fact that we've saved this much data
498		 * *before* we overwrite it with test data
499		 */
500		test_record[num_saved].block = currently_testing;
501		test_record[num_saved].num = got;
502		num_saved++;
503
504		/* Write the test data */
505		written = do_write (dev, test_ptr, got, block_size,
506				    currently_testing);
507		if (written != got)
508			com_err (program_name, errno,
509				 _("during test data write, block %lu"),
510				 currently_testing + written);
511
512		buf_used += got;
513		save_ptr += got * block_size;
514		test_ptr += got * block_size;
515		currently_testing += got;
516		if (got != try)
517			bb_count += bb_output(currently_testing++);
518
519	check_for_more:
520		/*
521		 * If there's room for more blocks to be tested this
522		 * around, and we're not done yet testing the disk, go
523		 * back and get some more blocks.
524		 */
525		if ((buf_used != blocks_at_once) &&
526		    (currently_testing < last_block))
527			continue;
528
529		flush_bufs();
530		save_currently_testing = currently_testing;
531
532		/*
533		 * for each contiguous block that we read into the
534		 * buffer (and wrote test data into afterwards), read
535		 * it back (looping if necessary, to get past newly
536		 * discovered unreadable blocks, of which there should
537		 * be none, but with a hard drive which is unreliable,
538		 * it has happened), and compare with the test data
539		 * that was written; output to the bad block list if
540		 * it doesn't match.
541		 */
542		used2 = 0;
543		save_ptr = blkbuf;
544		test_ptr = blkbuf + (blocks_at_once * block_size);
545		read_ptr = blkbuf + (2 * blocks_at_once * block_size);
546		try = 0;
547
548		while (1) {
549			if (try == 0) {
550				if (used2 >= num_saved)
551					break;
552				currently_testing = test_record[used2].block;
553				try = test_record[used2].num;
554				used2++;
555			}
556
557			got = do_read (dev, read_ptr, try,
558				       block_size, currently_testing);
559
560			/* test the comparison between all the
561			   blocks successfully read  */
562			for (i = 0; i < got; ++i)
563				if (memcmp (test_ptr+i*block_size,
564					    read_ptr+i*block_size, block_size))
565					bb_count += bb_output(currently_testing + i);
566			if (got < try) {
567				bb_count += bb_output(currently_testing + got);
568				got++;
569			}
570
571			/* when done, write back original data */
572			do_write (dev, save_ptr, got, block_size,
573				  currently_testing);
574
575			currently_testing += got;
576			save_ptr += got * block_size;
577			test_ptr += got * block_size;
578			read_ptr += got * block_size;
579			try -= got;
580		}
581
582		/* empty the buffer so it can be reused */
583		num_saved = 0;
584		buf_used = 0;
585		save_ptr = blkbuf;
586		test_ptr = blkbuf + (blocks_at_once * block_size);
587		currently_testing = save_currently_testing;
588	}
589	num_blocks = 0;
590	alarm(0);
591	uncapture_terminate();
592	if (s_flag || v_flag > 1)
593		fprintf(stderr, _(done_string));
594
595	fflush(stderr);
596	free(blkbuf);
597	free(test_record);
598
599	ext2fs_badblocks_list_iterate_end(bb_iter);
600
601	return bb_count;
602}
603
604static void check_mount(char *device_name)
605{
606	errcode_t	retval;
607	int		mount_flags;
608
609	retval = ext2fs_check_if_mounted(device_name, &mount_flags);
610	if (retval) {
611		com_err("ext2fs_check_if_mount", retval,
612			_("while determining whether %s is mounted."),
613			device_name);
614		return;
615	}
616	if (!(mount_flags & EXT2_MF_MOUNTED))
617		return;
618
619	fprintf(stderr, _("%s is mounted; "), device_name);
620	if (force) {
621		fprintf(stderr, _("badblocks forced anyway.  "
622			"Hope /etc/mtab is incorrect.\n"));
623		return;
624	}
625	fprintf(stderr, _("it's not safe to run badblocks!\n"));
626	exit(1);
627}
628
629
630int main (int argc, char ** argv)
631{
632	int c;
633	char * tmp;
634	char * device_name;
635	char * host_device_name = NULL;
636	char * input_file = NULL;
637	char * output_file = NULL;
638	FILE * in = NULL;
639	int block_size = 1024;
640	unsigned long blocks_at_once = 16;
641	blk_t last_block, from_count;
642	int num_passes = 0;
643	int passes_clean = 0;
644	int dev;
645	errcode_t errcode;
646	unsigned int (*test_func)(int, unsigned long,
647				  int, unsigned long,
648				  unsigned long);
649
650	setbuf(stdout, NULL);
651	setbuf(stderr, NULL);
652#ifdef ENABLE_NLS
653	setlocale(LC_MESSAGES, "");
654	bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
655	textdomain(NLS_CAT_NAME);
656#endif
657	test_func = test_ro;
658
659	if (argc && *argv)
660		program_name = *argv;
661	while ((c = getopt (argc, argv, "b:fi:o:svwnc:p:h:")) != EOF) {
662		switch (c) {
663		case 'b':
664			block_size = strtoul (optarg, &tmp, 0);
665			if (*tmp || block_size > 4096) {
666				com_err (program_name, 0,
667					 _("bad block size - %s"), optarg);
668				exit (1);
669			}
670			break;
671		case 'f':
672			force++;
673			break;
674		case 'i':
675			input_file = optarg;
676			break;
677		case 'o':
678			output_file = optarg;
679			break;
680		case 's':
681			s_flag = 1;
682			break;
683		case 'v':
684			v_flag++;
685			break;
686		case 'w':
687			if (w_flag)
688				usage();
689			test_func = test_rw;
690			w_flag = 1;
691			break;
692		case 'n':
693			if (w_flag)
694				usage();
695			test_func = test_nd;
696			w_flag = 2;
697			break;
698		case 'c':
699			blocks_at_once = strtoul (optarg, &tmp, 0);
700			if (*tmp) {
701				com_err (program_name, 0,
702					 "bad simultaneous block count - %s", optarg);
703				exit (1);
704			}
705			break;
706		case 'p':
707			num_passes = strtoul (optarg, &tmp, 0);
708			if (*tmp) {
709				com_err (program_name, 0,
710				    "bad number of clean passes - %s", optarg);
711				exit (1);
712			}
713			break;
714		case 'h':
715			host_device_name = optarg;
716			break;
717		default:
718			usage();
719		}
720	}
721	if (optind > argc - 1)
722		usage();
723	device_name = argv[optind++];
724	if (optind > argc - 1) {
725		errcode = ext2fs_get_device_size(device_name,
726						 block_size,
727						 &last_block);
728		if (errcode == EXT2_ET_UNIMPLEMENTED) {
729			com_err(program_name, 0,
730				_("Couldn't determine device size; you "
731				  "must specify\nthe size manually\n"));
732			exit(1);
733		}
734		if (errcode) {
735			com_err(program_name, errcode,
736				_("while trying to determine device size"));
737			exit(1);
738		}
739	} else {
740		last_block = strtoul (argv[optind], &tmp, 0);
741		if (*tmp) {
742			com_err (program_name, 0, _("bad blocks count - %s"),
743				 argv[optind]);
744			exit (1);
745		}
746		optind++;
747	}
748	if (optind <= argc-1) {
749		from_count = strtoul (argv[optind], &tmp, 0);
750		if (*tmp) {
751			com_err (program_name, 0, _("bad starting block - %s"),
752				 argv[optind]);
753			exit (1);
754		}
755	} else from_count = 0;
756	if (from_count >= last_block) {
757	    com_err (program_name, 0, _("bad blocks range: %lu-%lu"),
758		     from_count, last_block);
759	    exit (1);
760	}
761	if (w_flag)
762		check_mount(device_name);
763
764	dev = open (device_name, w_flag ? O_RDWR : O_RDONLY);
765	if (dev == -1)
766	{
767		com_err (program_name, errno, _("while trying to open %s"),
768			 device_name);
769		exit (1);
770	}
771	if (host_device_name) {
772		host_dev = open (host_device_name, O_RDONLY);
773		if (host_dev == -1)
774		{
775			com_err (program_name, errno,
776				 _("while trying to open %s"),
777				 host_device_name);
778			exit (1);
779		}
780	} else
781		host_dev = dev;
782	if (input_file)
783		if (strcmp (input_file, "-") == 0)
784			in = stdin;
785		else {
786			in = fopen (input_file, "r");
787			if (in == NULL)
788			{
789				com_err (program_name, errno,
790					 _("while trying to open %s"),
791					 input_file);
792				exit (1);
793			}
794		}
795	if (output_file && strcmp (output_file, "-") != 0)
796	{
797		out = fopen (output_file, "w");
798		if (out == NULL)
799		{
800			com_err (program_name, errno,
801				 _("while trying to open %s"),
802				 output_file);
803			exit (1);
804		}
805	}
806	else
807		out = stdout;
808
809	errcode = ext2fs_badblocks_list_create(&bb_list,0);
810	if (errcode) {
811		com_err (program_name, errcode,
812			 _("creating in-memory bad blocks list"));
813		exit (1);
814	}
815
816	if (in) {
817		for(;;) {
818			switch(fscanf (in, "%u\n", &next_bad)) {
819				case 0:
820					com_err (program_name, 0, "input file - bad format");
821					exit (1);
822				case EOF:
823					break;
824				default:
825					errcode = ext2fs_badblocks_list_add(bb_list,next_bad);
826					if (errcode) {
827						com_err (program_name, errcode, _("adding to in-memory bad block list"));
828						exit (1);
829					}
830					continue;
831			}
832			break;
833		}
834
835		if (in != stdin)
836			fclose (in);
837	}
838
839	do {
840		unsigned int bb_count;
841
842		bb_count = test_func(dev, last_block, block_size,
843				     from_count, blocks_at_once);
844		if (bb_count)
845			passes_clean = 0;
846		else
847			++passes_clean;
848
849		if (v_flag)
850			fprintf(stderr,
851				_("Pass completed, %u bad blocks found.\n"),
852				bb_count);
853
854	} while (passes_clean < num_passes);
855
856	close (dev);
857	if (out != stdout)
858		fclose (out);
859	return 0;
860}
861
862