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