blob: ef6ab5b252ec4a798e899235558a4772396e02e1 [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
Theodore Ts'o1c29b092003-07-12 16:01:45 -040032#define _GNU_SOURCE /* for O_DIRECT */
33
Theodore Ts'o3839e651997-04-26 13:21:57 +000034#include <errno.h>
35#include <fcntl.h>
Theodore Ts'oa418d3a1997-04-26 14:00:26 +000036#ifdef HAVE_GETOPT_H
Theodore Ts'o3839e651997-04-26 13:21:57 +000037#include <getopt.h>
Theodore Ts'o373b8332000-04-03 16:22:35 +000038#else
39extern char *optarg;
40extern int optind;
Theodore Ts'oa418d3a1997-04-26 14:00:26 +000041#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +000042#include <signal.h>
43#include <stdio.h>
44#include <stdlib.h>
45#include <string.h>
46#include <unistd.h>
Theodore Ts'o879ac922000-01-18 20:59:11 +000047#include <setjmp.h>
Theodore Ts'o6d40f562003-05-07 08:35:38 -040048#include <time.h>
Theodore Ts'o5267a522007-06-04 01:49:51 -040049#include <limits.h>
Theodore Ts'o3839e651997-04-26 13:21:57 +000050
Iustin Popedf261f2008-06-18 22:26:26 +020051#include <sys/time.h>
Theodore Ts'o3839e651997-04-26 13:21:57 +000052#include <sys/ioctl.h>
Theodore Ts'of3db3561997-04-26 13:34:30 +000053#include <sys/types.h>
Eric Sandeen79e62402008-07-06 18:36:56 -040054#include <sys/time.h>
Theodore Ts'o3839e651997-04-26 13:21:57 +000055
Theodore Ts'o3839e651997-04-26 13:21:57 +000056#include "et/com_err.h"
Theodore Ts'od40259f1997-10-20 00:44:26 +000057#include "ext2fs/ext2_io.h"
Theodore Ts'o54c637d2001-05-14 11:45:38 +000058#include "ext2fs/ext2_fs.h"
Theodore Ts'o879ac922000-01-18 20:59:11 +000059#include "ext2fs/ext2fs.h"
Theodore Ts'od9c56d32000-02-08 00:47:55 +000060#include "nls-enable.h"
Theodore Ts'o3839e651997-04-26 13:21:57 +000061
62const char * program_name = "badblocks";
Theodore Ts'of63978a2006-05-13 09:25:47 -040063const char * done_string = N_("done \n");
Theodore Ts'o3839e651997-04-26 13:21:57 +000064
Theodore Ts'o4d003982000-04-03 16:01:11 +000065static int v_flag = 0; /* verbose */
66static int w_flag = 0; /* do r/w test: 0=no, 1=yes,
67 * 2=non-destructive */
68static int s_flag = 0; /* show progress of test */
Theodore Ts'o981dc562000-07-06 14:13:29 +000069static int force = 0; /* force check of mounted device */
Theodore Ts'o849b6bc2003-05-07 09:52:14 -040070static int t_flag = 0; /* number of test patterns */
71static int t_max = 0; /* allocated test patterns */
Theodore Ts'oe9860ae2007-10-22 09:51:50 -040072static unsigned int *t_patts = NULL; /* test patterns */
Theodore Ts'o1c29b092003-07-12 16:01:45 -040073static int current_O_DIRECT = 0; /* Current status of O_DIRECT flag */
Theodore Ts'of63978a2006-05-13 09:25:47 -040074static int exclusive_ok = 0;
Iustin Pop931b0282008-06-11 13:12:17 +020075static unsigned int max_bb = 0; /* Abort test if more than this number of bad blocks has been encountered */
Iustin Pop264f64a2008-06-12 09:30:04 +020076static unsigned int d_flag = 0; /* delay factor between reads */
Theodore Ts'o1c29b092003-07-12 16:01:45 -040077
Theodore Ts'o849b6bc2003-05-07 09:52:14 -040078#define T_INC 32
Theodore Ts'o4d003982000-04-03 16:01:11 +000079
Theodore Ts'oacd77412007-10-22 10:09:05 -040080unsigned int sys_page_size = 4096;
Theodore Ts'o1c29b092003-07-12 16:01:45 -040081
Theodore Ts'o8820c792001-01-06 04:20:03 +000082static void usage(void)
Theodore Ts'o3839e651997-04-26 13:21:57 +000083{
Benno Schulenbergad39bcd2008-07-17 23:32:02 +020084 fprintf(stderr, _(
85"Usage: %s [-b block_size] [-i input_file] [-o output_file] [-svwnf]\n"
86" [-c blocks_at_once] [-d delay_factor_between_reads] [-e max_bad_blocks]\n"
87" [-p num_passes] [-t test_pattern [-t test_pattern [...]]]\n"
88" device [last_block [first_block]]\n"),
Theodore Ts'o3839e651997-04-26 13:21:57 +000089 program_name);
90 exit (1);
91}
92
Theodore Ts'od8b5f772006-11-12 23:09:03 -050093static void exclusive_usage(void)
94{
95 fprintf(stderr,
Theodore Ts'o017a76e2006-11-17 23:00:19 -050096 _("%s: The -n and -w options are mutually exclusive.\n\n"),
97 program_name);
98 exit(1);
Theodore Ts'od8b5f772006-11-12 23:09:03 -050099}
100
Theodore Ts'oacd77412007-10-22 10:09:05 -0400101static blk_t currently_testing = 0;
102static blk_t num_blocks = 0;
Theodore Ts'o879ac922000-01-18 20:59:11 +0000103static ext2_badblocks_list bb_list = NULL;
104static FILE *out;
105static blk_t next_bad = 0;
106static ext2_badblocks_iterate bb_iter = NULL;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000107
Theodore Ts'o1c29b092003-07-12 16:01:45 -0400108static void *allocate_buffer(size_t size)
109{
110 void *ret = 0;
111
112#ifdef HAVE_POSIX_MEMALIGN
113 if (posix_memalign(&ret, sys_page_size, size) < 0)
114 ret = 0;
115#else
116#ifdef HAVE_MEMALIGN
117 ret = memalign(sys_page_size, size);
118#else
119#ifdef HAVE_VALLOC
120 ret = valloc(size);
121#endif /* HAVE_VALLOC */
122#endif /* HAVE_MEMALIGN */
123#endif /* HAVE_POSIX_MEMALIGN */
124
125 if (!ret)
126 ret = malloc(size);
127
128 return ret;
129}
130
Theodore Ts'odd018f52000-02-06 23:57:07 +0000131/*
132 * This routine reports a new bad block. If the bad block has already
133 * been seen before, then it returns 0; otherwise it returns 1.
134 */
Theodore Ts'oacd77412007-10-22 10:09:05 -0400135static int bb_output (blk_t bad)
Theodore Ts'o879ac922000-01-18 20:59:11 +0000136{
137 errcode_t errcode;
138
Theodore Ts'odd018f52000-02-06 23:57:07 +0000139 if (ext2fs_badblocks_list_test(bb_list, bad))
140 return 0;
141
Theodore Ts'oacd77412007-10-22 10:09:05 -0400142 fprintf(out, "%lu\n", (unsigned long) bad);
Theodore Ts'occ4f98e2003-04-03 11:37:46 -0500143 fflush(out);
Theodore Ts'o879ac922000-01-18 20:59:11 +0000144
145 errcode = ext2fs_badblocks_list_add (bb_list, bad);
146 if (errcode) {
147 com_err (program_name, errcode, "adding to in-memory bad block list");
148 exit (1);
149 }
150
151 /* kludge:
152 increment the iteration through the bb_list if
153 an element was just added before the current iteration
154 position. This should not cause next_bad to change. */
155 if (bb_iter && bad < next_bad)
156 ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
Theodore Ts'odd018f52000-02-06 23:57:07 +0000157 return 1;
Theodore Ts'o879ac922000-01-18 20:59:11 +0000158}
159
Theodore Ts'o8820c792001-01-06 04:20:03 +0000160static void print_status(void)
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000161{
Theodore Ts'oacd77412007-10-22 10:09:05 -0400162 fprintf(stderr, "%15lu/%15lu", (unsigned long) currently_testing,
163 (unsigned long) num_blocks);
Theodore Ts'oc76564a2005-01-06 14:48:59 -0500164 fputs("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", stderr);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000165 fflush (stderr);
166}
167
Theodore Ts'o54434922003-12-07 01:28:50 -0500168static void alarm_intr(int alnum EXT2FS_ATTR((unused)))
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000169{
170 signal (SIGALRM, alarm_intr);
171 alarm(1);
172 if (!num_blocks)
173 return;
Theodore Ts'oc76564a2005-01-06 14:48:59 -0500174 print_status();
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000175}
176
Theodore Ts'o879ac922000-01-18 20:59:11 +0000177static void *terminate_addr = NULL;
178
Theodore Ts'o54434922003-12-07 01:28:50 -0500179static void terminate_intr(int signo EXT2FS_ATTR((unused)))
Theodore Ts'o879ac922000-01-18 20:59:11 +0000180{
181 if (terminate_addr)
182 longjmp(terminate_addr,1);
183 exit(1);
184}
185
Theodore Ts'o981dc562000-07-06 14:13:29 +0000186static void capture_terminate(jmp_buf term_addr)
Theodore Ts'o879ac922000-01-18 20:59:11 +0000187{
188 terminate_addr = term_addr;
189 signal (SIGHUP, terminate_intr);
190 signal (SIGINT, terminate_intr);
191 signal (SIGPIPE, terminate_intr);
192 signal (SIGTERM, terminate_intr);
193 signal (SIGUSR1, terminate_intr);
194 signal (SIGUSR2, terminate_intr);
195}
196
Theodore Ts'o8820c792001-01-06 04:20:03 +0000197static void uncapture_terminate(void)
Theodore Ts'o4d003982000-04-03 16:01:11 +0000198{
199 terminate_addr = NULL;
200 signal (SIGHUP, SIG_DFL);
201 signal (SIGINT, SIG_DFL);
202 signal (SIGPIPE, SIG_DFL);
203 signal (SIGTERM, SIG_DFL);
204 signal (SIGUSR1, SIG_DFL);
205 signal (SIGUSR2, SIG_DFL);
206}
207
Theodore Ts'o1f9a60c2003-08-01 00:58:00 -0400208static void set_o_direct(int dev, unsigned char *buffer, size_t size,
Theodore Ts'oacd77412007-10-22 10:09:05 -0400209 blk_t current_block)
Theodore Ts'o1c29b092003-07-12 16:01:45 -0400210{
211#ifdef O_DIRECT
212 int new_flag = O_DIRECT;
213 int flag;
214
215 if ((((unsigned long) buffer & (sys_page_size - 1)) != 0) ||
Theodore Ts'o1f9a60c2003-08-01 00:58:00 -0400216 ((size & (sys_page_size - 1)) != 0) ||
217 ((current_block & ((sys_page_size >> 9)-1)) != 0))
Theodore Ts'o1c29b092003-07-12 16:01:45 -0400218 new_flag = 0;
219
220 if (new_flag != current_O_DIRECT) {
Theodore Ts'odc058712003-07-25 07:39:33 -0400221 /* printf("%s O_DIRECT\n", new_flag ? "Setting" : "Clearing"); */
Theodore Ts'o1c29b092003-07-12 16:01:45 -0400222 flag = fcntl(dev, F_GETFL);
223 if (flag > 0) {
224 flag = (flag & ~O_DIRECT) | new_flag;
225 fcntl(dev, F_SETFL, flag);
226 }
227 current_O_DIRECT = new_flag;
228 }
229#endif
230}
231
232
Theodore Ts'oe9860ae2007-10-22 09:51:50 -0400233static void pattern_fill(unsigned char *buffer, unsigned int pattern,
Theodore Ts'o84c05452003-05-18 01:11:52 -0400234 size_t n)
Theodore Ts'o849b6bc2003-05-07 09:52:14 -0400235{
Theodore Ts'o54434922003-12-07 01:28:50 -0500236 unsigned int i, nb;
Theodore Ts'o84c05452003-05-18 01:11:52 -0400237 unsigned char bpattern[sizeof(pattern)], *ptr;
Theodore Ts'o849b6bc2003-05-07 09:52:14 -0400238
Theodore Ts'oe9860ae2007-10-22 09:51:50 -0400239 if (pattern == (unsigned int) ~0) {
Theodore Ts'o849b6bc2003-05-07 09:52:14 -0400240 for (ptr = buffer; ptr < buffer + n; ptr++) {
241 (*ptr) = random() % (1 << (8 * sizeof(char)));
242 }
243 if (s_flag | v_flag)
Theodore Ts'o54434922003-12-07 01:28:50 -0500244 fputs(_("Testing with random pattern: "), stderr);
Theodore Ts'o849b6bc2003-05-07 09:52:14 -0400245 } else {
246 bpattern[0] = 0;
247 for (i = 0; i < sizeof(bpattern); i++) {
248 if (pattern == 0)
249 break;
250 bpattern[i] = pattern & 0xFF;
251 pattern = pattern >> 8;
252 }
253 nb = i ? (i-1) : 0;
254 for (ptr = buffer, i = nb; ptr < buffer + n; ptr++) {
Theodore Ts'o54434922003-12-07 01:28:50 -0500255 *ptr = bpattern[i];
256 if (i == 0)
Theodore Ts'o849b6bc2003-05-07 09:52:14 -0400257 i = nb;
Theodore Ts'o54434922003-12-07 01:28:50 -0500258 else
259 i--;
Theodore Ts'o849b6bc2003-05-07 09:52:14 -0400260 }
Theodore Ts'o84c05452003-05-18 01:11:52 -0400261 if (s_flag | v_flag) {
Theodore Ts'o54434922003-12-07 01:28:50 -0500262 fputs(_("Testing with pattern 0x"), stderr);
Theodore Ts'o84c05452003-05-18 01:11:52 -0400263 for (i = 0; i <= nb; i++)
264 fprintf(stderr, "%02x", buffer[i]);
Theodore Ts'o54434922003-12-07 01:28:50 -0500265 fputs(": ", stderr);
Theodore Ts'o84c05452003-05-18 01:11:52 -0400266 }
Theodore Ts'o849b6bc2003-05-07 09:52:14 -0400267 }
268}
269
Theodore Ts'o3839e651997-04-26 13:21:57 +0000270/*
Theodore Ts'o879ac922000-01-18 20:59:11 +0000271 * Perform a read of a sequence of blocks; return the number of blocks
272 * successfully sequentially read.
Theodore Ts'o3839e651997-04-26 13:21:57 +0000273 */
Theodore Ts'oacd77412007-10-22 10:09:05 -0400274static int do_read (int dev, unsigned char * buffer, int try, int block_size,
275 blk_t current_block)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000276{
277 long got;
Iustin Pop264f64a2008-06-12 09:30:04 +0200278 struct timeval tv1, tv2;
279#define NANOSEC (1000000000L)
280#define MILISEC (1000L)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000281
Theodore Ts'o1f9a60c2003-08-01 00:58:00 -0400282 set_o_direct(dev, buffer, try * block_size, current_block);
Theodore Ts'o1c29b092003-07-12 16:01:45 -0400283
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000284 if (v_flag > 1)
285 print_status();
286
Theodore Ts'o3839e651997-04-26 13:21:57 +0000287 /* Seek to the correct loc. */
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000288 if (ext2fs_llseek (dev, (ext2_loff_t) current_block * block_size,
Theodore Ts'of3db3561997-04-26 13:34:30 +0000289 SEEK_SET) != (ext2_loff_t) current_block * block_size)
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000290 com_err (program_name, errno, _("during seek"));
Theodore Ts'o3839e651997-04-26 13:21:57 +0000291
292 /* Try the read */
Iustin Pop264f64a2008-06-12 09:30:04 +0200293 if (d_flag)
Iustin Popedf261f2008-06-18 22:26:26 +0200294 gettimeofday(&tv1, NULL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000295 got = read (dev, buffer, try * block_size);
Iustin Pop264f64a2008-06-12 09:30:04 +0200296 if (d_flag)
Iustin Popedf261f2008-06-18 22:26:26 +0200297 gettimeofday(&tv2, NULL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000298 if (got < 0)
299 got = 0;
Theodore Ts'o9f10a7b1999-07-16 10:41:36 +0000300 if (got & 511)
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000301 fprintf(stderr, _("Weird value (%ld) in do_read\n"), got);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000302 got /= block_size;
Iustin Pop264f64a2008-06-12 09:30:04 +0200303 if (d_flag && got == try) {
Theodore Ts'oc13ab4f2008-07-13 16:17:57 -0400304#ifdef HAVE_NANOSLEEP
Iustin Pop264f64a2008-06-12 09:30:04 +0200305 struct timespec ts;
306 ts.tv_sec = tv2.tv_sec - tv1.tv_sec;
307 ts.tv_nsec = (tv2.tv_usec - tv1.tv_usec) * MILISEC;
308 if (ts.tv_nsec < 0) {
309 ts.tv_nsec += NANOSEC;
310 ts.tv_sec -= 1;
311 }
312 /* increase/decrease the sleep time based on d_flag value */
313 ts.tv_sec = ts.tv_sec * d_flag / 100;
314 ts.tv_nsec = ts.tv_nsec * d_flag / 100;
315 if (ts.tv_nsec > NANOSEC) {
316 ts.tv_sec += ts.tv_nsec / NANOSEC;
317 ts.tv_nsec %= NANOSEC;
318 }
319 if (ts.tv_sec || ts.tv_nsec)
320 nanosleep(&ts, NULL);
Theodore Ts'oc13ab4f2008-07-13 16:17:57 -0400321#else
322#ifdef HAVE_USLEEP
323 struct timeval tv;
324 tv.tv_sec = tv2.tv_sec - tv1.tv_sec;
325 tv.tv_usec = tv2.tv_usec - tv1.tv_usec;
326 tv.tv_sec = tv.tv_sec * d_flag / 100;
327 tv.tv_usec = tv.tv_usec * d_flag / 100;
328 if (tv.tv_usec > 1000000) {
329 tv.tv_sec += tv.tv_usec / 1000000;
330 tv.tv_usec %= 1000000;
331 }
332 if (tv.tv_sec)
333 sleep(tv.tv_sec);
334 if (tv.tv_usec)
335 usleep(tv.tv_usec);
336#endif
337#endif
Iustin Pop264f64a2008-06-12 09:30:04 +0200338 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000339 return got;
340}
341
Theodore Ts'o879ac922000-01-18 20:59:11 +0000342/*
343 * Perform a write of a sequence of blocks; return the number of blocks
344 * successfully sequentially written.
345 */
Theodore Ts'oacd77412007-10-22 10:09:05 -0400346static int do_write(int dev, unsigned char * buffer, int try, int block_size,
347 unsigned long current_block)
Theodore Ts'o879ac922000-01-18 20:59:11 +0000348{
349 long got;
350
Theodore Ts'o1f9a60c2003-08-01 00:58:00 -0400351 set_o_direct(dev, buffer, try * block_size, current_block);
Theodore Ts'o1c29b092003-07-12 16:01:45 -0400352
Theodore Ts'o879ac922000-01-18 20:59:11 +0000353 if (v_flag > 1)
354 print_status();
355
356 /* Seek to the correct loc. */
357 if (ext2fs_llseek (dev, (ext2_loff_t) current_block * block_size,
358 SEEK_SET) != (ext2_loff_t) current_block * block_size)
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000359 com_err (program_name, errno, _("during seek"));
Theodore Ts'o879ac922000-01-18 20:59:11 +0000360
361 /* Try the write */
362 got = write (dev, buffer, try * block_size);
363 if (got < 0)
364 got = 0;
365 if (got & 511)
Theodore Ts'o54434922003-12-07 01:28:50 -0500366 fprintf(stderr, "Weird value (%ld) in do_write\n", got);
Theodore Ts'o879ac922000-01-18 20:59:11 +0000367 got /= block_size;
368 return got;
369}
370
371static int host_dev;
372
Theodore Ts'o4d404542001-01-11 16:04:59 +0000373static void flush_bufs(void)
Theodore Ts'oa418d3a1997-04-26 14:00:26 +0000374{
Theodore Ts'o4d404542001-01-11 16:04:59 +0000375 errcode_t retval;
Theodore Ts'oa418d3a1997-04-26 14:00:26 +0000376
Theodore Ts'o4d404542001-01-11 16:04:59 +0000377 retval = ext2fs_sync_device(host_dev, 1);
378 if (retval)
379 com_err(program_name, retval, _("during ext2fs_sync_device"));
Theodore Ts'oa418d3a1997-04-26 14:00:26 +0000380}
381
Theodore Ts'oacd77412007-10-22 10:09:05 -0400382static unsigned int test_ro (int dev, blk_t last_block,
Theodore Ts'of56f32b2008-07-10 10:08:40 -0400383 int block_size, blk_t first_block,
Theodore Ts'oacd77412007-10-22 10:09:05 -0400384 unsigned int blocks_at_once)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000385{
Theodore Ts'o48e6e812003-07-06 00:36:48 -0400386 unsigned char * blkbuf;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000387 int try;
Theodore Ts'oacd77412007-10-22 10:09:05 -0400388 int got;
Theodore Ts'o879ac922000-01-18 20:59:11 +0000389 unsigned int bb_count = 0;
390 errcode_t errcode;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000391
Theodore Ts'o879ac922000-01-18 20:59:11 +0000392 errcode = ext2fs_badblocks_list_iterate_begin(bb_list,&bb_iter);
393 if (errcode) {
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000394 com_err (program_name, errcode,
395 _("while beginning bad block list iteration"));
Theodore Ts'o879ac922000-01-18 20:59:11 +0000396 exit (1);
397 }
398 do {
399 ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
Theodore Ts'of56f32b2008-07-10 10:08:40 -0400400 } while (next_bad && next_bad < first_block);
Theodore Ts'o879ac922000-01-18 20:59:11 +0000401
Theodore Ts'o849b6bc2003-05-07 09:52:14 -0400402 if (t_flag) {
Theodore Ts'o1c29b092003-07-12 16:01:45 -0400403 blkbuf = allocate_buffer((blocks_at_once + 1) * block_size);
Theodore Ts'o849b6bc2003-05-07 09:52:14 -0400404 } else {
Theodore Ts'o1c29b092003-07-12 16:01:45 -0400405 blkbuf = allocate_buffer(blocks_at_once * block_size);
Theodore Ts'o849b6bc2003-05-07 09:52:14 -0400406 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000407 if (!blkbuf)
408 {
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000409 com_err (program_name, ENOMEM, _("while allocating buffers"));
Theodore Ts'o3839e651997-04-26 13:21:57 +0000410 exit (1);
411 }
Theodore Ts'of3db3561997-04-26 13:34:30 +0000412 if (v_flag) {
Theodore Ts'oacd77412007-10-22 10:09:05 -0400413 fprintf (stderr, _("Checking blocks %lu to %lu\n"),
Theodore Ts'of56f32b2008-07-10 10:08:40 -0400414 (unsigned long) first_block,
Theodore Ts'oacd77412007-10-22 10:09:05 -0400415 (unsigned long) last_block - 1);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000416 }
Theodore Ts'o849b6bc2003-05-07 09:52:14 -0400417 if (t_flag) {
Theodore Ts'o54434922003-12-07 01:28:50 -0500418 fputs(_("Checking for bad blocks in read-only mode\n"), stderr);
Theodore Ts'o849b6bc2003-05-07 09:52:14 -0400419 pattern_fill(blkbuf + blocks_at_once * block_size,
420 t_patts[0], block_size);
421 }
422 flush_bufs();
Theodore Ts'o879ac922000-01-18 20:59:11 +0000423 try = blocks_at_once;
Theodore Ts'of56f32b2008-07-10 10:08:40 -0400424 currently_testing = first_block;
Theodore Ts'o8938ce62006-10-03 23:35:57 -0400425 num_blocks = last_block - 1;
Theodore Ts'o849b6bc2003-05-07 09:52:14 -0400426 if (!t_flag && (s_flag || v_flag)) {
Theodore Ts'o54434922003-12-07 01:28:50 -0500427 fputs(_("Checking for bad blocks (read-only test): "), stderr);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000428 if (v_flag <= 1)
429 alarm_intr(SIGALRM);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000430 }
Theodore Ts'ocd130a02001-05-05 05:43:23 +0000431 while (currently_testing < last_block)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000432 {
Iustin Pop931b0282008-06-11 13:12:17 +0200433 if (max_bb && bb_count >= max_bb) {
434 if (s_flag || v_flag) {
435 fputs(_("Too many bad blocks, aborting test\n"), stderr);
436 }
437 break;
438 }
Theodore Ts'o879ac922000-01-18 20:59:11 +0000439 if (next_bad) {
440 if (currently_testing == next_bad) {
441 /* fprintf (out, "%lu\n", nextbad); */
442 ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
443 currently_testing++;
444 continue;
445 }
446 else if (currently_testing + try > next_bad)
447 try = next_bad - currently_testing;
448 }
Theodore Ts'ocd130a02001-05-05 05:43:23 +0000449 if (currently_testing + try > last_block)
450 try = last_block - currently_testing;
Theodore Ts'o879ac922000-01-18 20:59:11 +0000451 got = do_read (dev, blkbuf, try, block_size, currently_testing);
Theodore Ts'o849b6bc2003-05-07 09:52:14 -0400452 if (t_flag) {
453 /* test the comparison between all the
454 blocks successfully read */
455 int i;
456 for (i = 0; i < got; ++i)
457 if (memcmp (blkbuf+i*block_size,
458 blkbuf+blocks_at_once*block_size,
459 block_size))
460 bb_count += bb_output(currently_testing + i);
461 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000462 currently_testing += got;
463 if (got == try) {
Theodore Ts'o879ac922000-01-18 20:59:11 +0000464 try = blocks_at_once;
Theodore Ts'o1f9a60c2003-08-01 00:58:00 -0400465 /* recover page-aligned offset for O_DIRECT */
Theodore Ts'oacd77412007-10-22 10:09:05 -0400466 if ( (blocks_at_once >= sys_page_size >> 9)
Theodore Ts'o1f9a60c2003-08-01 00:58:00 -0400467 && (currently_testing % (sys_page_size >> 9)!= 0))
468 try -= (sys_page_size >> 9)
469 - (currently_testing
470 % (sys_page_size >> 9));
Theodore Ts'o3839e651997-04-26 13:21:57 +0000471 continue;
472 }
473 else
474 try = 1;
Theodore Ts'o879ac922000-01-18 20:59:11 +0000475 if (got == 0) {
Theodore Ts'odd018f52000-02-06 23:57:07 +0000476 bb_count += bb_output(currently_testing++);
Theodore Ts'o879ac922000-01-18 20:59:11 +0000477 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000478 }
479 num_blocks = 0;
480 alarm(0);
Theodore Ts'o849b6bc2003-05-07 09:52:14 -0400481 if (s_flag || v_flag)
Theodore Ts'o3ef681c2004-09-19 08:04:44 -0400482 fputs(_(done_string), stderr);
Theodore Ts'o879ac922000-01-18 20:59:11 +0000483
Theodore Ts'of3db3561997-04-26 13:34:30 +0000484 fflush (stderr);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000485 free (blkbuf);
Theodore Ts'o879ac922000-01-18 20:59:11 +0000486
487 ext2fs_badblocks_list_iterate_end(bb_iter);
488
489 return bb_count;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000490}
491
Theodore Ts'oacd77412007-10-22 10:09:05 -0400492static unsigned int test_rw (int dev, blk_t last_block,
Theodore Ts'of56f32b2008-07-10 10:08:40 -0400493 int block_size, blk_t first_block,
Theodore Ts'oacd77412007-10-22 10:09:05 -0400494 unsigned int blocks_at_once)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000495{
Theodore Ts'o1c29b092003-07-12 16:01:45 -0400496 unsigned char *buffer, *read_buffer;
Theodore Ts'oe9860ae2007-10-22 09:51:50 -0400497 const unsigned int patterns[] = {0xaa, 0x55, 0xff, 0x00};
498 const unsigned int *pattern;
Theodore Ts'o1c29b092003-07-12 16:01:45 -0400499 int i, try, got, nr_pattern, pat_idx;
Theodore Ts'o879ac922000-01-18 20:59:11 +0000500 unsigned int bb_count = 0;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000501
Theodore Ts'o1c29b092003-07-12 16:01:45 -0400502 buffer = allocate_buffer(2 * blocks_at_once * block_size);
503 read_buffer = buffer + blocks_at_once * block_size;
504
505 if (!buffer) {
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000506 com_err (program_name, ENOMEM, _("while allocating buffers"));
Theodore Ts'o3839e651997-04-26 13:21:57 +0000507 exit (1);
508 }
509
Theodore Ts'o4d404542001-01-11 16:04:59 +0000510 flush_bufs();
Theodore Ts'oa418d3a1997-04-26 14:00:26 +0000511
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000512 if (v_flag) {
Theodore Ts'o54434922003-12-07 01:28:50 -0500513 fputs(_("Checking for bad blocks in read-write mode\n"),
514 stderr);
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000515 fprintf(stderr, _("From block %lu to %lu\n"),
Theodore Ts'of56f32b2008-07-10 10:08:40 -0400516 (unsigned long) first_block,
517 (unsigned long) last_block - 1);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000518 }
Theodore Ts'o849b6bc2003-05-07 09:52:14 -0400519 if (t_flag) {
520 pattern = t_patts;
521 nr_pattern = t_flag;
522 } else {
523 pattern = patterns;
524 nr_pattern = sizeof(patterns) / sizeof(patterns[0]);
525 }
526 for (pat_idx = 0; pat_idx < nr_pattern; pat_idx++) {
Theodore Ts'o1c29b092003-07-12 16:01:45 -0400527 pattern_fill(buffer, pattern[pat_idx],
528 blocks_at_once * block_size);
Theodore Ts'o8938ce62006-10-03 23:35:57 -0400529 num_blocks = last_block - 1;
Theodore Ts'of56f32b2008-07-10 10:08:40 -0400530 currently_testing = first_block;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000531 if (s_flag && v_flag <= 1)
Theodore Ts'of3db3561997-04-26 13:34:30 +0000532 alarm_intr(SIGALRM);
Theodore Ts'o1c29b092003-07-12 16:01:45 -0400533
534 try = blocks_at_once;
535 while (currently_testing < last_block) {
Iustin Pop931b0282008-06-11 13:12:17 +0200536 if (max_bb && bb_count >= max_bb) {
537 if (s_flag || v_flag) {
538 fputs(_("Too many bad blocks, aborting test\n"), stderr);
539 }
540 break;
541 }
Theodore Ts'o1c29b092003-07-12 16:01:45 -0400542 if (currently_testing + try > last_block)
543 try = last_block - currently_testing;
544 got = do_write(dev, buffer, try, block_size,
545 currently_testing);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000546 if (v_flag > 1)
547 print_status();
Theodore Ts'o1c29b092003-07-12 16:01:45 -0400548
549 currently_testing += got;
550 if (got == try) {
551 try = blocks_at_once;
Theodore Ts'o1f9a60c2003-08-01 00:58:00 -0400552 /* recover page-aligned offset for O_DIRECT */
Theodore Ts'oacd77412007-10-22 10:09:05 -0400553 if ( (blocks_at_once >= sys_page_size >> 9)
Theodore Ts'o1f9a60c2003-08-01 00:58:00 -0400554 && (currently_testing %
555 (sys_page_size >> 9)!= 0))
556 try -= (sys_page_size >> 9)
557 - (currently_testing
558 % (sys_page_size >> 9));
Theodore Ts'o1c29b092003-07-12 16:01:45 -0400559 continue;
560 } else
561 try = 1;
562 if (got == 0) {
563 bb_count += bb_output(currently_testing++);
564 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000565 }
Theodore Ts'o1c29b092003-07-12 16:01:45 -0400566
Theodore Ts'of3db3561997-04-26 13:34:30 +0000567 num_blocks = 0;
568 alarm (0);
569 if (s_flag | v_flag)
Theodore Ts'o3ef681c2004-09-19 08:04:44 -0400570 fputs(_(done_string), stderr);
Theodore Ts'o4d404542001-01-11 16:04:59 +0000571 flush_bufs();
Theodore Ts'of3db3561997-04-26 13:34:30 +0000572 if (s_flag | v_flag)
Theodore Ts'o54434922003-12-07 01:28:50 -0500573 fputs(_("Reading and comparing: "), stderr);
Theodore Ts'ocd130a02001-05-05 05:43:23 +0000574 num_blocks = last_block;
Theodore Ts'of56f32b2008-07-10 10:08:40 -0400575 currently_testing = first_block;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000576 if (s_flag && v_flag <= 1)
Theodore Ts'of3db3561997-04-26 13:34:30 +0000577 alarm_intr(SIGALRM);
Theodore Ts'o1c29b092003-07-12 16:01:45 -0400578
579 try = blocks_at_once;
580 while (currently_testing < last_block) {
Iustin Pop931b0282008-06-11 13:12:17 +0200581 if (max_bb && bb_count >= max_bb) {
582 if (s_flag || v_flag) {
583 fputs(_("Too many bad blocks, aborting test\n"), stderr);
584 }
585 break;
586 }
Theodore Ts'o1c29b092003-07-12 16:01:45 -0400587 if (currently_testing + try > last_block)
588 try = last_block - currently_testing;
589 got = do_read (dev, read_buffer, try, block_size,
590 currently_testing);
591 if (got == 0) {
592 bb_count += bb_output(currently_testing++);
593 continue;
594 }
595 for (i=0; i < got; i++) {
596 if (memcmp(read_buffer + i * block_size,
597 buffer + i * block_size,
598 block_size))
599 bb_count += bb_output(currently_testing+i);
600 }
601 currently_testing += got;
Theodore Ts'o1f9a60c2003-08-01 00:58:00 -0400602 /* recover page-aligned offset for O_DIRECT */
Theodore Ts'oacd77412007-10-22 10:09:05 -0400603 if ( (blocks_at_once >= sys_page_size >> 9)
Theodore Ts'o1f9a60c2003-08-01 00:58:00 -0400604 && (currently_testing % (sys_page_size >> 9)!= 0))
605 try = blocks_at_once - (sys_page_size >> 9)
606 - (currently_testing
607 % (sys_page_size >> 9));
608 else
609 try = blocks_at_once;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000610 if (v_flag > 1)
611 print_status();
Theodore Ts'o3839e651997-04-26 13:21:57 +0000612 }
Theodore Ts'o1c29b092003-07-12 16:01:45 -0400613
Theodore Ts'of3db3561997-04-26 13:34:30 +0000614 num_blocks = 0;
615 alarm (0);
616 if (s_flag | v_flag)
Theodore Ts'o3ef681c2004-09-19 08:04:44 -0400617 fputs(_(done_string), stderr);
Theodore Ts'o4d404542001-01-11 16:04:59 +0000618 flush_bufs();
Theodore Ts'o3839e651997-04-26 13:21:57 +0000619 }
Theodore Ts'o849b6bc2003-05-07 09:52:14 -0400620 uncapture_terminate();
Theodore Ts'o6d40f562003-05-07 08:35:38 -0400621 free(buffer);
Theodore Ts'o879ac922000-01-18 20:59:11 +0000622 return bb_count;
623}
624
Theodore Ts'od49a22b2000-07-06 00:31:27 +0000625struct saved_blk_record {
626 blk_t block;
627 int num;
628};
629
Theodore Ts'oacd77412007-10-22 10:09:05 -0400630static unsigned int test_nd (int dev, blk_t last_block,
Theodore Ts'of56f32b2008-07-10 10:08:40 -0400631 int block_size, blk_t first_block,
Theodore Ts'oacd77412007-10-22 10:09:05 -0400632 unsigned int blocks_at_once)
Theodore Ts'o879ac922000-01-18 20:59:11 +0000633{
Theodore Ts'o48e6e812003-07-06 00:36:48 -0400634 unsigned char *blkbuf, *save_ptr, *test_ptr, *read_ptr;
Theodore Ts'o1c29b092003-07-12 16:01:45 -0400635 unsigned char *test_base, *save_base, *read_base;
Theodore Ts'odd018f52000-02-06 23:57:07 +0000636 int try, i;
Theodore Ts'oe9860ae2007-10-22 09:51:50 -0400637 const unsigned int patterns[] = { ~0 };
638 const unsigned int *pattern;
Theodore Ts'o849b6bc2003-05-07 09:52:14 -0400639 int nr_pattern, pat_idx;
Theodore Ts'oacd77412007-10-22 10:09:05 -0400640 int got, used2, written;
641 blk_t save_currently_testing;
Theodore Ts'od49a22b2000-07-06 00:31:27 +0000642 struct saved_blk_record *test_record;
Theodore Ts'oa551b782000-07-13 22:05:31 +0000643 /* This is static to prevent being clobbered by the longjmp */
644 static int num_saved;
Theodore Ts'o879ac922000-01-18 20:59:11 +0000645 jmp_buf terminate_env;
Theodore Ts'o879ac922000-01-18 20:59:11 +0000646 errcode_t errcode;
Theodore Ts'o54434922003-12-07 01:28:50 -0500647 unsigned long buf_used;
648 static unsigned int bb_count;
Theodore Ts'o879ac922000-01-18 20:59:11 +0000649
Theodore Ts'o54434922003-12-07 01:28:50 -0500650 bb_count = 0;
Theodore Ts'o879ac922000-01-18 20:59:11 +0000651 errcode = ext2fs_badblocks_list_iterate_begin(bb_list,&bb_iter);
652 if (errcode) {
Theodore Ts'odd018f52000-02-06 23:57:07 +0000653 com_err (program_name, errcode,
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000654 _("while beginning bad block list iteration"));
Theodore Ts'o879ac922000-01-18 20:59:11 +0000655 exit (1);
656 }
657 do {
658 ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
Theodore Ts'of56f32b2008-07-10 10:08:40 -0400659 } while (next_bad && next_bad < first_block);
Theodore Ts'o879ac922000-01-18 20:59:11 +0000660
Theodore Ts'o1c29b092003-07-12 16:01:45 -0400661 blkbuf = allocate_buffer(3 * blocks_at_once * block_size);
Theodore Ts'od49a22b2000-07-06 00:31:27 +0000662 test_record = malloc (blocks_at_once*sizeof(struct saved_blk_record));
663 if (!blkbuf || !test_record) {
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000664 com_err(program_name, ENOMEM, _("while allocating buffers"));
Theodore Ts'o879ac922000-01-18 20:59:11 +0000665 exit (1);
666 }
Theodore Ts'o1c29b092003-07-12 16:01:45 -0400667
668 save_base = blkbuf;
669 test_base = blkbuf + (blocks_at_once * block_size);
670 read_base = blkbuf + (2 * blocks_at_once * block_size);
671
Theodore Ts'od49a22b2000-07-06 00:31:27 +0000672 num_saved = 0;
Theodore Ts'o879ac922000-01-18 20:59:11 +0000673
Theodore Ts'o4d404542001-01-11 16:04:59 +0000674 flush_bufs();
Theodore Ts'o879ac922000-01-18 20:59:11 +0000675 if (v_flag) {
Theodore Ts'o54434922003-12-07 01:28:50 -0500676 fputs(_("Checking for bad blocks in non-destructive read-write mode\n"), stderr);
Theodore Ts'oacd77412007-10-22 10:09:05 -0400677 fprintf (stderr, _("From block %lu to %lu\n"),
Theodore Ts'of56f32b2008-07-10 10:08:40 -0400678 (unsigned long) first_block,
679 (unsigned long) last_block - 1);
Theodore Ts'o879ac922000-01-18 20:59:11 +0000680 }
Theodore Ts'o879ac922000-01-18 20:59:11 +0000681 if (s_flag || v_flag > 1) {
Theodore Ts'o54434922003-12-07 01:28:50 -0500682 fputs(_("Checking for bad blocks (non-destructive read-write test)\n"), stderr);
Theodore Ts'o879ac922000-01-18 20:59:11 +0000683 }
Theodore Ts'o4d003982000-04-03 16:01:11 +0000684 if (setjmp(terminate_env)) {
685 /*
686 * Abnormal termination by a signal is handled here.
Theodore Ts'o4d003982000-04-03 16:01:11 +0000687 */
Theodore Ts'oa551b782000-07-13 22:05:31 +0000688 signal (SIGALRM, SIG_IGN);
Theodore Ts'o54434922003-12-07 01:28:50 -0500689 fputs(_("\nInterrupt caught, cleaning up\n"), stderr);
Theodore Ts'o879ac922000-01-18 20:59:11 +0000690
Theodore Ts'o1c29b092003-07-12 16:01:45 -0400691 save_ptr = save_base;
Theodore Ts'od49a22b2000-07-06 00:31:27 +0000692 for (i=0; i < num_saved; i++) {
693 do_write(dev, save_ptr, test_record[i].num,
694 block_size, test_record[i].block);
695 save_ptr += test_record[i].num * block_size;
696 }
Theodore Ts'o879ac922000-01-18 20:59:11 +0000697 fflush (out);
Theodore Ts'odd018f52000-02-06 23:57:07 +0000698 exit(1);
Theodore Ts'o879ac922000-01-18 20:59:11 +0000699 }
Theodore Ts'o4d003982000-04-03 16:01:11 +0000700
701 /* set up abend handler */
702 capture_terminate(terminate_env);
703
Theodore Ts'o849b6bc2003-05-07 09:52:14 -0400704 if (t_flag) {
705 pattern = t_patts;
706 nr_pattern = t_flag;
707 } else {
708 pattern = patterns;
709 nr_pattern = sizeof(patterns) / sizeof(patterns[0]);
710 }
711 for (pat_idx = 0; pat_idx < nr_pattern; pat_idx++) {
Theodore Ts'o1c29b092003-07-12 16:01:45 -0400712 pattern_fill(test_base, pattern[pat_idx],
713 blocks_at_once * block_size);
Theodore Ts'o4d003982000-04-03 16:01:11 +0000714
Theodore Ts'o849b6bc2003-05-07 09:52:14 -0400715 buf_used = 0;
716 bb_count = 0;
Theodore Ts'o1c29b092003-07-12 16:01:45 -0400717 save_ptr = save_base;
718 test_ptr = test_base;
Theodore Ts'of56f32b2008-07-10 10:08:40 -0400719 currently_testing = first_block;
Theodore Ts'o8938ce62006-10-03 23:35:57 -0400720 num_blocks = last_block - 1;
Theodore Ts'o849b6bc2003-05-07 09:52:14 -0400721 if (s_flag && v_flag <= 1)
722 alarm_intr(SIGALRM);
723
724 while (currently_testing < last_block) {
Iustin Pop931b0282008-06-11 13:12:17 +0200725 if (max_bb && bb_count >= max_bb) {
726 if (s_flag || v_flag) {
727 fputs(_("Too many bad blocks, aborting test\n"), stderr);
728 }
729 break;
730 }
Theodore Ts'o1c29b092003-07-12 16:01:45 -0400731 got = try = blocks_at_once - buf_used;
Theodore Ts'o849b6bc2003-05-07 09:52:14 -0400732 if (next_bad) {
733 if (currently_testing == next_bad) {
734 /* fprintf (out, "%lu\n", nextbad); */
735 ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
736 currently_testing++;
737 goto check_for_more;
738 }
739 else if (currently_testing + try > next_bad)
740 try = next_bad - currently_testing;
741 }
742 if (currently_testing + try > last_block)
743 try = last_block - currently_testing;
744 got = do_read (dev, save_ptr, try, block_size,
745 currently_testing);
746 if (got == 0) {
747 /* First block must have been bad. */
748 bb_count += bb_output(currently_testing++);
Theodore Ts'od49a22b2000-07-06 00:31:27 +0000749 goto check_for_more;
Theodore Ts'o4d003982000-04-03 16:01:11 +0000750 }
Theodore Ts'o4d003982000-04-03 16:01:11 +0000751
Theodore Ts'o849b6bc2003-05-07 09:52:14 -0400752 /*
753 * Note the fact that we've saved this much data
754 * *before* we overwrite it with test data
755 */
756 test_record[num_saved].block = currently_testing;
757 test_record[num_saved].num = got;
758 num_saved++;
Theodore Ts'od49a22b2000-07-06 00:31:27 +0000759
Theodore Ts'o849b6bc2003-05-07 09:52:14 -0400760 /* Write the test data */
761 written = do_write (dev, test_ptr, got, block_size,
762 currently_testing);
763 if (written != got)
764 com_err (program_name, errno,
765 _("during test data write, block %lu"),
Theodore Ts'oacd77412007-10-22 10:09:05 -0400766 (unsigned long) currently_testing +
767 written);
Theodore Ts'od49a22b2000-07-06 00:31:27 +0000768
Theodore Ts'o849b6bc2003-05-07 09:52:14 -0400769 buf_used += got;
Theodore Ts'o4d003982000-04-03 16:01:11 +0000770 save_ptr += got * block_size;
771 test_ptr += got * block_size;
Theodore Ts'o849b6bc2003-05-07 09:52:14 -0400772 currently_testing += got;
773 if (got != try)
774 bb_count += bb_output(currently_testing++);
775
776 check_for_more:
777 /*
778 * If there's room for more blocks to be tested this
779 * around, and we're not done yet testing the disk, go
780 * back and get some more blocks.
781 */
782 if ((buf_used != blocks_at_once) &&
783 (currently_testing < last_block))
784 continue;
785
786 flush_bufs();
787 save_currently_testing = currently_testing;
788
789 /*
790 * for each contiguous block that we read into the
791 * buffer (and wrote test data into afterwards), read
792 * it back (looping if necessary, to get past newly
793 * discovered unreadable blocks, of which there should
794 * be none, but with a hard drive which is unreliable,
795 * it has happened), and compare with the test data
796 * that was written; output to the bad block list if
797 * it doesn't match.
798 */
799 used2 = 0;
Theodore Ts'o1c29b092003-07-12 16:01:45 -0400800 save_ptr = save_base;
801 test_ptr = test_base;
802 read_ptr = read_base;
Theodore Ts'o849b6bc2003-05-07 09:52:14 -0400803 try = 0;
804
805 while (1) {
806 if (try == 0) {
807 if (used2 >= num_saved)
808 break;
809 currently_testing = test_record[used2].block;
810 try = test_record[used2].num;
811 used2++;
812 }
813
814 got = do_read (dev, read_ptr, try,
815 block_size, currently_testing);
816
817 /* test the comparison between all the
818 blocks successfully read */
819 for (i = 0; i < got; ++i)
820 if (memcmp (test_ptr+i*block_size,
821 read_ptr+i*block_size, block_size))
822 bb_count += bb_output(currently_testing + i);
823 if (got < try) {
824 bb_count += bb_output(currently_testing + got);
825 got++;
826 }
827
Theodore Ts'o1c29b092003-07-12 16:01:45 -0400828 /* write back original data */
829 do_write (dev, save_ptr, got,
830 block_size, currently_testing);
831 save_ptr += got * block_size;
Theodore Ts'o849b6bc2003-05-07 09:52:14 -0400832
833 currently_testing += got;
Theodore Ts'o849b6bc2003-05-07 09:52:14 -0400834 test_ptr += got * block_size;
835 read_ptr += got * block_size;
836 try -= got;
837 }
838
839 /* empty the buffer so it can be reused */
840 num_saved = 0;
841 buf_used = 0;
Theodore Ts'o1c29b092003-07-12 16:01:45 -0400842 save_ptr = save_base;
843 test_ptr = test_base;
Theodore Ts'o849b6bc2003-05-07 09:52:14 -0400844 currently_testing = save_currently_testing;
Theodore Ts'o4d003982000-04-03 16:01:11 +0000845 }
Theodore Ts'o849b6bc2003-05-07 09:52:14 -0400846 num_blocks = 0;
847 alarm(0);
848 if (s_flag || v_flag > 1)
Theodore Ts'o3ef681c2004-09-19 08:04:44 -0400849 fputs(_(done_string), stderr);
Theodore Ts'o4d003982000-04-03 16:01:11 +0000850
Theodore Ts'o849b6bc2003-05-07 09:52:14 -0400851 flush_bufs();
Theodore Ts'o4d003982000-04-03 16:01:11 +0000852 }
Theodore Ts'o4d003982000-04-03 16:01:11 +0000853 uncapture_terminate();
Theodore Ts'odd018f52000-02-06 23:57:07 +0000854 fflush(stderr);
855 free(blkbuf);
Theodore Ts'od49a22b2000-07-06 00:31:27 +0000856 free(test_record);
Theodore Ts'o879ac922000-01-18 20:59:11 +0000857
858 ext2fs_badblocks_list_iterate_end(bb_iter);
859
860 return bb_count;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000861}
862
Theodore Ts'o981dc562000-07-06 14:13:29 +0000863static void check_mount(char *device_name)
864{
865 errcode_t retval;
866 int mount_flags;
867
868 retval = ext2fs_check_if_mounted(device_name, &mount_flags);
869 if (retval) {
870 com_err("ext2fs_check_if_mount", retval,
871 _("while determining whether %s is mounted."),
872 device_name);
873 return;
874 }
Theodore Ts'o2fa8f372005-06-05 16:05:22 -0400875 if (mount_flags & EXT2_MF_MOUNTED) {
876 fprintf(stderr, _("%s is mounted; "), device_name);
877 if (force) {
878 fputs(_("badblocks forced anyway. "
879 "Hope /etc/mtab is incorrect.\n"), stderr);
880 return;
881 }
882 abort_badblocks:
883 fputs(_("it's not safe to run badblocks!\n"), stderr);
884 exit(1);
Theodore Ts'o981dc562000-07-06 14:13:29 +0000885 }
Theodore Ts'o2fa8f372005-06-05 16:05:22 -0400886
Theodore Ts'of63978a2006-05-13 09:25:47 -0400887 if ((mount_flags & EXT2_MF_BUSY) && !exclusive_ok) {
Theodore Ts'o2fa8f372005-06-05 16:05:22 -0400888 fprintf(stderr, _("%s is apparently in use by the system; "),
889 device_name);
890 if (force)
891 fputs(_("badblocks forced anyway.\n"), stderr);
892 else
893 goto abort_badblocks;
894 }
895
Theodore Ts'o981dc562000-07-06 14:13:29 +0000896}
897
Theodore Ts'od4be9fa2007-10-22 10:19:20 -0400898/*
899 * This function will convert a string to an unsigned long, printing
900 * an error message if it fails, and returning success or failure in err.
901 */
902static unsigned int parse_uint(const char *str, const char *descr)
903{
904 char *tmp;
905 unsigned long ret;
906
Iustin Popeb594252008-06-11 17:55:18 +0200907 errno = 0;
Theodore Ts'od4be9fa2007-10-22 10:19:20 -0400908 ret = strtoul(str, &tmp, 0);
909 if (*tmp || errno || (ret > UINT_MAX) ||
910 (ret == ULONG_MAX && errno == ERANGE)) {
911 com_err (program_name, 0, _("invalid %s - %s"), descr, str);
912 exit (1);
913 }
914 return ret;
915}
Theodore Ts'o981dc562000-07-06 14:13:29 +0000916
Theodore Ts'o00e54331997-09-16 02:13:52 +0000917int main (int argc, char ** argv)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000918{
Theodore Ts'o519149f1997-10-25 03:49:49 +0000919 int c;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000920 char * device_name;
Theodore Ts'o879ac922000-01-18 20:59:11 +0000921 char * host_device_name = NULL;
922 char * input_file = NULL;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000923 char * output_file = NULL;
Theodore Ts'o879ac922000-01-18 20:59:11 +0000924 FILE * in = NULL;
Theodore Ts'odd018f52000-02-06 23:57:07 +0000925 int block_size = 1024;
Theodore Ts'oacd77412007-10-22 10:09:05 -0400926 unsigned int blocks_at_once = 64;
Theodore Ts'of56f32b2008-07-10 10:08:40 -0400927 blk_t last_block, first_block;
Theodore Ts'o879ac922000-01-18 20:59:11 +0000928 int num_passes = 0;
929 int passes_clean = 0;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000930 int dev;
Theodore Ts'o879ac922000-01-18 20:59:11 +0000931 errcode_t errcode;
Theodore Ts'oe9860ae2007-10-22 09:51:50 -0400932 unsigned int pattern;
Theodore Ts'oacd77412007-10-22 10:09:05 -0400933 unsigned int (*test_func)(int, blk_t,
934 int, blk_t,
935 unsigned int);
Theodore Ts'o1c29b092003-07-12 16:01:45 -0400936 int open_flag = 0;
937 long sysval;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000938
939 setbuf(stdout, NULL);
940 setbuf(stderr, NULL);
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000941#ifdef ENABLE_NLS
942 setlocale(LC_MESSAGES, "");
Theodore Ts'o14308a52002-03-05 03:26:52 -0500943 setlocale(LC_CTYPE, "");
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000944 bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
945 textdomain(NLS_CAT_NAME);
946#endif
Theodore Ts'o6d40f562003-05-07 08:35:38 -0400947 srandom((unsigned int)time(NULL)); /* simple randomness is enough */
Theodore Ts'o4d003982000-04-03 16:01:11 +0000948 test_func = test_ro;
Theodore Ts'o4d404542001-01-11 16:04:59 +0000949
Theodore Ts'o1c29b092003-07-12 16:01:45 -0400950 /* Determine the system page size if possible */
951#ifdef HAVE_SYSCONF
952#if (!defined(_SC_PAGESIZE) && defined(_SC_PAGE_SIZE))
953#define _SC_PAGESIZE _SC_PAGE_SIZE
954#endif
955#ifdef _SC_PAGESIZE
956 sysval = sysconf(_SC_PAGESIZE);
957 if (sysval > 0)
958 sys_page_size = sysval;
959#endif /* _SC_PAGESIZE */
960#endif /* HAVE_SYSCONF */
961
Theodore Ts'o3839e651997-04-26 13:21:57 +0000962 if (argc && *argv)
963 program_name = *argv;
Iustin Pop264f64a2008-06-12 09:30:04 +0200964 while ((c = getopt (argc, argv, "b:d:e:fi:o:svwnc:p:h:t:X")) != EOF) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000965 switch (c) {
966 case 'b':
Theodore Ts'od4be9fa2007-10-22 10:19:20 -0400967 block_size = parse_uint(optarg, "block size");
968 if (block_size > 4096) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000969 com_err (program_name, 0,
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000970 _("bad block size - %s"), optarg);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000971 exit (1);
972 }
973 break;
Theodore Ts'o981dc562000-07-06 14:13:29 +0000974 case 'f':
975 force++;
976 break;
Theodore Ts'o879ac922000-01-18 20:59:11 +0000977 case 'i':
978 input_file = optarg;
979 break;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000980 case 'o':
981 output_file = optarg;
982 break;
983 case 's':
984 s_flag = 1;
985 break;
986 case 'v':
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000987 v_flag++;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000988 break;
989 case 'w':
Theodore Ts'o4d003982000-04-03 16:01:11 +0000990 if (w_flag)
Theodore Ts'od8b5f772006-11-12 23:09:03 -0500991 exclusive_usage();
Theodore Ts'o4d003982000-04-03 16:01:11 +0000992 test_func = test_rw;
993 w_flag = 1;
Theodore Ts'o879ac922000-01-18 20:59:11 +0000994 break;
995 case 'n':
Theodore Ts'o4d003982000-04-03 16:01:11 +0000996 if (w_flag)
Theodore Ts'od8b5f772006-11-12 23:09:03 -0500997 exclusive_usage();
Theodore Ts'o4d003982000-04-03 16:01:11 +0000998 test_func = test_nd;
Theodore Ts'o879ac922000-01-18 20:59:11 +0000999 w_flag = 2;
1000 break;
1001 case 'c':
Theodore Ts'od4be9fa2007-10-22 10:19:20 -04001002 blocks_at_once = parse_uint(optarg, "blocks at once");
Theodore Ts'o879ac922000-01-18 20:59:11 +00001003 break;
Iustin Pop931b0282008-06-11 13:12:17 +02001004 case 'e':
1005 max_bb = parse_uint(optarg, "max bad block count");
1006 break;
Iustin Pop264f64a2008-06-12 09:30:04 +02001007 case 'd':
1008 d_flag = parse_uint(optarg, "read delay factor");
1009 break;
Theodore Ts'o879ac922000-01-18 20:59:11 +00001010 case 'p':
Theodore Ts'od4be9fa2007-10-22 10:19:20 -04001011 num_passes = parse_uint(optarg,
1012 "number of clean passes");
Theodore Ts'o879ac922000-01-18 20:59:11 +00001013 break;
1014 case 'h':
1015 host_device_name = optarg;
Theodore Ts'o3839e651997-04-26 13:21:57 +00001016 break;
Theodore Ts'o849b6bc2003-05-07 09:52:14 -04001017 case 't':
1018 if (t_flag + 1 > t_max) {
Theodore Ts'oe9860ae2007-10-22 09:51:50 -04001019 unsigned int *t_patts_new;
Theodore Ts'o849b6bc2003-05-07 09:52:14 -04001020
Theodore Ts'o69d81352008-07-06 18:50:44 -04001021 t_patts_new = realloc(t_patts, sizeof(int) *
1022 (t_max + T_INC));
Theodore Ts'o849b6bc2003-05-07 09:52:14 -04001023 if (!t_patts_new) {
1024 com_err(program_name, ENOMEM,
1025 _("can't allocate memory for "
1026 "test_pattern - %s"),
1027 optarg);
1028 exit(1);
1029 }
1030 t_patts = t_patts_new;
1031 t_max += T_INC;
1032 }
Theodore Ts'o84c05452003-05-18 01:11:52 -04001033 if (!strcmp(optarg, "r") || !strcmp(optarg,"random")) {
1034 t_patts[t_flag++] = ~0;
1035 } else {
Theodore Ts'od4be9fa2007-10-22 10:19:20 -04001036 pattern = parse_uint(optarg, "test pattern");
Theodore Ts'oe9860ae2007-10-22 09:51:50 -04001037 if (pattern == (unsigned int) ~0)
Theodore Ts'o84c05452003-05-18 01:11:52 -04001038 pattern = 0xffff;
1039 t_patts[t_flag++] = pattern;
Theodore Ts'o849b6bc2003-05-07 09:52:14 -04001040 }
Theodore Ts'o849b6bc2003-05-07 09:52:14 -04001041 break;
Theodore Ts'of63978a2006-05-13 09:25:47 -04001042 case 'X':
1043 exclusive_ok++;
1044 break;
Theodore Ts'o3839e651997-04-26 13:21:57 +00001045 default:
Theodore Ts'o818180c1998-06-27 05:11:14 +00001046 usage();
Theodore Ts'o3839e651997-04-26 13:21:57 +00001047 }
1048 }
Theodore Ts'o849b6bc2003-05-07 09:52:14 -04001049 if (!w_flag) {
1050 if (t_flag > 1) {
1051 com_err(program_name, 0,
1052 _("Maximum of one test_pattern may be specified "
1053 "in read-only mode"));
1054 exit(1);
1055 }
Theodore Ts'oe9860ae2007-10-22 09:51:50 -04001056 if (t_patts && (t_patts[0] == (unsigned int) ~0)) {
Theodore Ts'o849b6bc2003-05-07 09:52:14 -04001057 com_err(program_name, 0,
1058 _("Random test_pattern is not allowed "
1059 "in read-only mode"));
1060 exit(1);
1061 }
1062 }
Theodore Ts'o3839e651997-04-26 13:21:57 +00001063 if (optind > argc - 1)
Theodore Ts'o818180c1998-06-27 05:11:14 +00001064 usage();
Theodore Ts'o3839e651997-04-26 13:21:57 +00001065 device_name = argv[optind++];
Theodore Ts'o35964b52000-07-06 13:19:43 +00001066 if (optind > argc - 1) {
1067 errcode = ext2fs_get_device_size(device_name,
1068 block_size,
Theodore Ts'ocd130a02001-05-05 05:43:23 +00001069 &last_block);
Theodore Ts'o35964b52000-07-06 13:19:43 +00001070 if (errcode == EXT2_ET_UNIMPLEMENTED) {
1071 com_err(program_name, 0,
1072 _("Couldn't determine device size; you "
1073 "must specify\nthe size manually\n"));
1074 exit(1);
1075 }
1076 if (errcode) {
1077 com_err(program_name, errcode,
1078 _("while trying to determine device size"));
1079 exit(1);
1080 }
1081 } else {
Theodore Ts'o5267a522007-06-04 01:49:51 -04001082 errno = 0;
Theodore Ts'of56f32b2008-07-10 10:08:40 -04001083 last_block = parse_uint(argv[optind], _("last block"));
Theodore Ts'o5267a522007-06-04 01:49:51 -04001084 last_block++;
Theodore Ts'o35964b52000-07-06 13:19:43 +00001085 optind++;
Theodore Ts'o3839e651997-04-26 13:21:57 +00001086 }
Theodore Ts'o35964b52000-07-06 13:19:43 +00001087 if (optind <= argc-1) {
Theodore Ts'o5267a522007-06-04 01:49:51 -04001088 errno = 0;
Theodore Ts'of56f32b2008-07-10 10:08:40 -04001089 first_block = parse_uint(argv[optind], _("first block"));
1090 } else first_block = 0;
1091 if (first_block >= last_block) {
Theodore Ts'od4e0b1c2007-08-03 18:56:01 -04001092 com_err (program_name, 0, _("invalid starting block (%lu): must be less than %lu"),
Theodore Ts'of56f32b2008-07-10 10:08:40 -04001093 (unsigned long) first_block, (unsigned long) last_block);
Theodore Ts'of3db3561997-04-26 13:34:30 +00001094 exit (1);
1095 }
Theodore Ts'o981dc562000-07-06 14:13:29 +00001096 if (w_flag)
1097 check_mount(device_name);
1098
Theodore Ts'o1c29b092003-07-12 16:01:45 -04001099 open_flag = w_flag ? O_RDWR : O_RDONLY;
1100 dev = open (device_name, open_flag);
Theodore Ts'o5493a272002-01-02 14:15:35 -05001101 if (dev == -1) {
Theodore Ts'od9c56d32000-02-08 00:47:55 +00001102 com_err (program_name, errno, _("while trying to open %s"),
Theodore Ts'o3839e651997-04-26 13:21:57 +00001103 device_name);
1104 exit (1);
1105 }
Theodore Ts'o879ac922000-01-18 20:59:11 +00001106 if (host_device_name) {
Theodore Ts'o1c29b092003-07-12 16:01:45 -04001107 host_dev = open (host_device_name, open_flag);
Theodore Ts'o5493a272002-01-02 14:15:35 -05001108 if (host_dev == -1) {
Theodore Ts'od9c56d32000-02-08 00:47:55 +00001109 com_err (program_name, errno,
1110 _("while trying to open %s"),
1111 host_device_name);
Theodore Ts'o879ac922000-01-18 20:59:11 +00001112 exit (1);
1113 }
1114 } else
1115 host_dev = dev;
Theodore Ts'o3e699062002-10-13 23:56:28 -04001116 if (input_file) {
Theodore Ts'o879ac922000-01-18 20:59:11 +00001117 if (strcmp (input_file, "-") == 0)
1118 in = stdin;
1119 else {
1120 in = fopen (input_file, "r");
1121 if (in == NULL)
1122 {
Theodore Ts'od9c56d32000-02-08 00:47:55 +00001123 com_err (program_name, errno,
1124 _("while trying to open %s"),
Theodore Ts'o879ac922000-01-18 20:59:11 +00001125 input_file);
1126 exit (1);
1127 }
1128 }
Theodore Ts'o3e699062002-10-13 23:56:28 -04001129 }
Theodore Ts'o3839e651997-04-26 13:21:57 +00001130 if (output_file && strcmp (output_file, "-") != 0)
1131 {
1132 out = fopen (output_file, "w");
1133 if (out == NULL)
1134 {
Theodore Ts'od9c56d32000-02-08 00:47:55 +00001135 com_err (program_name, errno,
1136 _("while trying to open %s"),
Theodore Ts'o879ac922000-01-18 20:59:11 +00001137 output_file);
Theodore Ts'o3839e651997-04-26 13:21:57 +00001138 exit (1);
1139 }
1140 }
1141 else
1142 out = stdout;
Theodore Ts'o879ac922000-01-18 20:59:11 +00001143
1144 errcode = ext2fs_badblocks_list_create(&bb_list,0);
1145 if (errcode) {
Theodore Ts'od9c56d32000-02-08 00:47:55 +00001146 com_err (program_name, errcode,
Theodore Ts'obb145b02005-06-20 08:35:27 -04001147 _("while creating in-memory bad blocks list"));
Theodore Ts'o879ac922000-01-18 20:59:11 +00001148 exit (1);
1149 }
1150
1151 if (in) {
1152 for(;;) {
Theodore Ts'oa551b782000-07-13 22:05:31 +00001153 switch(fscanf (in, "%u\n", &next_bad)) {
Theodore Ts'o879ac922000-01-18 20:59:11 +00001154 case 0:
1155 com_err (program_name, 0, "input file - bad format");
1156 exit (1);
1157 case EOF:
1158 break;
1159 default:
1160 errcode = ext2fs_badblocks_list_add(bb_list,next_bad);
1161 if (errcode) {
Theodore Ts'obb145b02005-06-20 08:35:27 -04001162 com_err (program_name, errcode, _("while adding to in-memory bad block list"));
Theodore Ts'o879ac922000-01-18 20:59:11 +00001163 exit (1);
1164 }
1165 continue;
1166 }
1167 break;
1168 }
1169
1170 if (in != stdin)
1171 fclose (in);
1172 }
1173
1174 do {
1175 unsigned int bb_count;
1176
Theodore Ts'ocd130a02001-05-05 05:43:23 +00001177 bb_count = test_func(dev, last_block, block_size,
Theodore Ts'of56f32b2008-07-10 10:08:40 -04001178 first_block, blocks_at_once);
Theodore Ts'o4d003982000-04-03 16:01:11 +00001179 if (bb_count)
1180 passes_clean = 0;
1181 else
1182 ++passes_clean;
1183
Theodore Ts'o879ac922000-01-18 20:59:11 +00001184 if (v_flag)
Theodore Ts'od9c56d32000-02-08 00:47:55 +00001185 fprintf(stderr,
1186 _("Pass completed, %u bad blocks found.\n"),
1187 bb_count);
Theodore Ts'o879ac922000-01-18 20:59:11 +00001188
1189 } while (passes_clean < num_passes);
1190
Theodore Ts'o3839e651997-04-26 13:21:57 +00001191 close (dev);
1192 if (out != stdout)
1193 fclose (out);
Theodore Ts'o849b6bc2003-05-07 09:52:14 -04001194 if (t_patts)
1195 free(t_patts);
Theodore Ts'o879ac922000-01-18 20:59:11 +00001196 return 0;
Theodore Ts'o3839e651997-04-26 13:21:57 +00001197}
Theodore Ts'od9c56d32000-02-08 00:47:55 +00001198