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 #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
56 const char * program_name = "badblocks";
57 const char * done_string = N_("done                        \n");
58
59 static int v_flag = 0;                  /* verbose */
60 static int w_flag = 0;                  /* do r/w test: 0=no, 1=yes,
61                                          * 2=non-destructive */
62 static int s_flag = 0;                  /* show progress of test */
63 static int force = 0;                   /* force check of mounted device */
64
65 static 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
72 static unsigned long currently_testing = 0;
73 static unsigned long num_blocks = 0;
74 static ext2_badblocks_list bb_list = NULL;
75 static FILE *out;
76 static blk_t next_bad = 0;
77 static 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  */
83 static 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
107 static 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
114 static 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
125 static void *terminate_addr = NULL;
126
127 static void terminate_intr(int signo)
128 {
129         if (terminate_addr)
130                 longjmp(terminate_addr,1);
131         exit(1);
132 }
133
134 static 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
145 static 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  */
160 static 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  */
187 static 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
211 static int host_dev;
212
213 static 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
222 static 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
302 static 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
387 struct saved_blk_record {
388         blk_t   block;
389         int     num;
390 };
391
392 static 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
604 static 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
630 int 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