]> git.ipfire.org Git - thirdparty/e2fsprogs.git/blame - misc/badblocks.c
Fix gcc -Wall warnings
[thirdparty/e2fsprogs.git] / misc / badblocks.c
CommitLineData
3839e657
TT
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 *
dd018f5a 8 * Copyright 1995, 1996, 1997, 1998, 1999 by Theodore Ts'o
879ac920 9 * Copyright 1999 by David Beattie
19c78dc0 10 *
3839e657
TT
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>
19c78dc0
TT
13 *
14 * %Begin-Header%
15 * This file may be redistributed under the terms of the GNU Public
16 * License.
17 * %End-Header%
3839e657
TT
18 */
19
20/*
21 * History:
22 * 93/05/26 - Creation from e2fsck
23 * 94/02/27 - Made a separate bad blocks checker
879ac920 24 * 99/06/30...99/07/26 - Added non-destructive write-testing,
dd018f5a
TT
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)
3839e657
TT
30 */
31
1c29b097
TT
32#define _GNU_SOURCE /* for O_DIRECT */
33
3839e657
TT
34#include <errno.h>
35#include <fcntl.h>
a418d3ad 36#ifdef HAVE_GETOPT_H
3839e657 37#include <getopt.h>
373b8337
TT
38#else
39extern char *optarg;
40extern int optind;
a418d3ad 41#endif
3839e657
TT
42#include <signal.h>
43#include <stdio.h>
44#include <stdlib.h>
45#include <string.h>
46#include <unistd.h>
879ac920 47#include <setjmp.h>
6d40f568 48#include <time.h>
5267a520 49#include <limits.h>
3839e657
TT
50
51#include <sys/ioctl.h>
f3db3566 52#include <sys/types.h>
3839e657 53
3839e657 54#include "et/com_err.h"
d40259fd 55#include "ext2fs/ext2_io.h"
54c637d4 56#include "ext2fs/ext2_fs.h"
879ac920 57#include "ext2fs/ext2fs.h"
d9c56d3c 58#include "nls-enable.h"
3839e657
TT
59
60const char * program_name = "badblocks";
f63978af 61const char * done_string = N_("done \n");
3839e657 62
4d003982
TT
63static int v_flag = 0; /* verbose */
64static int w_flag = 0; /* do r/w test: 0=no, 1=yes,
65 * 2=non-destructive */
66static int s_flag = 0; /* show progress of test */
981dc56a 67static int force = 0; /* force check of mounted device */
849b6bc8
TT
68static int t_flag = 0; /* number of test patterns */
69static int t_max = 0; /* allocated test patterns */
84c05457 70static unsigned long *t_patts = NULL; /* test patterns */
1c29b097 71static int current_O_DIRECT = 0; /* Current status of O_DIRECT flag */
f63978af 72static int exclusive_ok = 0;
1c29b097 73
849b6bc8 74#define T_INC 32
4d003982 75
1c29b097
TT
76int sys_page_size = 4096;
77
8820c79f 78static void usage(void)
3839e657 79{
849b6bc8 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"),
3839e657
TT
81 program_name);
82 exit (1);
83}
84
d8b5f777
TT
85static void exclusive_usage(void)
86{
87 fprintf(stderr,
017a76ee
TT
88 _("%s: The -n and -w options are mutually exclusive.\n\n"),
89 program_name);
90 exit(1);
d8b5f777
TT
91}
92
19c78dc0
TT
93static unsigned long currently_testing = 0;
94static unsigned long num_blocks = 0;
879ac920
TT
95static ext2_badblocks_list bb_list = NULL;
96static FILE *out;
97static blk_t next_bad = 0;
98static ext2_badblocks_iterate bb_iter = NULL;
19c78dc0 99
1c29b097
TT
100static 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
dd018f5a
TT
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 */
127static int bb_output (unsigned long bad)
879ac920
TT
128{
129 errcode_t errcode;
130
dd018f5a
TT
131 if (ext2fs_badblocks_list_test(bb_list, bad))
132 return 0;
133
cc4f98ed
TT
134 fprintf(out, "%lu\n", bad);
135 fflush(out);
879ac920
TT
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);
dd018f5a 149 return 1;
879ac920
TT
150}
151
8820c79f 152static void print_status(void)
19c78dc0 153{
c76564a8
TT
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);
19c78dc0
TT
156 fflush (stderr);
157}
158
54434927 159static void alarm_intr(int alnum EXT2FS_ATTR((unused)))
19c78dc0
TT
160{
161 signal (SIGALRM, alarm_intr);
162 alarm(1);
163 if (!num_blocks)
164 return;
c76564a8 165 print_status();
19c78dc0
TT
166}
167
879ac920
TT
168static void *terminate_addr = NULL;
169
54434927 170static void terminate_intr(int signo EXT2FS_ATTR((unused)))
879ac920
TT
171{
172 if (terminate_addr)
173 longjmp(terminate_addr,1);
174 exit(1);
175}
176
981dc56a 177static void capture_terminate(jmp_buf term_addr)
879ac920
TT
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
8820c79f 188static void uncapture_terminate(void)
4d003982
TT
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
1f9a60c2
TT
199static void set_o_direct(int dev, unsigned char *buffer, size_t size,
200 unsigned long current_block)
1c29b097
TT
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) ||
1f9a60c2
TT
207 ((size & (sys_page_size - 1)) != 0) ||
208 ((current_block & ((sys_page_size >> 9)-1)) != 0))
1c29b097
TT
209 new_flag = 0;
210
211 if (new_flag != current_O_DIRECT) {
dc058719 212 /* printf("%s O_DIRECT\n", new_flag ? "Setting" : "Clearing"); */
1c29b097
TT
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
84c05457
TT
224static void pattern_fill(unsigned char *buffer, unsigned long pattern,
225 size_t n)
849b6bc8 226{
54434927 227 unsigned int i, nb;
84c05457 228 unsigned char bpattern[sizeof(pattern)], *ptr;
849b6bc8 229
023cbb30 230 if (pattern == (unsigned long) ~0) {
849b6bc8
TT
231 for (ptr = buffer; ptr < buffer + n; ptr++) {
232 (*ptr) = random() % (1 << (8 * sizeof(char)));
233 }
234 if (s_flag | v_flag)
54434927 235 fputs(_("Testing with random pattern: "), stderr);
849b6bc8
TT
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++) {
54434927
TT
246 *ptr = bpattern[i];
247 if (i == 0)
849b6bc8 248 i = nb;
54434927
TT
249 else
250 i--;
849b6bc8 251 }
84c05457 252 if (s_flag | v_flag) {
54434927 253 fputs(_("Testing with pattern 0x"), stderr);
84c05457
TT
254 for (i = 0; i <= nb; i++)
255 fprintf(stderr, "%02x", buffer[i]);
54434927 256 fputs(": ", stderr);
84c05457 257 }
849b6bc8
TT
258 }
259}
260
3839e657 261/*
879ac920
TT
262 * Perform a read of a sequence of blocks; return the number of blocks
263 * successfully sequentially read.
3839e657 264 */
48e6e813 265static long do_read (int dev, unsigned char * buffer, int try, int block_size,
3839e657
TT
266 unsigned long current_block)
267{
268 long got;
269
1f9a60c2 270 set_o_direct(dev, buffer, try * block_size, current_block);
1c29b097 271
19c78dc0
TT
272 if (v_flag > 1)
273 print_status();
274
3839e657 275 /* Seek to the correct loc. */
19c78dc0 276 if (ext2fs_llseek (dev, (ext2_loff_t) current_block * block_size,
f3db3566 277 SEEK_SET) != (ext2_loff_t) current_block * block_size)
d9c56d3c 278 com_err (program_name, errno, _("during seek"));
3839e657
TT
279
280 /* Try the read */
281 got = read (dev, buffer, try * block_size);
282 if (got < 0)
283 got = 0;
9f10a7b3 284 if (got & 511)
d9c56d3c 285 fprintf(stderr, _("Weird value (%ld) in do_read\n"), got);
879ac920
TT
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 */
48e6e813 294static long do_write (int dev, unsigned char * buffer, int try, int block_size,
879ac920
TT
295 unsigned long current_block)
296{
297 long got;
298
1f9a60c2 299 set_o_direct(dev, buffer, try * block_size, current_block);
1c29b097 300
879ac920
TT
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)
d9c56d3c 307 com_err (program_name, errno, _("during seek"));
879ac920
TT
308
309 /* Try the write */
310 got = write (dev, buffer, try * block_size);
311 if (got < 0)
312 got = 0;
313 if (got & 511)
54434927 314 fprintf(stderr, "Weird value (%ld) in do_write\n", got);
3839e657
TT
315 got /= block_size;
316 return got;
317}
318
879ac920
TT
319static int host_dev;
320
4d404547 321static void flush_bufs(void)
a418d3ad 322{
4d404547 323 errcode_t retval;
a418d3ad 324
4d404547
TT
325 retval = ext2fs_sync_device(host_dev, 1);
326 if (retval)
327 com_err(program_name, retval, _("during ext2fs_sync_device"));
a418d3ad
TT
328}
329
cd130a08 330static unsigned int test_ro (int dev, unsigned long last_block,
dd018f5a
TT
331 int block_size, unsigned long from_count,
332 unsigned long blocks_at_once)
3839e657 333{
48e6e813 334 unsigned char * blkbuf;
3839e657
TT
335 int try;
336 long got;
879ac920
TT
337 unsigned int bb_count = 0;
338 errcode_t errcode;
3839e657 339
879ac920
TT
340 errcode = ext2fs_badblocks_list_iterate_begin(bb_list,&bb_iter);
341 if (errcode) {
d9c56d3c
TT
342 com_err (program_name, errcode,
343 _("while beginning bad block list iteration"));
879ac920
TT
344 exit (1);
345 }
346 do {
347 ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
348 } while (next_bad && next_bad < from_count);
349
849b6bc8 350 if (t_flag) {
1c29b097 351 blkbuf = allocate_buffer((blocks_at_once + 1) * block_size);
849b6bc8 352 } else {
1c29b097 353 blkbuf = allocate_buffer(blocks_at_once * block_size);
849b6bc8 354 }
3839e657
TT
355 if (!blkbuf)
356 {
d9c56d3c 357 com_err (program_name, ENOMEM, _("while allocating buffers"));
3839e657
TT
358 exit (1);
359 }
f3db3566 360 if (v_flag) {
849b6bc8 361 fprintf (stderr, _("Checking blocks %lu to %lu\n"), from_count,
8938ce64 362 last_block - 1);
f3db3566 363 }
849b6bc8 364 if (t_flag) {
54434927 365 fputs(_("Checking for bad blocks in read-only mode\n"), stderr);
849b6bc8
TT
366 pattern_fill(blkbuf + blocks_at_once * block_size,
367 t_patts[0], block_size);
368 }
369 flush_bufs();
879ac920 370 try = blocks_at_once;
f3db3566 371 currently_testing = from_count;
8938ce64 372 num_blocks = last_block - 1;
849b6bc8 373 if (!t_flag && (s_flag || v_flag)) {
54434927 374 fputs(_("Checking for bad blocks (read-only test): "), stderr);
19c78dc0
TT
375 if (v_flag <= 1)
376 alarm_intr(SIGALRM);
3839e657 377 }
cd130a08 378 while (currently_testing < last_block)
3839e657 379 {
879ac920
TT
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 }
cd130a08
TT
390 if (currently_testing + try > last_block)
391 try = last_block - currently_testing;
879ac920 392 got = do_read (dev, blkbuf, try, block_size, currently_testing);
849b6bc8
TT
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 }
3839e657
TT
403 currently_testing += got;
404 if (got == try) {
879ac920 405 try = blocks_at_once;
1f9a60c2 406 /* recover page-aligned offset for O_DIRECT */
54434927 407 if ( blocks_at_once >= (unsigned long) (sys_page_size >> 9)
1f9a60c2
TT
408 && (currently_testing % (sys_page_size >> 9)!= 0))
409 try -= (sys_page_size >> 9)
410 - (currently_testing
411 % (sys_page_size >> 9));
3839e657
TT
412 continue;
413 }
414 else
415 try = 1;
879ac920 416 if (got == 0) {
dd018f5a 417 bb_count += bb_output(currently_testing++);
879ac920 418 }
3839e657
TT
419 }
420 num_blocks = 0;
421 alarm(0);
849b6bc8 422 if (s_flag || v_flag)
3ef681c5 423 fputs(_(done_string), stderr);
879ac920 424
f3db3566 425 fflush (stderr);
3839e657 426 free (blkbuf);
879ac920
TT
427
428 ext2fs_badblocks_list_iterate_end(bb_iter);
429
430 return bb_count;
3839e657
TT
431}
432
cd130a08 433static unsigned int test_rw (int dev, unsigned long last_block,
dd018f5a
TT
434 int block_size, unsigned long from_count,
435 unsigned long blocks_at_once)
3839e657 436{
1c29b097 437 unsigned char *buffer, *read_buffer;
84c05457
TT
438 const unsigned long patterns[] = {0xaa, 0x55, 0xff, 0x00};
439 const unsigned long *pattern;
1c29b097 440 int i, try, got, nr_pattern, pat_idx;
879ac920 441 unsigned int bb_count = 0;
3839e657 442
1c29b097
TT
443 buffer = allocate_buffer(2 * blocks_at_once * block_size);
444 read_buffer = buffer + blocks_at_once * block_size;
445
446 if (!buffer) {
d9c56d3c 447 com_err (program_name, ENOMEM, _("while allocating buffers"));
3839e657
TT
448 exit (1);
449 }
450
4d404547 451 flush_bufs();
a418d3ad 452
19c78dc0 453 if (v_flag) {
54434927
TT
454 fputs(_("Checking for bad blocks in read-write mode\n"),
455 stderr);
d9c56d3c 456 fprintf(stderr, _("From block %lu to %lu\n"),
cd130a08 457 from_count, last_block);
19c78dc0 458 }
849b6bc8
TT
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++) {
1c29b097
TT
467 pattern_fill(buffer, pattern[pat_idx],
468 blocks_at_once * block_size);
8938ce64 469 num_blocks = last_block - 1;
f3db3566 470 currently_testing = from_count;
19c78dc0 471 if (s_flag && v_flag <= 1)
f3db3566 472 alarm_intr(SIGALRM);
1c29b097
TT
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);
19c78dc0
TT
480 if (v_flag > 1)
481 print_status();
1c29b097
TT
482
483 currently_testing += got;
484 if (got == try) {
485 try = blocks_at_once;
1f9a60c2 486 /* recover page-aligned offset for O_DIRECT */
54434927 487 if ( blocks_at_once >= (unsigned long) (sys_page_size >> 9)
1f9a60c2
TT
488 && (currently_testing %
489 (sys_page_size >> 9)!= 0))
490 try -= (sys_page_size >> 9)
491 - (currently_testing
492 % (sys_page_size >> 9));
1c29b097
TT
493 continue;
494 } else
495 try = 1;
496 if (got == 0) {
497 bb_count += bb_output(currently_testing++);
498 }
3839e657 499 }
1c29b097 500
f3db3566
TT
501 num_blocks = 0;
502 alarm (0);
503 if (s_flag | v_flag)
3ef681c5 504 fputs(_(done_string), stderr);
4d404547 505 flush_bufs();
f3db3566 506 if (s_flag | v_flag)
54434927 507 fputs(_("Reading and comparing: "), stderr);
cd130a08 508 num_blocks = last_block;
f3db3566 509 currently_testing = from_count;
19c78dc0 510 if (s_flag && v_flag <= 1)
f3db3566 511 alarm_intr(SIGALRM);
1c29b097
TT
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;
1f9a60c2 530 /* recover page-aligned offset for O_DIRECT */
54434927 531 if ( blocks_at_once >= (unsigned long) (sys_page_size >> 9)
1f9a60c2
TT
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;
19c78dc0
TT
538 if (v_flag > 1)
539 print_status();
3839e657 540 }
1c29b097 541
f3db3566
TT
542 num_blocks = 0;
543 alarm (0);
544 if (s_flag | v_flag)
3ef681c5 545 fputs(_(done_string), stderr);
4d404547 546 flush_bufs();
3839e657 547 }
849b6bc8 548 uncapture_terminate();
6d40f568 549 free(buffer);
879ac920
TT
550 return bb_count;
551}
552
d49a22b7
TT
553struct saved_blk_record {
554 blk_t block;
555 int num;
556};
557
cd130a08 558static unsigned int test_nd (int dev, unsigned long last_block,
dd018f5a
TT
559 int block_size, unsigned long from_count,
560 unsigned long blocks_at_once)
879ac920 561{
48e6e813 562 unsigned char *blkbuf, *save_ptr, *test_ptr, *read_ptr;
1c29b097 563 unsigned char *test_base, *save_base, *read_base;
dd018f5a 564 int try, i;
84c05457
TT
565 const unsigned long patterns[] = { ~0 };
566 const unsigned long *pattern;
849b6bc8 567 int nr_pattern, pat_idx;
a551b783 568 long got, used2, written, save_currently_testing;
d49a22b7 569 struct saved_blk_record *test_record;
a551b783
TT
570 /* This is static to prevent being clobbered by the longjmp */
571 static int num_saved;
879ac920 572 jmp_buf terminate_env;
879ac920 573 errcode_t errcode;
54434927
TT
574 unsigned long buf_used;
575 static unsigned int bb_count;
879ac920 576
54434927 577 bb_count = 0;
879ac920
TT
578 errcode = ext2fs_badblocks_list_iterate_begin(bb_list,&bb_iter);
579 if (errcode) {
dd018f5a 580 com_err (program_name, errcode,
d9c56d3c 581 _("while beginning bad block list iteration"));
879ac920
TT
582 exit (1);
583 }
584 do {
585 ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
586 } while (next_bad && next_bad < from_count);
587
1c29b097 588 blkbuf = allocate_buffer(3 * blocks_at_once * block_size);
d49a22b7
TT
589 test_record = malloc (blocks_at_once*sizeof(struct saved_blk_record));
590 if (!blkbuf || !test_record) {
d9c56d3c 591 com_err(program_name, ENOMEM, _("while allocating buffers"));
879ac920
TT
592 exit (1);
593 }
1c29b097
TT
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
d49a22b7 599 num_saved = 0;
879ac920 600
4d404547 601 flush_bufs();
879ac920 602 if (v_flag) {
54434927 603 fputs(_("Checking for bad blocks in non-destructive read-write mode\n"), stderr);
cd130a08 604 fprintf (stderr, _("From block %lu to %lu\n"), from_count, last_block);
879ac920 605 }
879ac920 606 if (s_flag || v_flag > 1) {
54434927 607 fputs(_("Checking for bad blocks (non-destructive read-write test)\n"), stderr);
879ac920 608 }
4d003982
TT
609 if (setjmp(terminate_env)) {
610 /*
611 * Abnormal termination by a signal is handled here.
4d003982 612 */
a551b783 613 signal (SIGALRM, SIG_IGN);
54434927 614 fputs(_("\nInterrupt caught, cleaning up\n"), stderr);
879ac920 615
1c29b097 616 save_ptr = save_base;
d49a22b7
TT
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 }
879ac920 622 fflush (out);
dd018f5a 623 exit(1);
879ac920 624 }
4d003982
TT
625
626 /* set up abend handler */
627 capture_terminate(terminate_env);
628
849b6bc8
TT
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++) {
1c29b097
TT
637 pattern_fill(test_base, pattern[pat_idx],
638 blocks_at_once * block_size);
4d003982 639
849b6bc8
TT
640 buf_used = 0;
641 bb_count = 0;
1c29b097
TT
642 save_ptr = save_base;
643 test_ptr = test_base;
849b6bc8 644 currently_testing = from_count;
8938ce64 645 num_blocks = last_block - 1;
849b6bc8
TT
646 if (s_flag && v_flag <= 1)
647 alarm_intr(SIGALRM);
4d003982 648
849b6bc8 649 while (currently_testing < last_block) {
1c29b097 650 got = try = blocks_at_once - buf_used;
849b6bc8
TT
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;
d49a22b7 660 }
849b6bc8
TT
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;
4d003982 669 }
4d003982 670
849b6bc8
TT
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;
4d003982
TT
688 save_ptr += got * block_size;
689 test_ptr += got * block_size;
849b6bc8
TT
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;
1c29b097
TT
718 save_ptr = save_base;
719 test_ptr = test_base;
720 read_ptr = read_base;
849b6bc8
TT
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
1c29b097
TT
746 /* write back original data */
747 do_write (dev, save_ptr, got,
748 block_size, currently_testing);
749 save_ptr += got * block_size;
849b6bc8
TT
750
751 currently_testing += got;
849b6bc8
TT
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;
1c29b097
TT
760 save_ptr = save_base;
761 test_ptr = test_base;
849b6bc8 762 currently_testing = save_currently_testing;
4d003982 763 }
849b6bc8
TT
764 num_blocks = 0;
765 alarm(0);
766 if (s_flag || v_flag > 1)
3ef681c5 767 fputs(_(done_string), stderr);
4d003982 768
849b6bc8 769 flush_bufs();
4d003982 770 }
4d003982 771 uncapture_terminate();
dd018f5a
TT
772 fflush(stderr);
773 free(blkbuf);
d49a22b7 774 free(test_record);
879ac920
TT
775
776 ext2fs_badblocks_list_iterate_end(bb_iter);
777
778 return bb_count;
3839e657
TT
779}
780
981dc56a
TT
781static 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 }
2fa8f37f
TT
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 }
981dc56a 804
f63978af 805 if ((mount_flags & EXT2_MF_BUSY) && !exclusive_ok) {
2fa8f37f
TT
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;
981dc56a 812 }
2fa8f37f 813
981dc56a
TT
814}
815
816
00e5433e 817int main (int argc, char ** argv)
3839e657 818{
519149fb 819 int c;
3839e657
TT
820 char * tmp;
821 char * device_name;
879ac920
TT
822 char * host_device_name = NULL;
823 char * input_file = NULL;
3839e657 824 char * output_file = NULL;
879ac920 825 FILE * in = NULL;
dd018f5a 826 int block_size = 1024;
167af997 827 unsigned long blocks_at_once = 64;
cd130a08 828 blk_t last_block, from_count;
879ac920
TT
829 int num_passes = 0;
830 int passes_clean = 0;
3839e657 831 int dev;
879ac920 832 errcode_t errcode;
84c05457 833 unsigned long pattern;
8820c79f
TT
834 unsigned int (*test_func)(int, unsigned long,
835 int, unsigned long,
836 unsigned long);
1c29b097
TT
837 int open_flag = 0;
838 long sysval;
3839e657
TT
839
840 setbuf(stdout, NULL);
841 setbuf(stderr, NULL);
d9c56d3c
TT
842#ifdef ENABLE_NLS
843 setlocale(LC_MESSAGES, "");
14308a53 844 setlocale(LC_CTYPE, "");
d9c56d3c
TT
845 bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
846 textdomain(NLS_CAT_NAME);
847#endif
6d40f568 848 srandom((unsigned int)time(NULL)); /* simple randomness is enough */
4d003982 849 test_func = test_ro;
4d404547 850
1c29b097
TT
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
3839e657
TT
863 if (argc && *argv)
864 program_name = *argv;
f63978af 865 while ((c = getopt (argc, argv, "b:fi:o:svwnc:p:h:t:X")) != EOF) {
3839e657
TT
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,
d9c56d3c 871 _("bad block size - %s"), optarg);
3839e657
TT
872 exit (1);
873 }
874 break;
981dc56a
TT
875 case 'f':
876 force++;
877 break;
879ac920
TT
878 case 'i':
879 input_file = optarg;
880 break;
3839e657
TT
881 case 'o':
882 output_file = optarg;
883 break;
884 case 's':
885 s_flag = 1;
886 break;
887 case 'v':
19c78dc0 888 v_flag++;
3839e657
TT
889 break;
890 case 'w':
4d003982 891 if (w_flag)
d8b5f777 892 exclusive_usage();
4d003982
TT
893 test_func = test_rw;
894 w_flag = 1;
879ac920
TT
895 break;
896 case 'n':
4d003982 897 if (w_flag)
d8b5f777 898 exclusive_usage();
4d003982 899 test_func = test_nd;
879ac920
TT
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;
3839e657 920 break;
849b6bc8
TT
921 case 't':
922 if (t_flag + 1 > t_max) {
48e6e813 923 unsigned long *t_patts_new;
849b6bc8
TT
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 }
84c05457
TT
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,
849b6bc8 942 _("invalid test_pattern: %s\n"),
84c05457
TT
943 optarg);
944 exit(1);
945 }
54434927 946 if (pattern == (unsigned long) ~0)
84c05457
TT
947 pattern = 0xffff;
948 t_patts[t_flag++] = pattern;
849b6bc8 949 }
849b6bc8 950 break;
f63978af
TT
951 case 'X':
952 exclusive_ok++;
953 break;
3839e657 954 default:
818180cd 955 usage();
3839e657
TT
956 }
957 }
849b6bc8
TT
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 }
54434927 965 if (t_patts && (t_patts[0] == (unsigned long) ~0)) {
849b6bc8
TT
966 com_err(program_name, 0,
967 _("Random test_pattern is not allowed "
968 "in read-only mode"));
969 exit(1);
970 }
971 }
3839e657 972 if (optind > argc - 1)
818180cd 973 usage();
3839e657 974 device_name = argv[optind++];
35964b5c
TT
975 if (optind > argc - 1) {
976 errcode = ext2fs_get_device_size(device_name,
977 block_size,
cd130a08 978 &last_block);
35964b5c
TT
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 {
5267a520
TT
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)) {
f37ab68a 996 com_err (program_name, 0, _("invalid blocks count - %s"),
35964b5c
TT
997 argv[optind]);
998 exit (1);
999 }
5267a520 1000 last_block++;
35964b5c 1001 optind++;
3839e657 1002 }
35964b5c 1003 if (optind <= argc-1) {
5267a520 1004 errno = 0;
f3db3566 1005 from_count = strtoul (argv[optind], &tmp, 0);
5267a520
TT
1006 printf("from_count = %d\n", from_count);
1007 if (*tmp || errno ||
1008 (from_count == ULONG_MAX && errno == ERANGE)) {
f37ab68a 1009 com_err (program_name, 0, _("invalid starting block - %s"),
a551b783
TT
1010 argv[optind]);
1011 exit (1);
1012 }
f3db3566 1013 } else from_count = 0;
cd130a08 1014 if (from_count >= last_block) {
d4e0b1c6 1015 com_err (program_name, 0, _("invalid starting block (%lu): must be less than %lu"),
54434927 1016 (unsigned long) from_count, (unsigned long) last_block);
f3db3566
TT
1017 exit (1);
1018 }
981dc56a
TT
1019 if (w_flag)
1020 check_mount(device_name);
1021
1c29b097
TT
1022 open_flag = w_flag ? O_RDWR : O_RDONLY;
1023 dev = open (device_name, open_flag);
5493a27d 1024 if (dev == -1) {
d9c56d3c 1025 com_err (program_name, errno, _("while trying to open %s"),
3839e657
TT
1026 device_name);
1027 exit (1);
1028 }
879ac920 1029 if (host_device_name) {
1c29b097 1030 host_dev = open (host_device_name, open_flag);
5493a27d 1031 if (host_dev == -1) {
d9c56d3c
TT
1032 com_err (program_name, errno,
1033 _("while trying to open %s"),
1034 host_device_name);
879ac920
TT
1035 exit (1);
1036 }
1037 } else
1038 host_dev = dev;
3e699064 1039 if (input_file) {
879ac920
TT
1040 if (strcmp (input_file, "-") == 0)
1041 in = stdin;
1042 else {
1043 in = fopen (input_file, "r");
1044 if (in == NULL)
1045 {
d9c56d3c
TT
1046 com_err (program_name, errno,
1047 _("while trying to open %s"),
879ac920
TT
1048 input_file);
1049 exit (1);
1050 }
1051 }
3e699064 1052 }
3839e657
TT
1053 if (output_file && strcmp (output_file, "-") != 0)
1054 {
1055 out = fopen (output_file, "w");
1056 if (out == NULL)
1057 {
d9c56d3c
TT
1058 com_err (program_name, errno,
1059 _("while trying to open %s"),
879ac920 1060 output_file);
3839e657
TT
1061 exit (1);
1062 }
1063 }
1064 else
1065 out = stdout;
879ac920
TT
1066
1067 errcode = ext2fs_badblocks_list_create(&bb_list,0);
1068 if (errcode) {
d9c56d3c 1069 com_err (program_name, errcode,
bb145b01 1070 _("while creating in-memory bad blocks list"));
879ac920
TT
1071 exit (1);
1072 }
1073
1074 if (in) {
1075 for(;;) {
a551b783 1076 switch(fscanf (in, "%u\n", &next_bad)) {
879ac920
TT
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) {
bb145b01 1085 com_err (program_name, errcode, _("while adding to in-memory bad block list"));
879ac920
TT
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
cd130a08 1100 bb_count = test_func(dev, last_block, block_size,
4d003982
TT
1101 from_count, blocks_at_once);
1102 if (bb_count)
1103 passes_clean = 0;
1104 else
1105 ++passes_clean;
1106
879ac920 1107 if (v_flag)
d9c56d3c
TT
1108 fprintf(stderr,
1109 _("Pass completed, %u bad blocks found.\n"),
1110 bb_count);
879ac920
TT
1111
1112 } while (passes_clean < num_passes);
1113
3839e657
TT
1114 close (dev);
1115 if (out != stdout)
1116 fclose (out);
849b6bc8
TT
1117 if (t_patts)
1118 free(t_patts);
879ac920 1119 return 0;
3839e657 1120}
d9c56d3c 1121