blob: ba3db875456880f4481c15dbbb974f88389f7ce7 [file] [log] [blame]
Theodore Ts'o3839e651997-04-26 13:21:57 +00001/*
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 *
Theodore Ts'odd018f52000-02-06 23:57:07 +00008 * Copyright 1995, 1996, 1997, 1998, 1999 by Theodore Ts'o
Theodore Ts'o879ac922000-01-18 20:59:11 +00009 * Copyright 1999 by David Beattie
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000010 *
Theodore Ts'o3839e651997-04-26 13:21:57 +000011 * 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>
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000013 *
14 * %Begin-Header%
15 * This file may be redistributed under the terms of the GNU Public
16 * License.
17 * %End-Header%
Theodore Ts'o3839e651997-04-26 13:21:57 +000018 */
19
20/*
21 * History:
22 * 93/05/26 - Creation from e2fsck
23 * 94/02/27 - Made a separate bad blocks checker
Theodore Ts'o879ac922000-01-18 20:59:11 +000024 * 99/06/30...99/07/26 - Added non-destructive write-testing,
Theodore Ts'odd018f52000-02-06 23:57:07 +000025 * 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)
Theodore Ts'o3839e651997-04-26 13:21:57 +000030 */
31
32#include <errno.h>
33#include <fcntl.h>
Theodore Ts'oa418d3a1997-04-26 14:00:26 +000034#ifdef HAVE_GETOPT_H
Theodore Ts'o3839e651997-04-26 13:21:57 +000035#include <getopt.h>
Theodore Ts'o373b8332000-04-03 16:22:35 +000036#else
37extern char *optarg;
38extern int optind;
Theodore Ts'oa418d3a1997-04-26 14:00:26 +000039#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +000040#include <signal.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <unistd.h>
Theodore Ts'o879ac922000-01-18 20:59:11 +000045#include <setjmp.h>
Theodore Ts'o3839e651997-04-26 13:21:57 +000046
47#include <sys/ioctl.h>
Theodore Ts'of3db3561997-04-26 13:34:30 +000048#include <sys/types.h>
Theodore Ts'o3839e651997-04-26 13:21:57 +000049
Theodore Ts'o3839e651997-04-26 13:21:57 +000050#include "et/com_err.h"
Theodore Ts'od40259f1997-10-20 00:44:26 +000051#include "ext2fs/ext2_io.h"
Theodore Ts'o54c637d2001-05-14 11:45:38 +000052#include "ext2fs/ext2_fs.h"
Theodore Ts'o879ac922000-01-18 20:59:11 +000053#include "ext2fs/ext2fs.h"
Theodore Ts'od9c56d32000-02-08 00:47:55 +000054#include "nls-enable.h"
Theodore Ts'o3839e651997-04-26 13:21:57 +000055
56const char * program_name = "badblocks";
Theodore Ts'o8f5c0f62000-02-08 20:29:44 +000057const char * done_string = N_("done \n");
Theodore Ts'o3839e651997-04-26 13:21:57 +000058
Theodore Ts'o4d003982000-04-03 16:01:11 +000059static int v_flag = 0; /* verbose */
60static int w_flag = 0; /* do r/w test: 0=no, 1=yes,
61 * 2=non-destructive */
62static int s_flag = 0; /* show progress of test */
Theodore Ts'o981dc562000-07-06 14:13:29 +000063static int force = 0; /* force check of mounted device */
Theodore Ts'o4d003982000-04-03 16:01:11 +000064
Theodore Ts'o8820c792001-01-06 04:20:03 +000065static void usage(void)
Theodore Ts'o3839e651997-04-26 13:21:57 +000066{
Theodore Ts'ocd130a02001-05-05 05:43:23 +000067 fprintf(stderr, _("Usage: %s [-b block_size] [-i input_file] [-o output_file] [-svwnf]\n [-c blocks_at_once] [-p num_passes] device [last_block [start_count]]\n"),
Theodore Ts'o3839e651997-04-26 13:21:57 +000068 program_name);
69 exit (1);
70}
71
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000072static unsigned long currently_testing = 0;
73static unsigned long num_blocks = 0;
Theodore Ts'o879ac922000-01-18 20:59:11 +000074static ext2_badblocks_list bb_list = NULL;
75static FILE *out;
76static blk_t next_bad = 0;
77static ext2_badblocks_iterate bb_iter = NULL;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000078
Theodore Ts'odd018f52000-02-06 23:57:07 +000079/*
80 * This routine reports a new bad block. If the bad block has already
81 * been seen before, then it returns 0; otherwise it returns 1.
82 */
83static int bb_output (unsigned long bad)
Theodore Ts'o879ac922000-01-18 20:59:11 +000084{
85 errcode_t errcode;
86
Theodore Ts'odd018f52000-02-06 23:57:07 +000087 if (ext2fs_badblocks_list_test(bb_list, bad))
88 return 0;
89
Theodore Ts'occ4f98e2003-04-03 11:37:46 -050090 fprintf(out, "%lu\n", bad);
91 fflush(out);
Theodore Ts'o879ac922000-01-18 20:59:11 +000092
93 errcode = ext2fs_badblocks_list_add (bb_list, bad);
94 if (errcode) {
95 com_err (program_name, errcode, "adding to in-memory bad block list");
96 exit (1);
97 }
98
99 /* kludge:
100 increment the iteration through the bb_list if
101 an element was just added before the current iteration
102 position. This should not cause next_bad to change. */
103 if (bb_iter && bad < next_bad)
104 ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
Theodore Ts'odd018f52000-02-06 23:57:07 +0000105 return 1;
Theodore Ts'o879ac922000-01-18 20:59:11 +0000106}
107
Theodore Ts'o8820c792001-01-06 04:20:03 +0000108static void print_status(void)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000109{
110 fprintf(stderr, "%9ld/%9ld", currently_testing, num_blocks);
111 fprintf(stderr, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
112 fflush (stderr);
113}
114
Theodore Ts'o981dc562000-07-06 14:13:29 +0000115static void alarm_intr(int alnum)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000116{
117 signal (SIGALRM, alarm_intr);
118 alarm(1);
119 if (!num_blocks)
120 return;
121 fprintf(stderr, "%9ld/%9ld", currently_testing, num_blocks);
122 fprintf(stderr, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
123 fflush (stderr);
124}
125
Theodore Ts'o879ac922000-01-18 20:59:11 +0000126static void *terminate_addr = NULL;
127
Theodore Ts'o981dc562000-07-06 14:13:29 +0000128static void terminate_intr(int signo)
Theodore Ts'o879ac922000-01-18 20:59:11 +0000129{
130 if (terminate_addr)
131 longjmp(terminate_addr,1);
132 exit(1);
133}
134
Theodore Ts'o981dc562000-07-06 14:13:29 +0000135static void capture_terminate(jmp_buf term_addr)
Theodore Ts'o879ac922000-01-18 20:59:11 +0000136{
137 terminate_addr = term_addr;
138 signal (SIGHUP, terminate_intr);
139 signal (SIGINT, terminate_intr);
140 signal (SIGPIPE, terminate_intr);
141 signal (SIGTERM, terminate_intr);
142 signal (SIGUSR1, terminate_intr);
143 signal (SIGUSR2, terminate_intr);
144}
145
Theodore Ts'o8820c792001-01-06 04:20:03 +0000146static void uncapture_terminate(void)
Theodore Ts'o4d003982000-04-03 16:01:11 +0000147{
148 terminate_addr = NULL;
149 signal (SIGHUP, SIG_DFL);
150 signal (SIGINT, SIG_DFL);
151 signal (SIGPIPE, SIG_DFL);
152 signal (SIGTERM, SIG_DFL);
153 signal (SIGUSR1, SIG_DFL);
154 signal (SIGUSR2, SIG_DFL);
155}
156
Theodore Ts'o3839e651997-04-26 13:21:57 +0000157/*
Theodore Ts'o879ac922000-01-18 20:59:11 +0000158 * Perform a read of a sequence of blocks; return the number of blocks
159 * successfully sequentially read.
Theodore Ts'o3839e651997-04-26 13:21:57 +0000160 */
Theodore Ts'odd018f52000-02-06 23:57:07 +0000161static long do_read (int dev, char * buffer, int try, int block_size,
Theodore Ts'o3839e651997-04-26 13:21:57 +0000162 unsigned long current_block)
163{
164 long got;
165
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000166 if (v_flag > 1)
167 print_status();
168
Theodore Ts'o3839e651997-04-26 13:21:57 +0000169 /* Seek to the correct loc. */
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000170 if (ext2fs_llseek (dev, (ext2_loff_t) current_block * block_size,
Theodore Ts'of3db3561997-04-26 13:34:30 +0000171 SEEK_SET) != (ext2_loff_t) current_block * block_size)
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000172 com_err (program_name, errno, _("during seek"));
Theodore Ts'o3839e651997-04-26 13:21:57 +0000173
174 /* Try the read */
175 got = read (dev, buffer, try * block_size);
176 if (got < 0)
177 got = 0;
Theodore Ts'o9f10a7b1999-07-16 10:41:36 +0000178 if (got & 511)
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000179 fprintf(stderr, _("Weird value (%ld) in do_read\n"), got);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000180 got /= block_size;
181 return got;
182}
183
Theodore Ts'o879ac922000-01-18 20:59:11 +0000184/*
185 * Perform a write of a sequence of blocks; return the number of blocks
186 * successfully sequentially written.
187 */
Theodore Ts'odd018f52000-02-06 23:57:07 +0000188static long do_write (int dev, char * buffer, int try, int block_size,
Theodore Ts'o879ac922000-01-18 20:59:11 +0000189 unsigned long current_block)
190{
191 long got;
192
193 if (v_flag > 1)
194 print_status();
195
196 /* Seek to the correct loc. */
197 if (ext2fs_llseek (dev, (ext2_loff_t) current_block * block_size,
198 SEEK_SET) != (ext2_loff_t) current_block * block_size)
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000199 com_err (program_name, errno, _("during seek"));
Theodore Ts'o879ac922000-01-18 20:59:11 +0000200
201 /* Try the write */
202 got = write (dev, buffer, try * block_size);
203 if (got < 0)
204 got = 0;
205 if (got & 511)
206 fprintf (stderr,
207 "Weird value (%ld) in do_write\n", got);
208 got /= block_size;
209 return got;
210}
211
212static int host_dev;
213
Theodore Ts'o4d404542001-01-11 16:04:59 +0000214static void flush_bufs(void)
Theodore Ts'oa418d3a1997-04-26 14:00:26 +0000215{
Theodore Ts'o4d404542001-01-11 16:04:59 +0000216 errcode_t retval;
Theodore Ts'oa418d3a1997-04-26 14:00:26 +0000217
Theodore Ts'o4d404542001-01-11 16:04:59 +0000218 retval = ext2fs_sync_device(host_dev, 1);
219 if (retval)
220 com_err(program_name, retval, _("during ext2fs_sync_device"));
Theodore Ts'oa418d3a1997-04-26 14:00:26 +0000221}
222
Theodore Ts'ocd130a02001-05-05 05:43:23 +0000223static unsigned int test_ro (int dev, unsigned long last_block,
Theodore Ts'odd018f52000-02-06 23:57:07 +0000224 int block_size, unsigned long from_count,
225 unsigned long blocks_at_once)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000226{
Theodore Ts'o3839e651997-04-26 13:21:57 +0000227 char * blkbuf;
228 int try;
229 long got;
Theodore Ts'o879ac922000-01-18 20:59:11 +0000230 unsigned int bb_count = 0;
231 errcode_t errcode;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000232
Theodore Ts'o879ac922000-01-18 20:59:11 +0000233 errcode = ext2fs_badblocks_list_iterate_begin(bb_list,&bb_iter);
234 if (errcode) {
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000235 com_err (program_name, errcode,
236 _("while beginning bad block list iteration"));
Theodore Ts'o879ac922000-01-18 20:59:11 +0000237 exit (1);
238 }
239 do {
240 ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
241 } while (next_bad && next_bad < from_count);
242
243 blkbuf = malloc (blocks_at_once * block_size);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000244 if (!blkbuf)
245 {
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000246 com_err (program_name, ENOMEM, _("while allocating buffers"));
Theodore Ts'o3839e651997-04-26 13:21:57 +0000247 exit (1);
248 }
Theodore Ts'o4d404542001-01-11 16:04:59 +0000249 flush_bufs();
Theodore Ts'of3db3561997-04-26 13:34:30 +0000250 if (v_flag) {
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000251 fprintf(stderr, _("Checking for bad blocks in read-only mode\n"));
252 fprintf (stderr, _("From block %lu to %lu\n"), from_count,
Theodore Ts'ocd130a02001-05-05 05:43:23 +0000253 last_block);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000254 }
Theodore Ts'o879ac922000-01-18 20:59:11 +0000255 try = blocks_at_once;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000256 currently_testing = from_count;
Theodore Ts'ocd130a02001-05-05 05:43:23 +0000257 num_blocks = last_block;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000258 if (s_flag || v_flag > 1) {
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000259 fprintf(stderr,
260 _("Checking for bad blocks (read-only test): "));
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000261 if (v_flag <= 1)
262 alarm_intr(SIGALRM);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000263 }
Theodore Ts'ocd130a02001-05-05 05:43:23 +0000264 while (currently_testing < last_block)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000265 {
Theodore Ts'o879ac922000-01-18 20:59:11 +0000266 if (next_bad) {
267 if (currently_testing == next_bad) {
268 /* fprintf (out, "%lu\n", nextbad); */
269 ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
270 currently_testing++;
271 continue;
272 }
273 else if (currently_testing + try > next_bad)
274 try = next_bad - currently_testing;
275 }
Theodore Ts'ocd130a02001-05-05 05:43:23 +0000276 if (currently_testing + try > last_block)
277 try = last_block - currently_testing;
Theodore Ts'o879ac922000-01-18 20:59:11 +0000278 got = do_read (dev, blkbuf, try, block_size, currently_testing);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000279 currently_testing += got;
280 if (got == try) {
Theodore Ts'o879ac922000-01-18 20:59:11 +0000281 try = blocks_at_once;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000282 continue;
283 }
284 else
285 try = 1;
Theodore Ts'o879ac922000-01-18 20:59:11 +0000286 if (got == 0) {
Theodore Ts'odd018f52000-02-06 23:57:07 +0000287 bb_count += bb_output(currently_testing++);
Theodore Ts'o879ac922000-01-18 20:59:11 +0000288 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000289 }
290 num_blocks = 0;
291 alarm(0);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000292 if (s_flag || v_flag > 1)
Theodore Ts'o8f5c0f62000-02-08 20:29:44 +0000293 fprintf(stderr, _(done_string));
Theodore Ts'o879ac922000-01-18 20:59:11 +0000294
Theodore Ts'of3db3561997-04-26 13:34:30 +0000295 fflush (stderr);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000296 free (blkbuf);
Theodore Ts'o879ac922000-01-18 20:59:11 +0000297
298 ext2fs_badblocks_list_iterate_end(bb_iter);
299
300 return bb_count;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000301}
302
Theodore Ts'ocd130a02001-05-05 05:43:23 +0000303static unsigned int test_rw (int dev, unsigned long last_block,
Theodore Ts'odd018f52000-02-06 23:57:07 +0000304 int block_size, unsigned long from_count,
305 unsigned long blocks_at_once)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000306{
307 int i;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000308 char * buffer;
Theodore Ts'o8820c792001-01-06 04:20:03 +0000309 static unsigned char pattern[] = {0xaa, 0x55, 0xff, 0x00};
Theodore Ts'o879ac922000-01-18 20:59:11 +0000310 unsigned int bb_count = 0;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000311
312 buffer = malloc (2 * block_size);
313 if (!buffer)
314 {
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000315 com_err (program_name, ENOMEM, _("while allocating buffers"));
Theodore Ts'o3839e651997-04-26 13:21:57 +0000316 exit (1);
317 }
318
Theodore Ts'o4d404542001-01-11 16:04:59 +0000319 flush_bufs();
Theodore Ts'oa418d3a1997-04-26 14:00:26 +0000320
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000321 if (v_flag) {
322 fprintf(stderr,
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000323 _("Checking for bad blocks in read-write mode\n"));
324 fprintf(stderr, _("From block %lu to %lu\n"),
Theodore Ts'ocd130a02001-05-05 05:43:23 +0000325 from_count, last_block);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000326 }
327 for (i = 0; i < sizeof (pattern); i++) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000328 memset (buffer, pattern[i], block_size);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000329 if (s_flag | v_flag)
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000330 fprintf (stderr, _("Writing pattern 0x%08x: "),
Theodore Ts'o3839e651997-04-26 13:21:57 +0000331 *((int *) buffer));
Theodore Ts'ocd130a02001-05-05 05:43:23 +0000332 num_blocks = last_block;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000333 currently_testing = from_count;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000334 if (s_flag && v_flag <= 1)
Theodore Ts'of3db3561997-04-26 13:34:30 +0000335 alarm_intr(SIGALRM);
336 for (;
Theodore Ts'ocd130a02001-05-05 05:43:23 +0000337 currently_testing < last_block;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000338 currently_testing++)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000339 {
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000340 if (ext2fs_llseek (dev, (ext2_loff_t) currently_testing *
Theodore Ts'of3db3561997-04-26 13:34:30 +0000341 block_size, SEEK_SET) !=
342 (ext2_loff_t) currently_testing * block_size)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000343 com_err (program_name, errno,
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000344 _("during seek on block %d"),
Theodore Ts'of3db3561997-04-26 13:34:30 +0000345 currently_testing);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000346 if (v_flag > 1)
347 print_status();
Theodore Ts'o3839e651997-04-26 13:21:57 +0000348 write (dev, buffer, block_size);
349 }
Theodore Ts'of3db3561997-04-26 13:34:30 +0000350 num_blocks = 0;
351 alarm (0);
352 if (s_flag | v_flag)
Theodore Ts'o8f5c0f62000-02-08 20:29:44 +0000353 fprintf(stderr, _(done_string));
Theodore Ts'o4d404542001-01-11 16:04:59 +0000354 flush_bufs();
Theodore Ts'of3db3561997-04-26 13:34:30 +0000355 if (s_flag | v_flag)
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000356 fprintf (stderr, _("Reading and comparing: "));
Theodore Ts'ocd130a02001-05-05 05:43:23 +0000357 num_blocks = last_block;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000358 currently_testing = from_count;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000359 if (s_flag && v_flag <= 1)
Theodore Ts'of3db3561997-04-26 13:34:30 +0000360 alarm_intr(SIGALRM);
361 for (;
Theodore Ts'ocd130a02001-05-05 05:43:23 +0000362 currently_testing < last_block;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000363 currently_testing++)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000364 {
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000365 if (ext2fs_llseek (dev, (ext2_loff_t) currently_testing *
Theodore Ts'of3db3561997-04-26 13:34:30 +0000366 block_size, SEEK_SET) !=
367 (ext2_loff_t) currently_testing * block_size)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000368 com_err (program_name, errno,
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000369 _("during seek on block %d"),
Theodore Ts'of3db3561997-04-26 13:34:30 +0000370 currently_testing);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000371 if (v_flag > 1)
372 print_status();
Theodore Ts'odd018f52000-02-06 23:57:07 +0000373 if ((read (dev, buffer + block_size, block_size)
Theodore Ts'o4d003982000-04-03 16:01:11 +0000374 != block_size) ||
Theodore Ts'odd018f52000-02-06 23:57:07 +0000375 memcmp(buffer, buffer + block_size, block_size))
Theodore Ts'o4d003982000-04-03 16:01:11 +0000376 bb_count += bb_output(currently_testing);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000377 }
Theodore Ts'of3db3561997-04-26 13:34:30 +0000378 num_blocks = 0;
379 alarm (0);
380 if (s_flag | v_flag)
Theodore Ts'o8f5c0f62000-02-08 20:29:44 +0000381 fprintf(stderr, _(done_string));
Theodore Ts'o4d404542001-01-11 16:04:59 +0000382 flush_bufs();
Theodore Ts'o3839e651997-04-26 13:21:57 +0000383 }
Theodore Ts'o879ac922000-01-18 20:59:11 +0000384
385 return bb_count;
386}
387
Theodore Ts'od49a22b2000-07-06 00:31:27 +0000388struct saved_blk_record {
389 blk_t block;
390 int num;
391};
392
Theodore Ts'ocd130a02001-05-05 05:43:23 +0000393static unsigned int test_nd (int dev, unsigned long last_block,
Theodore Ts'odd018f52000-02-06 23:57:07 +0000394 int block_size, unsigned long from_count,
395 unsigned long blocks_at_once)
Theodore Ts'o879ac922000-01-18 20:59:11 +0000396{
Theodore Ts'odd018f52000-02-06 23:57:07 +0000397 char *blkbuf, *save_ptr, *test_ptr, *read_ptr;
Theodore Ts'o879ac922000-01-18 20:59:11 +0000398 char * ptr;
Theodore Ts'odd018f52000-02-06 23:57:07 +0000399 int try, i;
Theodore Ts'oa551b782000-07-13 22:05:31 +0000400 long got, used2, written, save_currently_testing;
Theodore Ts'od49a22b2000-07-06 00:31:27 +0000401 struct saved_blk_record *test_record;
Theodore Ts'oa551b782000-07-13 22:05:31 +0000402 /* This is static to prevent being clobbered by the longjmp */
403 static int num_saved;
Theodore Ts'o879ac922000-01-18 20:59:11 +0000404 jmp_buf terminate_env;
Theodore Ts'o879ac922000-01-18 20:59:11 +0000405 errcode_t errcode;
Theodore Ts'oa551b782000-07-13 22:05:31 +0000406 long buf_used;
407 unsigned int bb_count;
Theodore Ts'o879ac922000-01-18 20:59:11 +0000408
409 errcode = ext2fs_badblocks_list_iterate_begin(bb_list,&bb_iter);
410 if (errcode) {
Theodore Ts'odd018f52000-02-06 23:57:07 +0000411 com_err (program_name, errcode,
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000412 _("while beginning bad block list iteration"));
Theodore Ts'o879ac922000-01-18 20:59:11 +0000413 exit (1);
414 }
415 do {
416 ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
417 } while (next_bad && next_bad < from_count);
418
419 blkbuf = malloc (3 * blocks_at_once * block_size);
Theodore Ts'od49a22b2000-07-06 00:31:27 +0000420 test_record = malloc (blocks_at_once*sizeof(struct saved_blk_record));
421 if (!blkbuf || !test_record) {
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000422 com_err(program_name, ENOMEM, _("while allocating buffers"));
Theodore Ts'o879ac922000-01-18 20:59:11 +0000423 exit (1);
424 }
Theodore Ts'od49a22b2000-07-06 00:31:27 +0000425 num_saved = 0;
Theodore Ts'o879ac922000-01-18 20:59:11 +0000426
427 /* inititalize the test data randomly: */
428 if (v_flag) {
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000429 fprintf (stderr, _("Initializing random test data\n"));
Theodore Ts'o879ac922000-01-18 20:59:11 +0000430 }
431 for(ptr = blkbuf + blocks_at_once * block_size;
Theodore Ts'odd018f52000-02-06 23:57:07 +0000432 ptr < blkbuf + 2 * blocks_at_once * block_size;
433 ++ptr) {
Theodore Ts'o879ac922000-01-18 20:59:11 +0000434 (*ptr) = random() % (1 << sizeof(char));
435 }
436
Theodore Ts'o4d404542001-01-11 16:04:59 +0000437 flush_bufs();
Theodore Ts'o879ac922000-01-18 20:59:11 +0000438 if (v_flag) {
439 fprintf (stderr,
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000440 _("Checking for bad blocks in non-destructive read-write mode\n"));
Theodore Ts'ocd130a02001-05-05 05:43:23 +0000441 fprintf (stderr, _("From block %lu to %lu\n"), from_count, last_block);
Theodore Ts'o879ac922000-01-18 20:59:11 +0000442 }
Theodore Ts'o879ac922000-01-18 20:59:11 +0000443 if (s_flag || v_flag > 1) {
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000444 fprintf(stderr, _("Checking for bad blocks (non-destructive read-write test): "));
Theodore Ts'o879ac922000-01-18 20:59:11 +0000445 if (v_flag <= 1)
446 alarm_intr(SIGALRM);
447 }
Theodore Ts'o4d003982000-04-03 16:01:11 +0000448 if (setjmp(terminate_env)) {
449 /*
450 * Abnormal termination by a signal is handled here.
Theodore Ts'o4d003982000-04-03 16:01:11 +0000451 */
Theodore Ts'oa551b782000-07-13 22:05:31 +0000452 signal (SIGALRM, SIG_IGN);
453 fprintf(stderr, _("\nInterrupt caught, cleaning up\n"));
Theodore Ts'o879ac922000-01-18 20:59:11 +0000454
Theodore Ts'od49a22b2000-07-06 00:31:27 +0000455 save_ptr = blkbuf;
456 for (i=0; i < num_saved; i++) {
457 do_write(dev, save_ptr, test_record[i].num,
458 block_size, test_record[i].block);
459 save_ptr += test_record[i].num * block_size;
460 }
Theodore Ts'o879ac922000-01-18 20:59:11 +0000461 fflush (out);
Theodore Ts'odd018f52000-02-06 23:57:07 +0000462 exit(1);
Theodore Ts'o879ac922000-01-18 20:59:11 +0000463 }
Theodore Ts'o4d003982000-04-03 16:01:11 +0000464
465 /* set up abend handler */
466 capture_terminate(terminate_env);
467
Theodore Ts'od49a22b2000-07-06 00:31:27 +0000468 buf_used = 0;
Theodore Ts'oa551b782000-07-13 22:05:31 +0000469 bb_count = 0;
Theodore Ts'od49a22b2000-07-06 00:31:27 +0000470 save_ptr = blkbuf;
Theodore Ts'o4d003982000-04-03 16:01:11 +0000471 test_ptr = blkbuf + (blocks_at_once * block_size);
472 currently_testing = from_count;
Theodore Ts'ocd130a02001-05-05 05:43:23 +0000473 num_blocks = last_block;
Theodore Ts'o4d003982000-04-03 16:01:11 +0000474
Theodore Ts'ocd130a02001-05-05 05:43:23 +0000475 while (currently_testing < last_block) {
Theodore Ts'o4d003982000-04-03 16:01:11 +0000476 try = blocks_at_once - buf_used;
477 if (next_bad) {
478 if (currently_testing == next_bad) {
479 /* fprintf (out, "%lu\n", nextbad); */
480 ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
Theodore Ts'od49a22b2000-07-06 00:31:27 +0000481 currently_testing++;
482 goto check_for_more;
Theodore Ts'o4d003982000-04-03 16:01:11 +0000483 }
484 else if (currently_testing + try > next_bad)
485 try = next_bad - currently_testing;
486 }
Theodore Ts'ocd130a02001-05-05 05:43:23 +0000487 if (currently_testing + try > last_block)
488 try = last_block - currently_testing;
Theodore Ts'o4d003982000-04-03 16:01:11 +0000489 got = do_read (dev, save_ptr, try, block_size,
490 currently_testing);
Theodore Ts'od49a22b2000-07-06 00:31:27 +0000491 if (got == 0) {
492 /* First block must have been bad. */
493 bb_count += bb_output(currently_testing++);
494 goto check_for_more;
Theodore Ts'o4d003982000-04-03 16:01:11 +0000495 }
496
Theodore Ts'od49a22b2000-07-06 00:31:27 +0000497 /*
498 * Note the fact that we've saved this much data
499 * *before* we overwrite it with test data
500 */
501 test_record[num_saved].block = currently_testing;
502 test_record[num_saved].num = got;
503 num_saved++;
504
505 /* Write the test data */
506 written = do_write (dev, test_ptr, got, block_size,
507 currently_testing);
508 if (written != got)
509 com_err (program_name, errno,
510 _("during test data write, block %lu"),
511 currently_testing + written);
512
Theodore Ts'o4d003982000-04-03 16:01:11 +0000513 buf_used += got;
514 save_ptr += got * block_size;
515 test_ptr += got * block_size;
516 currently_testing += got;
517 if (got != try)
518 bb_count += bb_output(currently_testing++);
519
Theodore Ts'od49a22b2000-07-06 00:31:27 +0000520 check_for_more:
Theodore Ts'o4d003982000-04-03 16:01:11 +0000521 /*
522 * If there's room for more blocks to be tested this
523 * around, and we're not done yet testing the disk, go
524 * back and get some more blocks.
525 */
526 if ((buf_used != blocks_at_once) &&
Theodore Ts'ocd130a02001-05-05 05:43:23 +0000527 (currently_testing < last_block))
Theodore Ts'o4d003982000-04-03 16:01:11 +0000528 continue;
529
Theodore Ts'o4d404542001-01-11 16:04:59 +0000530 flush_bufs();
Theodore Ts'oa551b782000-07-13 22:05:31 +0000531 save_currently_testing = currently_testing;
Theodore Ts'o4d003982000-04-03 16:01:11 +0000532
533 /*
534 * for each contiguous block that we read into the
535 * buffer (and wrote test data into afterwards), read
536 * it back (looping if necessary, to get past newly
537 * discovered unreadable blocks, of which there should
538 * be none, but with a hard drive which is unreliable,
539 * it has happened), and compare with the test data
540 * that was written; output to the bad block list if
541 * it doesn't match.
542 */
543 used2 = 0;
544 save_ptr = blkbuf;
545 test_ptr = blkbuf + (blocks_at_once * block_size);
546 read_ptr = blkbuf + (2 * blocks_at_once * block_size);
Theodore Ts'od49a22b2000-07-06 00:31:27 +0000547 try = 0;
Theodore Ts'o4d003982000-04-03 16:01:11 +0000548
Theodore Ts'od49a22b2000-07-06 00:31:27 +0000549 while (1) {
550 if (try == 0) {
551 if (used2 >= num_saved)
552 break;
553 currently_testing = test_record[used2].block;
554 try = test_record[used2].num;
555 used2++;
556 }
557
Theodore Ts'o4d003982000-04-03 16:01:11 +0000558 got = do_read (dev, read_ptr, try,
559 block_size, currently_testing);
560
561 /* test the comparison between all the
562 blocks successfully read */
563 for (i = 0; i < got; ++i)
564 if (memcmp (test_ptr+i*block_size,
565 read_ptr+i*block_size, block_size))
566 bb_count += bb_output(currently_testing + i);
567 if (got < try) {
568 bb_count += bb_output(currently_testing + got);
569 got++;
570 }
571
572 /* when done, write back original data */
573 do_write (dev, save_ptr, got, block_size,
574 currently_testing);
575
576 currently_testing += got;
577 save_ptr += got * block_size;
578 test_ptr += got * block_size;
579 read_ptr += got * block_size;
580 try -= got;
Theodore Ts'o4d003982000-04-03 16:01:11 +0000581 }
582
583 /* empty the buffer so it can be reused */
Theodore Ts'od49a22b2000-07-06 00:31:27 +0000584 num_saved = 0;
Theodore Ts'o4d003982000-04-03 16:01:11 +0000585 buf_used = 0;
Theodore Ts'od49a22b2000-07-06 00:31:27 +0000586 save_ptr = blkbuf;
587 test_ptr = blkbuf + (blocks_at_once * block_size);
Theodore Ts'oa551b782000-07-13 22:05:31 +0000588 currently_testing = save_currently_testing;
Theodore Ts'o4d003982000-04-03 16:01:11 +0000589 }
590 num_blocks = 0;
591 alarm(0);
592 uncapture_terminate();
593 if (s_flag || v_flag > 1)
594 fprintf(stderr, _(done_string));
Theodore Ts'o879ac922000-01-18 20:59:11 +0000595
Theodore Ts'odd018f52000-02-06 23:57:07 +0000596 fflush(stderr);
597 free(blkbuf);
Theodore Ts'od49a22b2000-07-06 00:31:27 +0000598 free(test_record);
Theodore Ts'o879ac922000-01-18 20:59:11 +0000599
600 ext2fs_badblocks_list_iterate_end(bb_iter);
601
602 return bb_count;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000603}
604
Theodore Ts'o981dc562000-07-06 14:13:29 +0000605static void check_mount(char *device_name)
606{
607 errcode_t retval;
608 int mount_flags;
609
610 retval = ext2fs_check_if_mounted(device_name, &mount_flags);
611 if (retval) {
612 com_err("ext2fs_check_if_mount", retval,
613 _("while determining whether %s is mounted."),
614 device_name);
615 return;
616 }
617 if (!(mount_flags & EXT2_MF_MOUNTED))
618 return;
619
620 fprintf(stderr, _("%s is mounted; "), device_name);
621 if (force) {
622 fprintf(stderr, _("badblocks forced anyway. "
623 "Hope /etc/mtab is incorrect.\n"));
624 return;
625 }
626 fprintf(stderr, _("it's not safe to run badblocks!\n"));
627 exit(1);
628}
629
630
Theodore Ts'o00e54331997-09-16 02:13:52 +0000631int main (int argc, char ** argv)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000632{
Theodore Ts'o519149f1997-10-25 03:49:49 +0000633 int c;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000634 char * tmp;
635 char * device_name;
Theodore Ts'o879ac922000-01-18 20:59:11 +0000636 char * host_device_name = NULL;
637 char * input_file = NULL;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000638 char * output_file = NULL;
Theodore Ts'o879ac922000-01-18 20:59:11 +0000639 FILE * in = NULL;
Theodore Ts'odd018f52000-02-06 23:57:07 +0000640 int block_size = 1024;
Theodore Ts'o879ac922000-01-18 20:59:11 +0000641 unsigned long blocks_at_once = 16;
Theodore Ts'ocd130a02001-05-05 05:43:23 +0000642 blk_t last_block, from_count;
Theodore Ts'o879ac922000-01-18 20:59:11 +0000643 int num_passes = 0;
644 int passes_clean = 0;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000645 int dev;
Theodore Ts'o879ac922000-01-18 20:59:11 +0000646 errcode_t errcode;
Theodore Ts'o8820c792001-01-06 04:20:03 +0000647 unsigned int (*test_func)(int, unsigned long,
648 int, unsigned long,
649 unsigned long);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000650
651 setbuf(stdout, NULL);
652 setbuf(stderr, NULL);
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000653#ifdef ENABLE_NLS
654 setlocale(LC_MESSAGES, "");
Theodore Ts'o14308a52002-03-05 03:26:52 -0500655 setlocale(LC_CTYPE, "");
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000656 bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
657 textdomain(NLS_CAT_NAME);
658#endif
Theodore Ts'o4d003982000-04-03 16:01:11 +0000659 test_func = test_ro;
Theodore Ts'o4d404542001-01-11 16:04:59 +0000660
Theodore Ts'o3839e651997-04-26 13:21:57 +0000661 if (argc && *argv)
662 program_name = *argv;
Theodore Ts'oa551b782000-07-13 22:05:31 +0000663 while ((c = getopt (argc, argv, "b:fi:o:svwnc:p:h:")) != EOF) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000664 switch (c) {
665 case 'b':
666 block_size = strtoul (optarg, &tmp, 0);
667 if (*tmp || block_size > 4096) {
668 com_err (program_name, 0,
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000669 _("bad block size - %s"), optarg);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000670 exit (1);
671 }
672 break;
Theodore Ts'o981dc562000-07-06 14:13:29 +0000673 case 'f':
674 force++;
675 break;
Theodore Ts'o879ac922000-01-18 20:59:11 +0000676 case 'i':
677 input_file = optarg;
678 break;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000679 case 'o':
680 output_file = optarg;
681 break;
682 case 's':
683 s_flag = 1;
684 break;
685 case 'v':
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000686 v_flag++;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000687 break;
688 case 'w':
Theodore Ts'o4d003982000-04-03 16:01:11 +0000689 if (w_flag)
690 usage();
691 test_func = test_rw;
692 w_flag = 1;
Theodore Ts'o879ac922000-01-18 20:59:11 +0000693 break;
694 case 'n':
Theodore Ts'o4d003982000-04-03 16:01:11 +0000695 if (w_flag)
696 usage();
697 test_func = test_nd;
Theodore Ts'o879ac922000-01-18 20:59:11 +0000698 w_flag = 2;
699 break;
700 case 'c':
701 blocks_at_once = strtoul (optarg, &tmp, 0);
702 if (*tmp) {
703 com_err (program_name, 0,
704 "bad simultaneous block count - %s", optarg);
705 exit (1);
706 }
707 break;
708 case 'p':
709 num_passes = strtoul (optarg, &tmp, 0);
710 if (*tmp) {
711 com_err (program_name, 0,
712 "bad number of clean passes - %s", optarg);
713 exit (1);
714 }
715 break;
716 case 'h':
717 host_device_name = optarg;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000718 break;
719 default:
Theodore Ts'o818180c1998-06-27 05:11:14 +0000720 usage();
Theodore Ts'o3839e651997-04-26 13:21:57 +0000721 }
722 }
723 if (optind > argc - 1)
Theodore Ts'o818180c1998-06-27 05:11:14 +0000724 usage();
Theodore Ts'o3839e651997-04-26 13:21:57 +0000725 device_name = argv[optind++];
Theodore Ts'o35964b52000-07-06 13:19:43 +0000726 if (optind > argc - 1) {
727 errcode = ext2fs_get_device_size(device_name,
728 block_size,
Theodore Ts'ocd130a02001-05-05 05:43:23 +0000729 &last_block);
Theodore Ts'o35964b52000-07-06 13:19:43 +0000730 if (errcode == EXT2_ET_UNIMPLEMENTED) {
731 com_err(program_name, 0,
732 _("Couldn't determine device size; you "
733 "must specify\nthe size manually\n"));
734 exit(1);
735 }
736 if (errcode) {
737 com_err(program_name, errcode,
738 _("while trying to determine device size"));
739 exit(1);
740 }
741 } else {
Theodore Ts'ocd130a02001-05-05 05:43:23 +0000742 last_block = strtoul (argv[optind], &tmp, 0);
Theodore Ts'o35964b52000-07-06 13:19:43 +0000743 if (*tmp) {
744 com_err (program_name, 0, _("bad blocks count - %s"),
745 argv[optind]);
746 exit (1);
747 }
748 optind++;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000749 }
Theodore Ts'o35964b52000-07-06 13:19:43 +0000750 if (optind <= argc-1) {
Theodore Ts'of3db3561997-04-26 13:34:30 +0000751 from_count = strtoul (argv[optind], &tmp, 0);
Theodore Ts'oa551b782000-07-13 22:05:31 +0000752 if (*tmp) {
753 com_err (program_name, 0, _("bad starting block - %s"),
754 argv[optind]);
755 exit (1);
756 }
Theodore Ts'of3db3561997-04-26 13:34:30 +0000757 } else from_count = 0;
Theodore Ts'ocd130a02001-05-05 05:43:23 +0000758 if (from_count >= last_block) {
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000759 com_err (program_name, 0, _("bad blocks range: %lu-%lu"),
Theodore Ts'ocd130a02001-05-05 05:43:23 +0000760 from_count, last_block);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000761 exit (1);
762 }
Theodore Ts'o981dc562000-07-06 14:13:29 +0000763 if (w_flag)
764 check_mount(device_name);
765
Theodore Ts'o5493a272002-01-02 14:15:35 -0500766 dev = open (device_name, O_RDWR);
Theodore Ts'o41ec7bb2002-09-29 22:37:40 -0400767 if ((dev == -1) && ((errno == EPERM) || (errno == EACCES) ||
768 (errno == EROFS)) &&
Theodore Ts'o5493a272002-01-02 14:15:35 -0500769 (w_flag == 0))
770 dev = open(device_name, O_RDONLY);
771 if (dev == -1) {
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000772 com_err (program_name, errno, _("while trying to open %s"),
Theodore Ts'o3839e651997-04-26 13:21:57 +0000773 device_name);
774 exit (1);
775 }
Theodore Ts'o879ac922000-01-18 20:59:11 +0000776 if (host_device_name) {
Theodore Ts'o5493a272002-01-02 14:15:35 -0500777 host_dev = open (host_device_name, O_RDWR);
778 if ((host_dev == -1) &&
779 ((errno == EPERM) || (errno == EACCES)))
780 host_dev = open(host_device_name, O_RDONLY);
781 if (host_dev == -1) {
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000782 com_err (program_name, errno,
783 _("while trying to open %s"),
784 host_device_name);
Theodore Ts'o879ac922000-01-18 20:59:11 +0000785 exit (1);
786 }
787 } else
788 host_dev = dev;
Theodore Ts'o3e699062002-10-13 23:56:28 -0400789 if (input_file) {
Theodore Ts'o879ac922000-01-18 20:59:11 +0000790 if (strcmp (input_file, "-") == 0)
791 in = stdin;
792 else {
793 in = fopen (input_file, "r");
794 if (in == NULL)
795 {
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000796 com_err (program_name, errno,
797 _("while trying to open %s"),
Theodore Ts'o879ac922000-01-18 20:59:11 +0000798 input_file);
799 exit (1);
800 }
801 }
Theodore Ts'o3e699062002-10-13 23:56:28 -0400802 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000803 if (output_file && strcmp (output_file, "-") != 0)
804 {
805 out = fopen (output_file, "w");
806 if (out == NULL)
807 {
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000808 com_err (program_name, errno,
809 _("while trying to open %s"),
Theodore Ts'o879ac922000-01-18 20:59:11 +0000810 output_file);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000811 exit (1);
812 }
813 }
814 else
815 out = stdout;
Theodore Ts'o879ac922000-01-18 20:59:11 +0000816
817 errcode = ext2fs_badblocks_list_create(&bb_list,0);
818 if (errcode) {
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000819 com_err (program_name, errcode,
820 _("creating in-memory bad blocks list"));
Theodore Ts'o879ac922000-01-18 20:59:11 +0000821 exit (1);
822 }
823
824 if (in) {
825 for(;;) {
Theodore Ts'oa551b782000-07-13 22:05:31 +0000826 switch(fscanf (in, "%u\n", &next_bad)) {
Theodore Ts'o879ac922000-01-18 20:59:11 +0000827 case 0:
828 com_err (program_name, 0, "input file - bad format");
829 exit (1);
830 case EOF:
831 break;
832 default:
833 errcode = ext2fs_badblocks_list_add(bb_list,next_bad);
834 if (errcode) {
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000835 com_err (program_name, errcode, _("adding to in-memory bad block list"));
Theodore Ts'o879ac922000-01-18 20:59:11 +0000836 exit (1);
837 }
838 continue;
839 }
840 break;
841 }
842
843 if (in != stdin)
844 fclose (in);
845 }
846
847 do {
848 unsigned int bb_count;
849
Theodore Ts'ocd130a02001-05-05 05:43:23 +0000850 bb_count = test_func(dev, last_block, block_size,
Theodore Ts'o4d003982000-04-03 16:01:11 +0000851 from_count, blocks_at_once);
852 if (bb_count)
853 passes_clean = 0;
854 else
855 ++passes_clean;
856
Theodore Ts'o879ac922000-01-18 20:59:11 +0000857 if (v_flag)
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000858 fprintf(stderr,
859 _("Pass completed, %u bad blocks found.\n"),
860 bb_count);
Theodore Ts'o879ac922000-01-18 20:59:11 +0000861
862 } while (passes_clean < num_passes);
863
Theodore Ts'o3839e651997-04-26 13:21:57 +0000864 close (dev);
865 if (out != stdout)
866 fclose (out);
Theodore Ts'o879ac922000-01-18 20:59:11 +0000867 return 0;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000868}
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000869