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