Whamcloud - gitweb
Corrects a few mistakes in the fsck man page, wraps two overlong lines,
[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 #define _GNU_SOURCE /* for O_DIRECT */
33
34 #include <errno.h>
35 #include <fcntl.h>
36 #ifdef HAVE_GETOPT_H
37 #include <getopt.h>
38 #else
39 extern char *optarg;
40 extern int optind;
41 #endif
42 #include <signal.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
47 #include <setjmp.h>
48 #include <time.h>
49
50 #include <sys/ioctl.h>
51 #include <sys/types.h>
52
53 #include "et/com_err.h"
54 #include "ext2fs/ext2_io.h"
55 #include "ext2fs/ext2_fs.h"
56 #include "ext2fs/ext2fs.h"
57 #include "nls-enable.h"
58
59 const char * program_name = "badblocks";
60 const char * done_string = N_("done                        \n");
61
62 static int v_flag = 0;                  /* verbose */
63 static int w_flag = 0;                  /* do r/w test: 0=no, 1=yes,
64                                          * 2=non-destructive */
65 static int s_flag = 0;                  /* show progress of test */
66 static int force = 0;                   /* force check of mounted device */
67 static int t_flag = 0;                  /* number of test patterns */
68 static int t_max = 0;                   /* allocated test patterns */
69 static unsigned long *t_patts = NULL;   /* test patterns */
70 static int current_O_DIRECT = 0;        /* Current status of O_DIRECT flag */
71
72 #define T_INC 32
73
74 int sys_page_size = 4096;
75
76 static void usage(void)
77 {
78         fprintf(stderr, _("Usage: %s [-b block_size] [-i input_file] [-o output_file] [-svwnf]\n [-c blocks_at_once] [-p num_passes] [-t test_pattern [-t test_pattern [...]]]\n device [last_block [start_block]]\n"),
79                  program_name);
80         exit (1);
81 }
82
83 static unsigned long currently_testing = 0;
84 static unsigned long num_blocks = 0;
85 static ext2_badblocks_list bb_list = NULL;
86 static FILE *out;
87 static blk_t next_bad = 0;
88 static ext2_badblocks_iterate bb_iter = NULL;
89
90 static void *allocate_buffer(size_t size)
91 {
92         void    *ret = 0;
93         
94 #ifdef HAVE_POSIX_MEMALIGN
95         if (posix_memalign(&ret, sys_page_size, size) < 0)
96                 ret = 0;
97 #else
98 #ifdef HAVE_MEMALIGN
99         ret = memalign(sys_page_size, size);
100 #else
101 #ifdef HAVE_VALLOC
102         ret = valloc(size);
103 #endif /* HAVE_VALLOC */
104 #endif /* HAVE_MEMALIGN */      
105 #endif /* HAVE_POSIX_MEMALIGN */
106
107         if (!ret)
108                 ret = malloc(size);
109
110         return ret;
111 }
112
113 /*
114  * This routine reports a new bad block.  If the bad block has already
115  * been seen before, then it returns 0; otherwise it returns 1.
116  */
117 static int bb_output (unsigned long bad)
118 {
119         errcode_t errcode;
120
121         if (ext2fs_badblocks_list_test(bb_list, bad))
122                 return 0;
123
124         fprintf(out, "%lu\n", bad);
125         fflush(out);
126
127         errcode = ext2fs_badblocks_list_add (bb_list, bad);
128         if (errcode) {
129                 com_err (program_name, errcode, "adding to in-memory bad block list");
130                 exit (1);
131         }
132
133         /* kludge:
134            increment the iteration through the bb_list if 
135            an element was just added before the current iteration
136            position.  This should not cause next_bad to change. */
137         if (bb_iter && bad < next_bad)
138                 ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
139         return 1;
140 }
141
142 static void print_status(void)
143 {
144         fprintf(stderr, "%15ld/%15ld", currently_testing, num_blocks);
145         fputs("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", stderr);
146         fflush (stderr);
147 }
148
149 static void alarm_intr(int alnum EXT2FS_ATTR((unused)))
150 {
151         signal (SIGALRM, alarm_intr);
152         alarm(1);
153         if (!num_blocks)
154                 return;
155         print_status();
156 }
157
158 static void *terminate_addr = NULL;
159
160 static void terminate_intr(int signo EXT2FS_ATTR((unused)))
161 {
162         if (terminate_addr)
163                 longjmp(terminate_addr,1);
164         exit(1);
165 }
166
167 static void capture_terminate(jmp_buf term_addr)
168 {
169         terminate_addr = term_addr;
170         signal (SIGHUP, terminate_intr);
171         signal (SIGINT, terminate_intr);
172         signal (SIGPIPE, terminate_intr);
173         signal (SIGTERM, terminate_intr);
174         signal (SIGUSR1, terminate_intr);
175         signal (SIGUSR2, terminate_intr);
176 }
177
178 static void uncapture_terminate(void)
179 {
180         terminate_addr = NULL;
181         signal (SIGHUP, SIG_DFL);
182         signal (SIGINT, SIG_DFL);
183         signal (SIGPIPE, SIG_DFL);
184         signal (SIGTERM, SIG_DFL);
185         signal (SIGUSR1, SIG_DFL);
186         signal (SIGUSR2, SIG_DFL);
187 }
188
189 static void set_o_direct(int dev, unsigned char *buffer, size_t size,
190                          unsigned long current_block)
191 {
192 #ifdef O_DIRECT
193         int new_flag = O_DIRECT;
194         int flag;
195         
196         if ((((unsigned long) buffer & (sys_page_size - 1)) != 0) ||
197             ((size & (sys_page_size - 1)) != 0) ||
198             ((current_block & ((sys_page_size >> 9)-1)) != 0))
199                 new_flag = 0;
200
201         if (new_flag != current_O_DIRECT) {
202              /* printf("%s O_DIRECT\n", new_flag ? "Setting" : "Clearing"); */
203                 flag = fcntl(dev, F_GETFL);
204                 if (flag > 0) {
205                         flag = (flag & ~O_DIRECT) | new_flag;
206                         fcntl(dev, F_SETFL, flag);
207                 }
208                 current_O_DIRECT = new_flag;
209         }
210 #endif
211 }
212
213
214 static void pattern_fill(unsigned char *buffer, unsigned long pattern,
215                          size_t n)
216 {
217         unsigned int    i, nb;
218         unsigned char   bpattern[sizeof(pattern)], *ptr;
219         
220         if (pattern == (unsigned long) ~0) {
221                 for (ptr = buffer; ptr < buffer + n; ptr++) {
222                         (*ptr) = random() % (1 << (8 * sizeof(char)));
223                 }
224                 if (s_flag | v_flag)
225                         fputs(_("Testing with random pattern: "), stderr);
226         } else {
227                 bpattern[0] = 0;
228                 for (i = 0; i < sizeof(bpattern); i++) {
229                         if (pattern == 0)
230                                 break;
231                         bpattern[i] = pattern & 0xFF;
232                         pattern = pattern >> 8;
233                 }
234                 nb = i ? (i-1) : 0;
235                 for (ptr = buffer, i = nb; ptr < buffer + n; ptr++) {
236                         *ptr = bpattern[i];
237                         if (i == 0)
238                                 i = nb;
239                         else
240                                 i--;
241                 }
242                 if (s_flag | v_flag) {
243                         fputs(_("Testing with pattern 0x"), stderr);
244                         for (i = 0; i <= nb; i++)
245                                 fprintf(stderr, "%02x", buffer[i]);
246                         fputs(": ", stderr);
247                 }
248         }
249 }
250
251 /*
252  * Perform a read of a sequence of blocks; return the number of blocks
253  *    successfully sequentially read.
254  */
255 static long do_read (int dev, unsigned char * buffer, int try, int block_size,
256                      unsigned long current_block)
257 {
258         long got;
259
260         set_o_direct(dev, buffer, try * block_size, current_block);
261
262         if (v_flag > 1)
263                 print_status();
264
265         /* Seek to the correct loc. */
266         if (ext2fs_llseek (dev, (ext2_loff_t) current_block * block_size,
267                          SEEK_SET) != (ext2_loff_t) current_block * block_size)
268                 com_err (program_name, errno, _("during seek"));
269
270         /* Try the read */
271         got = read (dev, buffer, try * block_size);
272         if (got < 0)
273                 got = 0;        
274         if (got & 511)
275                 fprintf(stderr, _("Weird value (%ld) in do_read\n"), got);
276         got /= block_size;
277         return got;
278 }
279
280 /*
281  * Perform a write of a sequence of blocks; return the number of blocks
282  *    successfully sequentially written.
283  */
284 static long do_write (int dev, unsigned char * buffer, int try, int block_size,
285                      unsigned long current_block)
286 {
287         long got;
288
289         set_o_direct(dev, buffer, try * block_size, current_block);
290
291         if (v_flag > 1)
292                 print_status();
293
294         /* Seek to the correct loc. */
295         if (ext2fs_llseek (dev, (ext2_loff_t) current_block * block_size,
296                          SEEK_SET) != (ext2_loff_t) current_block * block_size)
297                 com_err (program_name, errno, _("during seek"));
298
299         /* Try the write */
300         got = write (dev, buffer, try * block_size);
301         if (got < 0)
302                 got = 0;        
303         if (got & 511)
304                 fprintf(stderr, "Weird value (%ld) in do_write\n", got);
305         got /= block_size;
306         return got;
307 }
308
309 static int host_dev;
310
311 static void flush_bufs(void)
312 {
313         errcode_t       retval;
314
315         retval = ext2fs_sync_device(host_dev, 1);
316         if (retval)
317                 com_err(program_name, retval, _("during ext2fs_sync_device"));
318 }
319
320 static unsigned int test_ro (int dev, unsigned long last_block,
321                              int block_size, unsigned long from_count,
322                              unsigned long blocks_at_once)
323 {
324         unsigned char * blkbuf;
325         int try;
326         long got;
327         unsigned int bb_count = 0;
328         errcode_t errcode;
329
330         errcode = ext2fs_badblocks_list_iterate_begin(bb_list,&bb_iter);
331         if (errcode) {
332                 com_err (program_name, errcode,
333                          _("while beginning bad block list iteration"));
334                 exit (1);
335         }
336         do {
337                 ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
338         } while (next_bad && next_bad < from_count);
339
340         if (t_flag) {
341                 blkbuf = allocate_buffer((blocks_at_once + 1) * block_size);
342         } else {
343                 blkbuf = allocate_buffer(blocks_at_once * block_size);
344         }
345         if (!blkbuf)
346         {
347                 com_err (program_name, ENOMEM, _("while allocating buffers"));
348                 exit (1);
349         }
350         if (v_flag) {
351             fprintf (stderr, _("Checking blocks %lu to %lu\n"), from_count,
352                      last_block);
353         }
354         if (t_flag) {
355                 fputs(_("Checking for bad blocks in read-only mode\n"), stderr);
356                 pattern_fill(blkbuf + blocks_at_once * block_size,
357                              t_patts[0], block_size);
358         }
359         flush_bufs();
360         try = blocks_at_once;
361         currently_testing = from_count;
362         num_blocks = last_block;
363         if (!t_flag && (s_flag || v_flag)) {
364                 fputs(_("Checking for bad blocks (read-only test): "), stderr);
365                 if (v_flag <= 1)
366                         alarm_intr(SIGALRM);
367         }
368         while (currently_testing < last_block)
369         {
370                 if (next_bad) {
371                         if (currently_testing == next_bad) {
372                                 /* fprintf (out, "%lu\n", nextbad); */
373                                 ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
374                                 currently_testing++;
375                                 continue;
376                         }
377                         else if (currently_testing + try > next_bad)
378                                 try = next_bad - currently_testing;
379                 }
380                 if (currently_testing + try > last_block)
381                         try = last_block - currently_testing;
382                 got = do_read (dev, blkbuf, try, block_size, currently_testing);
383                 if (t_flag) {
384                         /* test the comparison between all the
385                            blocks successfully read  */
386                         int i;
387                         for (i = 0; i < got; ++i)
388                                 if (memcmp (blkbuf+i*block_size,
389                                             blkbuf+blocks_at_once*block_size,
390                                             block_size))
391                                         bb_count += bb_output(currently_testing + i);
392                 }
393                 currently_testing += got;
394                 if (got == try) {
395                         try = blocks_at_once;
396                         /* recover page-aligned offset for O_DIRECT */
397                         if ( blocks_at_once >= (unsigned long) (sys_page_size >> 9)
398                              && (currently_testing % (sys_page_size >> 9)!= 0))
399                                 try -= (sys_page_size >> 9)
400                                         - (currently_testing 
401                                            % (sys_page_size >> 9));
402                         continue;
403                 }
404                 else
405                         try = 1;
406                 if (got == 0) {
407                         bb_count += bb_output(currently_testing++);
408                 }
409         }
410         num_blocks = 0;
411         alarm(0);
412         if (s_flag || v_flag)
413                 fputs(_(done_string), stderr);
414
415         fflush (stderr);
416         free (blkbuf);
417
418         ext2fs_badblocks_list_iterate_end(bb_iter);
419
420         return bb_count;
421 }
422
423 static unsigned int test_rw (int dev, unsigned long last_block,
424                              int block_size, unsigned long from_count,
425                              unsigned long blocks_at_once)
426 {
427         unsigned char *buffer, *read_buffer;
428         const unsigned long patterns[] = {0xaa, 0x55, 0xff, 0x00};
429         const unsigned long *pattern;
430         int i, try, got, nr_pattern, pat_idx;
431         unsigned int bb_count = 0;
432
433         buffer = allocate_buffer(2 * blocks_at_once * block_size);
434         read_buffer = buffer + blocks_at_once * block_size;
435         
436         if (!buffer) {
437                 com_err (program_name, ENOMEM, _("while allocating buffers"));
438                 exit (1);
439         }
440
441         flush_bufs();
442
443         if (v_flag) {
444                 fputs(_("Checking for bad blocks in read-write mode\n"), 
445                       stderr);
446                 fprintf(stderr, _("From block %lu to %lu\n"),
447                          from_count, last_block);
448         }
449         if (t_flag) {
450                 pattern = t_patts;
451                 nr_pattern = t_flag;
452         } else {
453                 pattern = patterns;
454                 nr_pattern = sizeof(patterns) / sizeof(patterns[0]);
455         }
456         for (pat_idx = 0; pat_idx < nr_pattern; pat_idx++) {
457                 pattern_fill(buffer, pattern[pat_idx],
458                              blocks_at_once * block_size);
459                 num_blocks = last_block;
460                 currently_testing = from_count;
461                 if (s_flag && v_flag <= 1)
462                         alarm_intr(SIGALRM);
463
464                 try = blocks_at_once;
465                 while (currently_testing < last_block) {
466                         if (currently_testing + try > last_block)
467                                 try = last_block - currently_testing;
468                         got = do_write(dev, buffer, try, block_size,
469                                         currently_testing);
470                         if (v_flag > 1)
471                                 print_status();
472
473                         currently_testing += got;
474                         if (got == try) {
475                                 try = blocks_at_once;
476                                 /* recover page-aligned offset for O_DIRECT */
477                                 if ( blocks_at_once >= (unsigned long) (sys_page_size >> 9)
478                                      && (currently_testing % 
479                                          (sys_page_size >> 9)!= 0))
480                                         try -= (sys_page_size >> 9)
481                                                 - (currently_testing 
482                                                    % (sys_page_size >> 9));
483                                 continue;
484                         } else
485                                 try = 1;
486                         if (got == 0) {
487                                 bb_count += bb_output(currently_testing++);
488                         }
489                 }
490                 
491                 num_blocks = 0;
492                 alarm (0);
493                 if (s_flag | v_flag)
494                         fputs(_(done_string), stderr);
495                 flush_bufs();
496                 if (s_flag | v_flag)
497                         fputs(_("Reading and comparing: "), stderr);
498                 num_blocks = last_block;
499                 currently_testing = from_count;
500                 if (s_flag && v_flag <= 1)
501                         alarm_intr(SIGALRM);
502
503                 try = blocks_at_once;
504                 while (currently_testing < last_block) {
505                         if (currently_testing + try > last_block)
506                                 try = last_block - currently_testing;
507                         got = do_read (dev, read_buffer, try, block_size,
508                                        currently_testing);
509                         if (got == 0) {
510                                 bb_count += bb_output(currently_testing++);
511                                 continue;
512                         }
513                         for (i=0; i < got; i++) {
514                                 if (memcmp(read_buffer + i * block_size,
515                                            buffer + i * block_size,
516                                            block_size))
517                                         bb_count += bb_output(currently_testing+i);
518                         }
519                         currently_testing += got;
520                         /* recover page-aligned offset for O_DIRECT */
521                         if ( blocks_at_once >= (unsigned long) (sys_page_size >> 9)
522                              && (currently_testing % (sys_page_size >> 9)!= 0))
523                                 try = blocks_at_once - (sys_page_size >> 9)
524                                         - (currently_testing 
525                                            % (sys_page_size >> 9));
526                         else
527                                 try = blocks_at_once;
528                         if (v_flag > 1)
529                                 print_status();
530                 }
531                 
532                 num_blocks = 0;
533                 alarm (0);
534                 if (s_flag | v_flag)
535                         fputs(_(done_string), stderr);
536                 flush_bufs();
537         }
538         uncapture_terminate();
539         free(buffer);
540         return bb_count;
541 }
542
543 struct saved_blk_record {
544         blk_t   block;
545         int     num;
546 };
547
548 static unsigned int test_nd (int dev, unsigned long last_block,
549                              int block_size, unsigned long from_count,
550                              unsigned long blocks_at_once)
551 {
552         unsigned char *blkbuf, *save_ptr, *test_ptr, *read_ptr;
553         unsigned char *test_base, *save_base, *read_base;
554         int try, i;
555         const unsigned long patterns[] = { ~0 };
556         const unsigned long *pattern;
557         int nr_pattern, pat_idx;
558         long got, used2, written, save_currently_testing;
559         struct saved_blk_record *test_record;
560         /* This is static to prevent being clobbered by the longjmp */
561         static int num_saved;
562         jmp_buf terminate_env;
563         errcode_t errcode;
564         unsigned long buf_used;
565         static unsigned int bb_count;
566
567         bb_count = 0;
568         errcode = ext2fs_badblocks_list_iterate_begin(bb_list,&bb_iter);
569         if (errcode) {
570                 com_err (program_name, errcode,
571                          _("while beginning bad block list iteration"));
572                 exit (1);
573         }
574         do {
575                 ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
576         } while (next_bad && next_bad < from_count);
577
578         blkbuf = allocate_buffer(3 * blocks_at_once * block_size);
579         test_record = malloc (blocks_at_once*sizeof(struct saved_blk_record));
580         if (!blkbuf || !test_record) {
581                 com_err(program_name, ENOMEM, _("while allocating buffers"));
582                 exit (1);
583         }
584
585         save_base = blkbuf;
586         test_base = blkbuf + (blocks_at_once * block_size);
587         read_base = blkbuf + (2 * blocks_at_once * block_size);
588         
589         num_saved = 0;
590
591         flush_bufs();
592         if (v_flag) {
593             fputs(_("Checking for bad blocks in non-destructive read-write mode\n"), stderr);
594             fprintf (stderr, _("From block %lu to %lu\n"), from_count, last_block);
595         }
596         if (s_flag || v_flag > 1) {
597                 fputs(_("Checking for bad blocks (non-destructive read-write test)\n"), stderr);
598         }
599         if (setjmp(terminate_env)) {
600                 /*
601                  * Abnormal termination by a signal is handled here.
602                  */
603                 signal (SIGALRM, SIG_IGN);
604                 fputs(_("\nInterrupt caught, cleaning up\n"), stderr);
605
606                 save_ptr = save_base;
607                 for (i=0; i < num_saved; i++) {
608                         do_write(dev, save_ptr, test_record[i].num,
609                                  block_size, test_record[i].block);
610                         save_ptr += test_record[i].num * block_size;
611                 }
612                 fflush (out);
613                 exit(1);
614         }
615         
616         /* set up abend handler */
617         capture_terminate(terminate_env);
618
619         if (t_flag) {
620                 pattern = t_patts;
621                 nr_pattern = t_flag;
622         } else {
623                 pattern = patterns;
624                 nr_pattern = sizeof(patterns) / sizeof(patterns[0]);
625         }
626         for (pat_idx = 0; pat_idx < nr_pattern; pat_idx++) {
627                 pattern_fill(test_base, pattern[pat_idx],
628                              blocks_at_once * block_size);
629
630                 buf_used = 0;
631                 bb_count = 0;
632                 save_ptr = save_base;
633                 test_ptr = test_base;
634                 currently_testing = from_count;
635                 num_blocks = last_block;
636                 if (s_flag && v_flag <= 1)
637                         alarm_intr(SIGALRM);
638
639                 while (currently_testing < last_block) {
640                         got = try = blocks_at_once - buf_used;
641                         if (next_bad) {
642                                 if (currently_testing == next_bad) {
643                                         /* fprintf (out, "%lu\n", nextbad); */
644                                         ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
645                                         currently_testing++;
646                                         goto check_for_more;
647                                 }
648                                 else if (currently_testing + try > next_bad)
649                                         try = next_bad - currently_testing;
650                         }
651                         if (currently_testing + try > last_block)
652                                 try = last_block - currently_testing;
653                         got = do_read (dev, save_ptr, try, block_size,
654                                        currently_testing);
655                         if (got == 0) {
656                                 /* First block must have been bad. */
657                                 bb_count += bb_output(currently_testing++);
658                                 goto check_for_more;
659                         }
660
661                         /*
662                          * Note the fact that we've saved this much data
663                          * *before* we overwrite it with test data
664                          */
665                         test_record[num_saved].block = currently_testing;
666                         test_record[num_saved].num = got;
667                         num_saved++;
668
669                         /* Write the test data */
670                         written = do_write (dev, test_ptr, got, block_size,
671                                             currently_testing);
672                         if (written != got)
673                                 com_err (program_name, errno,
674                                          _("during test data write, block %lu"),
675                                          currently_testing + written);
676
677                         buf_used += got;
678                         save_ptr += got * block_size;
679                         test_ptr += got * block_size;
680                         currently_testing += got;
681                         if (got != try)
682                                 bb_count += bb_output(currently_testing++);
683
684                 check_for_more:
685                         /*
686                          * If there's room for more blocks to be tested this
687                          * around, and we're not done yet testing the disk, go
688                          * back and get some more blocks.
689                          */
690                         if ((buf_used != blocks_at_once) &&
691                             (currently_testing < last_block))
692                                 continue;
693
694                         flush_bufs();
695                         save_currently_testing = currently_testing;
696
697                         /*
698                          * for each contiguous block that we read into the
699                          * buffer (and wrote test data into afterwards), read
700                          * it back (looping if necessary, to get past newly
701                          * discovered unreadable blocks, of which there should
702                          * be none, but with a hard drive which is unreliable,
703                          * it has happened), and compare with the test data
704                          * that was written; output to the bad block list if
705                          * it doesn't match.
706                          */
707                         used2 = 0;
708                         save_ptr = save_base;
709                         test_ptr = test_base;
710                         read_ptr = read_base;
711                         try = 0;
712
713                         while (1) {
714                                 if (try == 0) {
715                                         if (used2 >= num_saved)
716                                                 break;
717                                         currently_testing = test_record[used2].block;
718                                         try = test_record[used2].num;
719                                         used2++;
720                                 }
721                                 
722                                 got = do_read (dev, read_ptr, try,
723                                                block_size, currently_testing);
724
725                                 /* test the comparison between all the
726                                    blocks successfully read  */
727                                 for (i = 0; i < got; ++i)
728                                         if (memcmp (test_ptr+i*block_size,
729                                                     read_ptr+i*block_size, block_size))
730                                                 bb_count += bb_output(currently_testing + i);
731                                 if (got < try) {
732                                         bb_count += bb_output(currently_testing + got);
733                                         got++;
734                                 }
735                                         
736                                 /* write back original data */
737                                 do_write (dev, save_ptr, got,
738                                           block_size, currently_testing);
739                                 save_ptr += got * block_size;
740
741                                 currently_testing += got;
742                                 test_ptr += got * block_size;
743                                 read_ptr += got * block_size;
744                                 try -= got;
745                         }
746
747                         /* empty the buffer so it can be reused */
748                         num_saved = 0;
749                         buf_used = 0;
750                         save_ptr = save_base;
751                         test_ptr = test_base;
752                         currently_testing = save_currently_testing;
753                 }
754                 num_blocks = 0;
755                 alarm(0);
756                 if (s_flag || v_flag > 1)
757                         fputs(_(done_string), stderr);
758
759                 flush_bufs();
760         }
761         uncapture_terminate();
762         fflush(stderr);
763         free(blkbuf);
764         free(test_record);
765
766         ext2fs_badblocks_list_iterate_end(bb_iter);
767
768         return bb_count;
769 }
770
771 static void check_mount(char *device_name)
772 {
773         errcode_t       retval;
774         int             mount_flags;
775
776         retval = ext2fs_check_if_mounted(device_name, &mount_flags);
777         if (retval) {
778                 com_err("ext2fs_check_if_mount", retval,
779                         _("while determining whether %s is mounted."),
780                         device_name);
781                 return;
782         }
783         if (!(mount_flags & EXT2_MF_MOUNTED))
784                 return;
785
786         fprintf(stderr, _("%s is mounted; "), device_name);
787         if (force) {
788                 fputs(_("badblocks forced anyway.  "
789                         "Hope /etc/mtab is incorrect.\n"), stderr);
790                 return;
791         }
792         fputs(_("it's not safe to run badblocks!\n"), stderr);
793         exit(1);
794 }
795
796
797 int main (int argc, char ** argv)
798 {
799         int c;
800         char * tmp;
801         char * device_name;
802         char * host_device_name = NULL;
803         char * input_file = NULL;
804         char * output_file = NULL;
805         FILE * in = NULL;
806         int block_size = 1024;
807         unsigned long blocks_at_once = 64;
808         blk_t last_block, from_count;
809         int num_passes = 0;
810         int passes_clean = 0;
811         int dev;
812         errcode_t errcode;
813         unsigned long pattern;
814         unsigned int (*test_func)(int, unsigned long,
815                                   int, unsigned long,
816                                   unsigned long);
817         int open_flag = 0;
818         long sysval;
819
820         setbuf(stdout, NULL);
821         setbuf(stderr, NULL);
822 #ifdef ENABLE_NLS
823         setlocale(LC_MESSAGES, "");
824         setlocale(LC_CTYPE, "");
825         bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
826         textdomain(NLS_CAT_NAME);
827 #endif
828         srandom((unsigned int)time(NULL));  /* simple randomness is enough */
829         test_func = test_ro;
830
831         /* Determine the system page size if possible */
832 #ifdef HAVE_SYSCONF
833 #if (!defined(_SC_PAGESIZE) && defined(_SC_PAGE_SIZE))
834 #define _SC_PAGESIZE _SC_PAGE_SIZE
835 #endif
836 #ifdef _SC_PAGESIZE
837         sysval = sysconf(_SC_PAGESIZE);
838         if (sysval > 0)
839                 sys_page_size = sysval;
840 #endif /* _SC_PAGESIZE */
841 #endif /* HAVE_SYSCONF */
842         
843         if (argc && *argv)
844                 program_name = *argv;
845         while ((c = getopt (argc, argv, "b:fi:o:svwnc:p:h:t:")) != EOF) {
846                 switch (c) {
847                 case 'b':
848                         block_size = strtoul (optarg, &tmp, 0);
849                         if (*tmp || block_size > 4096) {
850                                 com_err (program_name, 0,
851                                          _("bad block size - %s"), optarg);
852                                 exit (1);
853                         }
854                         break;
855                 case 'f':
856                         force++;
857                         break;
858                 case 'i':
859                         input_file = optarg;
860                         break;
861                 case 'o':
862                         output_file = optarg;
863                         break;
864                 case 's':
865                         s_flag = 1;
866                         break;
867                 case 'v':
868                         v_flag++;
869                         break;
870                 case 'w':
871                         if (w_flag)
872                                 usage();
873                         test_func = test_rw;
874                         w_flag = 1;
875                         break;
876                 case 'n':
877                         if (w_flag)
878                                 usage();
879                         test_func = test_nd;
880                         w_flag = 2;
881                         break;
882                 case 'c':
883                         blocks_at_once = strtoul (optarg, &tmp, 0);
884                         if (*tmp) {
885                                 com_err (program_name, 0,
886                                          "bad simultaneous block count - %s", optarg);
887                                 exit (1);
888                         }
889                         break;
890                 case 'p':
891                         num_passes = strtoul (optarg, &tmp, 0);
892                         if (*tmp) {
893                                 com_err (program_name, 0,
894                                     "bad number of clean passes - %s", optarg);
895                                 exit (1);
896                         }
897                         break;
898                 case 'h':
899                         host_device_name = optarg;
900                         break;
901                 case 't':
902                         if (t_flag + 1 > t_max) {
903                                 unsigned long *t_patts_new;
904
905                                 t_patts_new = realloc(t_patts, t_max + T_INC);
906                                 if (!t_patts_new) {
907                                         com_err(program_name, ENOMEM,
908                                                 _("can't allocate memory for "
909                                                   "test_pattern - %s"),
910                                                 optarg);
911                                         exit(1);
912                                 }
913                                 t_patts = t_patts_new;
914                                 t_max += T_INC;
915                         }
916                         if (!strcmp(optarg, "r") || !strcmp(optarg,"random")) {
917                                 t_patts[t_flag++] = ~0;
918                         } else {
919                                 pattern = strtoul(optarg, &tmp, 0);
920                                 if (*tmp) {
921                                         com_err(program_name, 0,
922                                         _("invalid test_pattern: %s\n"),
923                                                 optarg);
924                                         exit(1);
925                                 }
926                                 if (pattern == (unsigned long) ~0)
927                                         pattern = 0xffff;
928                                 t_patts[t_flag++] = pattern;
929                         }
930                         break;
931                 default:
932                         usage();
933                 }
934         }
935         if (!w_flag) {
936                 if (t_flag > 1) {
937                         com_err(program_name, 0,
938                         _("Maximum of one test_pattern may be specified "
939                           "in read-only mode"));
940                         exit(1);
941                 }
942                 if (t_patts && (t_patts[0] == (unsigned long) ~0)) {
943                         com_err(program_name, 0,
944                         _("Random test_pattern is not allowed "
945                           "in read-only mode"));
946                         exit(1);
947                 }
948         }
949         if (optind > argc - 1)
950                 usage();
951         device_name = argv[optind++];
952         if (optind > argc - 1) {
953                 errcode = ext2fs_get_device_size(device_name,
954                                                  block_size,
955                                                  &last_block);
956                 if (errcode == EXT2_ET_UNIMPLEMENTED) {
957                         com_err(program_name, 0,
958                                 _("Couldn't determine device size; you "
959                                   "must specify\nthe size manually\n"));
960                         exit(1);
961                 }
962                 if (errcode) {
963                         com_err(program_name, errcode,
964                                 _("while trying to determine device size"));
965                         exit(1);
966                 }
967         } else {
968                 last_block = strtoul (argv[optind], &tmp, 0);
969                 if (*tmp) {
970                         com_err (program_name, 0, _("invalid blocks count - %s"),
971                                  argv[optind]);
972                         exit (1);
973                 }
974                 optind++;
975         }
976         if (optind <= argc-1) {
977                 from_count = strtoul (argv[optind], &tmp, 0);
978                 if (*tmp) {
979                         com_err (program_name, 0, _("invalid starting block - %s"),
980                                  argv[optind]);
981                         exit (1);
982                 }
983         } else from_count = 0;
984         if (from_count >= last_block) {
985             com_err (program_name, 0, _("invalid blocks range: %lu-%lu"),
986                      (unsigned long) from_count, (unsigned long) last_block);
987             exit (1);
988         }
989         if (w_flag)
990                 check_mount(device_name);
991         
992         open_flag = w_flag ? O_RDWR : O_RDONLY;
993         dev = open (device_name, open_flag);
994         if (dev == -1) {
995                 com_err (program_name, errno, _("while trying to open %s"),
996                          device_name);
997                 exit (1);
998         }
999         if (host_device_name) {
1000                 host_dev = open (host_device_name, open_flag);
1001                 if (host_dev == -1) {
1002                         com_err (program_name, errno,
1003                                  _("while trying to open %s"),
1004                                  host_device_name);
1005                         exit (1);
1006                 }
1007         } else
1008                 host_dev = dev;
1009         if (input_file) {
1010                 if (strcmp (input_file, "-") == 0)
1011                         in = stdin;
1012                 else {
1013                         in = fopen (input_file, "r");
1014                         if (in == NULL)
1015                         {
1016                                 com_err (program_name, errno,
1017                                          _("while trying to open %s"),
1018                                          input_file);
1019                                 exit (1);
1020                         }
1021                 }
1022         }
1023         if (output_file && strcmp (output_file, "-") != 0)
1024         {
1025                 out = fopen (output_file, "w");
1026                 if (out == NULL)
1027                 {
1028                         com_err (program_name, errno,
1029                                  _("while trying to open %s"),
1030                                  output_file);
1031                         exit (1);
1032                 }
1033         }
1034         else
1035                 out = stdout;
1036
1037         errcode = ext2fs_badblocks_list_create(&bb_list,0);
1038         if (errcode) {
1039                 com_err (program_name, errcode,
1040                          _("creating in-memory bad blocks list"));
1041                 exit (1);
1042         }
1043
1044         if (in) {
1045                 for(;;) {
1046                         switch(fscanf (in, "%u\n", &next_bad)) {
1047                                 case 0:
1048                                         com_err (program_name, 0, "input file - bad format");
1049                                         exit (1);
1050                                 case EOF:
1051                                         break;
1052                                 default:
1053                                         errcode = ext2fs_badblocks_list_add(bb_list,next_bad);
1054                                         if (errcode) {
1055                                                 com_err (program_name, errcode, _("adding to in-memory bad block list"));
1056                                                 exit (1);
1057                                         }
1058                                         continue;
1059                         }
1060                         break;
1061                 }
1062
1063                 if (in != stdin)
1064                         fclose (in);
1065         }
1066
1067         do {
1068                 unsigned int bb_count;
1069
1070                 bb_count = test_func(dev, last_block, block_size,
1071                                      from_count, blocks_at_once);
1072                 if (bb_count)
1073                         passes_clean = 0;
1074                 else
1075                         ++passes_clean;
1076                 
1077                 if (v_flag)
1078                         fprintf(stderr,
1079                                 _("Pass completed, %u bad blocks found.\n"), 
1080                                 bb_count);
1081
1082         } while (passes_clean < num_passes);
1083
1084         close (dev);
1085         if (out != stdout)
1086                 fclose (out);
1087         if (t_patts)
1088                 free(t_patts);
1089         return 0;
1090 }
1091