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