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