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