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