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