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