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