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