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