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