badblocks.c revision 39791dc0bda8c5a995e307e6306b79cd0fded644
1/*
2 * badblocks.c		- Bad blocks checker
3 *
4 * Copyright (C) 1992, 1993, 1994  Remy Card <card@masi.ibp.fr>
5 *                                 Laboratoire MASI, Institut Blaise Pascal
6 *                                 Universite Pierre et Marie Curie (Paris VI)
7 *
8 * Copyright 1995, 1996, 1997, 1998, 1999 by Theodore Ts'o
9 * Copyright 1999 by David Beattie
10 *
11 * This file is based on the minix file system programs fsck and mkfs
12 * written and copyrighted by Linus Torvalds <Linus.Torvalds@cs.helsinki.fi>
13 *
14 * %Begin-Header%
15 * This file may be redistributed under the terms of the GNU Public
16 * License.
17 * %End-Header%
18 */
19
20/*
21 * History:
22 * 93/05/26	- Creation from e2fsck
23 * 94/02/27	- Made a separate bad blocks checker
24 * 99/06/30...99/07/26 - Added non-destructive write-testing,
25 *                       configurable blocks-at-once parameter,
26 * 			 loading of badblocks list to avoid testing
27 * 			 blocks known to be bad, multiple passes to
28 * 			 make sure that no new blocks are added to the
29 * 			 list.  (Work done by David Beattie)
30 */
31
32#define _GNU_SOURCE /* for O_DIRECT */
33
34#ifndef O_LARGEFILE
35#define O_LARGEFILE 0
36#endif
37
38#include <errno.h>
39#include <fcntl.h>
40#ifdef HAVE_GETOPT_H
41#include <getopt.h>
42#else
43extern char *optarg;
44extern int optind;
45#endif
46#include <signal.h>
47#include <stdio.h>
48#include <stdlib.h>
49#include <string.h>
50#include <unistd.h>
51#include <setjmp.h>
52#include <time.h>
53#include <limits.h>
54
55#include <sys/time.h>
56#include <sys/ioctl.h>
57#include <sys/types.h>
58
59#include "et/com_err.h"
60#include "ext2fs/ext2_io.h"
61#include "ext2fs/ext2_fs.h"
62#include "ext2fs/ext2fs.h"
63#include "nls-enable.h"
64
65const char * program_name = "badblocks";
66const char * done_string = N_("done                                \n");
67
68static int v_flag = 0;			/* verbose */
69static int w_flag = 0;			/* do r/w test: 0=no, 1=yes,
70					 * 2=non-destructive */
71static int s_flag = 0;			/* show progress of test */
72static int force = 0;			/* force check of mounted device */
73static int t_flag = 0;			/* number of test patterns */
74static int t_max = 0;			/* allocated test patterns */
75static unsigned int *t_patts = NULL;	/* test patterns */
76static int current_O_DIRECT = 0;	/* Current status of O_DIRECT flag */
77static int use_buffered_io = 0;
78static int exclusive_ok = 0;
79static unsigned int max_bb = 0;		/* Abort test if more than this number of bad blocks has been encountered */
80static unsigned int d_flag = 0;		/* delay factor between reads */
81static struct timeval time_start;
82
83#define T_INC 32
84
85unsigned int sys_page_size = 4096;
86
87static void usage(void)
88{
89	fprintf(stderr, _(
90"Usage: %s [-b block_size] [-i input_file] [-o output_file] [-svwnf]\n"
91"       [-c blocks_at_once] [-d delay_factor_between_reads] [-e max_bad_blocks]\n"
92"       [-p num_passes] [-t test_pattern [-t test_pattern [...]]]\n"
93"       device [last_block [first_block]]\n"),
94		 program_name);
95	exit (1);
96}
97
98static void exclusive_usage(void)
99{
100	fprintf(stderr,
101		_("%s: The -n and -w options are mutually exclusive.\n\n"),
102		program_name);
103	exit(1);
104}
105
106static blk_t currently_testing = 0;
107static blk_t num_blocks = 0;
108static ext2_badblocks_list bb_list = NULL;
109static FILE *out;
110static blk_t next_bad = 0;
111static ext2_badblocks_iterate bb_iter = NULL;
112
113static void *allocate_buffer(size_t size)
114{
115	void	*ret = 0;
116
117#ifdef HAVE_POSIX_MEMALIGN
118	if (posix_memalign(&ret, sys_page_size, size) < 0)
119		ret = 0;
120#else
121#ifdef HAVE_MEMALIGN
122	ret = memalign(sys_page_size, size);
123#else
124#ifdef HAVE_VALLOC
125	ret = valloc(size);
126#endif /* HAVE_VALLOC */
127#endif /* HAVE_MEMALIGN */
128#endif /* HAVE_POSIX_MEMALIGN */
129
130	if (!ret)
131		ret = malloc(size);
132
133	return ret;
134}
135
136/*
137 * This routine reports a new bad block.  If the bad block has already
138 * been seen before, then it returns 0; otherwise it returns 1.
139 */
140static int bb_output (blk_t bad)
141{
142	errcode_t errcode;
143
144	if (ext2fs_badblocks_list_test(bb_list, bad))
145		return 0;
146
147	fprintf(out, "%lu\n", (unsigned long) bad);
148	fflush(out);
149
150	errcode = ext2fs_badblocks_list_add (bb_list, bad);
151	if (errcode) {
152		com_err (program_name, errcode, "adding to in-memory bad block list");
153		exit (1);
154	}
155
156	/* kludge:
157	   increment the iteration through the bb_list if
158	   an element was just added before the current iteration
159	   position.  This should not cause next_bad to change. */
160	if (bb_iter && bad < next_bad)
161		ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
162	return 1;
163}
164
165static char *time_diff_format(struct timeval *tv1,
166			      struct timeval *tv2, char *buf)
167{
168        time_t	diff = (tv1->tv_sec - tv2->tv_sec);
169	int	hr,min,sec;
170
171	sec = diff % 60;
172	diff /= 60;
173	min = diff % 60;
174	hr = diff / 60;
175
176	if (hr)
177		sprintf(buf, "%d:%02d:%02d", hr, min, sec);
178	else
179		sprintf(buf, "%d:%02d", min, sec);
180	return buf;
181}
182
183static float calc_percent(unsigned long current, unsigned long total) {
184	float percent = 0.0;
185	if (total <= 0)
186		return percent;
187	if (current >= total) {
188		percent = 100.0;
189	} else {
190		percent=(100.0*(float)current/(float)total);
191	}
192	return percent;
193}
194
195static void print_status(void)
196{
197	struct timeval time_end;
198	char diff_buf[32], line_buf[128];
199	int len;
200
201	gettimeofday(&time_end, 0);
202	len = snprintf(line_buf, sizeof(line_buf),
203		       _("%6.2f%% done, %s elapsed"),
204		       calc_percent((unsigned long) currently_testing,
205				    (unsigned long) num_blocks),
206		       time_diff_format(&time_end, &time_start, diff_buf));
207#ifdef HAVE_MBSTOWCS
208	len = mbstowcs(NULL, line_buf, sizeof(line_buf));
209#endif
210	fputs(line_buf, stderr);
211	memset(line_buf, '\b', len);
212	line_buf[len] = 0;
213	fputs(line_buf, stderr);
214	fflush (stderr);
215}
216
217static void alarm_intr(int alnum EXT2FS_ATTR((unused)))
218{
219	signal (SIGALRM, alarm_intr);
220	alarm(1);
221	if (!num_blocks)
222		return;
223	print_status();
224}
225
226static void *terminate_addr = NULL;
227
228static void terminate_intr(int signo EXT2FS_ATTR((unused)))
229{
230	fflush(out);
231	fprintf(stderr, "\n\nInterrupted at block %llu\n",
232		(unsigned long long) currently_testing);
233	fflush(stderr);
234	if (terminate_addr)
235		longjmp(terminate_addr,1);
236	exit(1);
237}
238
239static void capture_terminate(jmp_buf term_addr)
240{
241	terminate_addr = term_addr;
242	signal (SIGHUP, terminate_intr);
243	signal (SIGINT, terminate_intr);
244	signal (SIGPIPE, terminate_intr);
245	signal (SIGTERM, terminate_intr);
246	signal (SIGUSR1, terminate_intr);
247	signal (SIGUSR2, terminate_intr);
248}
249
250static void uncapture_terminate(void)
251{
252	terminate_addr = NULL;
253	signal (SIGHUP, SIG_DFL);
254	signal (SIGINT, SIG_DFL);
255	signal (SIGPIPE, SIG_DFL);
256	signal (SIGTERM, SIG_DFL);
257	signal (SIGUSR1, SIG_DFL);
258	signal (SIGUSR2, SIG_DFL);
259}
260
261/* Linux requires that O_DIRECT I/Os be 512-byte sector aligned */
262
263#define O_DIRECT_SIZE 512
264
265static void set_o_direct(int dev, unsigned char *buffer, size_t size,
266			 ext2_loff_t offset)
267{
268#ifdef O_DIRECT
269	int new_flag = O_DIRECT;
270	int flag;
271
272	if ((use_buffered_io != 0) ||
273	    (((unsigned long) buffer & (sys_page_size - 1)) != 0) ||
274	    ((size & (sys_page_size - 1)) != 0) ||
275	    ((offset & (O_DIRECT_SIZE - 1)) != 0))
276		new_flag = 0;
277
278	if (new_flag != current_O_DIRECT) {
279	     /* printf("%s O_DIRECT\n", new_flag ? "Setting" : "Clearing"); */
280		flag = fcntl(dev, F_GETFL);
281		if (flag > 0) {
282			flag = (flag & ~O_DIRECT) | new_flag;
283			fcntl(dev, F_SETFL, flag);
284		}
285		current_O_DIRECT = new_flag;
286	}
287#endif
288}
289
290
291static void pattern_fill(unsigned char *buffer, unsigned int pattern,
292			 size_t n)
293{
294	unsigned int	i, nb;
295	unsigned char	bpattern[sizeof(pattern)], *ptr;
296
297	if (pattern == (unsigned int) ~0) {
298		for (ptr = buffer; ptr < buffer + n; ptr++) {
299			(*ptr) = random() % (1 << (8 * sizeof(char)));
300		}
301		if (s_flag | v_flag)
302			fputs(_("Testing with random pattern: "), stderr);
303	} else {
304		bpattern[0] = 0;
305		for (i = 0; i < sizeof(bpattern); i++) {
306			if (pattern == 0)
307				break;
308			bpattern[i] = pattern & 0xFF;
309			pattern = pattern >> 8;
310		}
311		nb = i ? (i-1) : 0;
312		for (ptr = buffer, i = nb; ptr < buffer + n; ptr++) {
313			*ptr = bpattern[i];
314			if (i == 0)
315				i = nb;
316			else
317				i--;
318		}
319		if (s_flag | v_flag) {
320			fputs(_("Testing with pattern 0x"), stderr);
321			for (i = 0; i <= nb; i++)
322				fprintf(stderr, "%02x", buffer[i]);
323			fputs(": ", stderr);
324		}
325	}
326}
327
328/*
329 * Perform a read of a sequence of blocks; return the number of blocks
330 *    successfully sequentially read.
331 */
332static int do_read (int dev, unsigned char * buffer, int try, int block_size,
333		    blk_t current_block)
334{
335	long got;
336	struct timeval tv1, tv2;
337#define NANOSEC (1000000000L)
338#define MILISEC (1000L)
339
340#if 0
341	printf("do_read: block %d, try %d\n", current_block, try);
342#endif
343	set_o_direct(dev, buffer, try * block_size,
344		     ((ext2_loff_t) current_block) * block_size);
345
346	if (v_flag > 1)
347		print_status();
348
349	/* Seek to the correct loc. */
350	if (ext2fs_llseek (dev, (ext2_loff_t) current_block * block_size,
351			 SEEK_SET) != (ext2_loff_t) current_block * block_size)
352		com_err (program_name, errno, _("during seek"));
353
354	/* Try the read */
355	if (d_flag)
356		gettimeofday(&tv1, NULL);
357	got = read (dev, buffer, try * block_size);
358	if (d_flag)
359		gettimeofday(&tv2, NULL);
360	if (got < 0)
361		got = 0;
362	if (got & 511)
363		fprintf(stderr, _("Weird value (%ld) in do_read\n"), got);
364	got /= block_size;
365	if (d_flag && got == try) {
366#ifdef HAVE_NANOSLEEP
367		struct timespec ts;
368		ts.tv_sec = tv2.tv_sec - tv1.tv_sec;
369		ts.tv_nsec = (tv2.tv_usec - tv1.tv_usec) * MILISEC;
370		if (ts.tv_nsec < 0) {
371			ts.tv_nsec += NANOSEC;
372			ts.tv_sec -= 1;
373		}
374		/* increase/decrease the sleep time based on d_flag value */
375		ts.tv_sec = ts.tv_sec * d_flag / 100;
376		ts.tv_nsec = ts.tv_nsec * d_flag / 100;
377		if (ts.tv_nsec > NANOSEC) {
378			ts.tv_sec += ts.tv_nsec / NANOSEC;
379			ts.tv_nsec %= NANOSEC;
380		}
381		if (ts.tv_sec || ts.tv_nsec)
382			nanosleep(&ts, NULL);
383#else
384#ifdef HAVE_USLEEP
385		struct timeval tv;
386		tv.tv_sec = tv2.tv_sec - tv1.tv_sec;
387		tv.tv_usec = tv2.tv_usec - tv1.tv_usec;
388		tv.tv_sec = tv.tv_sec * d_flag / 100;
389		tv.tv_usec = tv.tv_usec * d_flag / 100;
390		if (tv.tv_usec > 1000000) {
391			tv.tv_sec += tv.tv_usec / 1000000;
392			tv.tv_usec %= 1000000;
393		}
394		if (tv.tv_sec)
395			sleep(tv.tv_sec);
396		if (tv.tv_usec)
397			usleep(tv.tv_usec);
398#endif
399#endif
400	}
401	return got;
402}
403
404/*
405 * Perform a write of a sequence of blocks; return the number of blocks
406 *    successfully sequentially written.
407 */
408static int do_write(int dev, unsigned char * buffer, int try, int block_size,
409		    unsigned long current_block)
410{
411	long got;
412
413#if 0
414	printf("do_write: block %lu, try %d\n", current_block, try);
415#endif
416	set_o_direct(dev, buffer, try * block_size,
417		     ((ext2_loff_t) current_block) * block_size);
418
419	if (v_flag > 1)
420		print_status();
421
422	/* Seek to the correct loc. */
423	if (ext2fs_llseek (dev, (ext2_loff_t) current_block * block_size,
424			 SEEK_SET) != (ext2_loff_t) current_block * block_size)
425		com_err (program_name, errno, _("during seek"));
426
427	/* Try the write */
428	got = write (dev, buffer, try * block_size);
429	if (got < 0)
430		got = 0;
431	if (got & 511)
432		fprintf(stderr, "Weird value (%ld) in do_write\n", got);
433	got /= block_size;
434	return got;
435}
436
437static int host_dev;
438
439static void flush_bufs(void)
440{
441	errcode_t	retval;
442
443#ifdef O_DIRECT
444	if (!use_buffered_io)
445		return;
446#endif
447	retval = ext2fs_sync_device(host_dev, 1);
448	if (retval)
449		com_err(program_name, retval, _("during ext2fs_sync_device"));
450}
451
452static unsigned int test_ro (int dev, blk_t last_block,
453			     int block_size, blk_t first_block,
454			     unsigned int blocks_at_once)
455{
456	unsigned char * blkbuf;
457	int try;
458	int got;
459	unsigned int bb_count = 0;
460	errcode_t errcode;
461	blk_t recover_block = ~0;
462
463	/* set up abend handler */
464	capture_terminate(NULL);
465
466	errcode = ext2fs_badblocks_list_iterate_begin(bb_list,&bb_iter);
467	if (errcode) {
468		com_err (program_name, errcode,
469			 _("while beginning bad block list iteration"));
470		exit (1);
471	}
472	do {
473		ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
474	} while (next_bad && next_bad < first_block);
475
476	if (t_flag) {
477		blkbuf = allocate_buffer((blocks_at_once + 1) * block_size);
478	} else {
479		blkbuf = allocate_buffer(blocks_at_once * block_size);
480	}
481	if (!blkbuf)
482	{
483		com_err (program_name, ENOMEM, _("while allocating buffers"));
484		exit (1);
485	}
486	if (v_flag) {
487		fprintf (stderr, _("Checking blocks %lu to %lu\n"),
488			 (unsigned long) first_block,
489			 (unsigned long) last_block - 1);
490	}
491	if (t_flag) {
492		fputs(_("Checking for bad blocks in read-only mode\n"), stderr);
493		pattern_fill(blkbuf + blocks_at_once * block_size,
494			     t_patts[0], block_size);
495	}
496	flush_bufs();
497	try = blocks_at_once;
498	currently_testing = first_block;
499	num_blocks = last_block - 1;
500	if (!t_flag && (s_flag || v_flag)) {
501		fputs(_("Checking for bad blocks (read-only test): "), stderr);
502		if (v_flag <= 1)
503			alarm_intr(SIGALRM);
504	}
505	while (currently_testing < last_block)
506	{
507		if (max_bb && bb_count >= max_bb) {
508			if (s_flag || v_flag) {
509				fputs(_("Too many bad blocks, aborting test\n"), stderr);
510			}
511			break;
512		}
513		if (next_bad) {
514			if (currently_testing == next_bad) {
515				/* fprintf (out, "%lu\n", nextbad); */
516				ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
517				currently_testing++;
518				continue;
519			}
520			else if (currently_testing + try > next_bad)
521				try = next_bad - currently_testing;
522		}
523		if (currently_testing + try > last_block)
524			try = last_block - currently_testing;
525		got = do_read (dev, blkbuf, try, block_size, currently_testing);
526		if (t_flag) {
527			/* test the comparison between all the
528			   blocks successfully read  */
529			int i;
530			for (i = 0; i < got; ++i)
531				if (memcmp (blkbuf+i*block_size,
532					    blkbuf+blocks_at_once*block_size,
533					    block_size))
534					bb_count += bb_output(currently_testing + i);
535		}
536		if (got == 0 && try == 1)
537			bb_count += bb_output(currently_testing++);
538		currently_testing += got;
539		if (got != try) {
540			try = 1;
541			if (recover_block == ~0)
542				recover_block = currently_testing - got +
543					blocks_at_once;
544			continue;
545		} else if (currently_testing == recover_block) {
546			try = blocks_at_once;
547			recover_block = ~0;
548		}
549	}
550	num_blocks = 0;
551	alarm(0);
552	if (s_flag || v_flag)
553		fputs(_(done_string), stderr);
554
555	fflush (stderr);
556	free (blkbuf);
557
558	ext2fs_badblocks_list_iterate_end(bb_iter);
559
560	uncapture_terminate();
561
562	return bb_count;
563}
564
565static unsigned int test_rw (int dev, blk_t last_block,
566			     int block_size, blk_t first_block,
567			     unsigned int blocks_at_once)
568{
569	unsigned char *buffer, *read_buffer;
570	const unsigned int patterns[] = {0xaa, 0x55, 0xff, 0x00};
571	const unsigned int *pattern;
572	int i, try, got, nr_pattern, pat_idx;
573	unsigned int bb_count = 0;
574	blk_t recover_block = ~0;
575
576	/* set up abend handler */
577	capture_terminate(NULL);
578
579	buffer = allocate_buffer(2 * blocks_at_once * block_size);
580	read_buffer = buffer + blocks_at_once * block_size;
581
582	if (!buffer) {
583		com_err (program_name, ENOMEM, _("while allocating buffers"));
584		exit (1);
585	}
586
587	flush_bufs();
588
589	if (v_flag) {
590		fputs(_("Checking for bad blocks in read-write mode\n"),
591		      stderr);
592		fprintf(stderr, _("From block %lu to %lu\n"),
593			(unsigned long) first_block,
594			(unsigned long) last_block - 1);
595	}
596	if (t_flag) {
597		pattern = t_patts;
598		nr_pattern = t_flag;
599	} else {
600		pattern = patterns;
601		nr_pattern = sizeof(patterns) / sizeof(patterns[0]);
602	}
603	for (pat_idx = 0; pat_idx < nr_pattern; pat_idx++) {
604		pattern_fill(buffer, pattern[pat_idx],
605			     blocks_at_once * block_size);
606		num_blocks = last_block - 1;
607		currently_testing = first_block;
608		if (s_flag && v_flag <= 1)
609			alarm_intr(SIGALRM);
610
611		try = blocks_at_once;
612		while (currently_testing < last_block) {
613			if (max_bb && bb_count >= max_bb) {
614				if (s_flag || v_flag) {
615					fputs(_("Too many bad blocks, aborting test\n"), stderr);
616				}
617				break;
618			}
619			if (currently_testing + try > last_block)
620				try = last_block - currently_testing;
621			got = do_write(dev, buffer, try, block_size,
622					currently_testing);
623			if (v_flag > 1)
624				print_status();
625
626			if (got == 0 && try == 1)
627				bb_count += bb_output(currently_testing++);
628			currently_testing += got;
629			if (got != try) {
630				try = 1;
631				if (recover_block == ~0)
632					recover_block = currently_testing -
633						got + blocks_at_once;
634				continue;
635			} else if (currently_testing == recover_block) {
636				try = blocks_at_once;
637				recover_block = ~0;
638			}
639		}
640
641		num_blocks = 0;
642		alarm (0);
643		if (s_flag | v_flag)
644			fputs(_(done_string), stderr);
645		flush_bufs();
646		if (s_flag | v_flag)
647			fputs(_("Reading and comparing: "), stderr);
648		num_blocks = last_block;
649		currently_testing = first_block;
650		if (s_flag && v_flag <= 1)
651			alarm_intr(SIGALRM);
652
653		try = blocks_at_once;
654		while (currently_testing < last_block) {
655			if (max_bb && bb_count >= max_bb) {
656				if (s_flag || v_flag) {
657					fputs(_("Too many bad blocks, aborting test\n"), stderr);
658				}
659				break;
660			}
661			if (currently_testing + try > last_block)
662				try = last_block - currently_testing;
663			got = do_read (dev, read_buffer, try, block_size,
664				       currently_testing);
665			if (got == 0 && try == 1)
666				bb_count += bb_output(currently_testing++);
667			currently_testing += got;
668			if (got != try) {
669				try = 1;
670				if (recover_block == ~0)
671					recover_block = currently_testing -
672						got + blocks_at_once;
673				continue;
674			} else if (currently_testing == recover_block) {
675				try = blocks_at_once;
676				recover_block = ~0;
677			}
678			for (i=0; i < got; i++) {
679				if (memcmp(read_buffer + i * block_size,
680					   buffer + i * block_size,
681					   block_size))
682					bb_count += bb_output(currently_testing+i);
683			}
684			if (v_flag > 1)
685				print_status();
686		}
687
688		num_blocks = 0;
689		alarm (0);
690		if (s_flag | v_flag)
691			fputs(_(done_string), stderr);
692		flush_bufs();
693	}
694	uncapture_terminate();
695	free(buffer);
696	return bb_count;
697}
698
699struct saved_blk_record {
700	blk_t	block;
701	int	num;
702};
703
704static unsigned int test_nd (int dev, blk_t last_block,
705			     int block_size, blk_t first_block,
706			     unsigned int blocks_at_once)
707{
708	unsigned char *blkbuf, *save_ptr, *test_ptr, *read_ptr;
709	unsigned char *test_base, *save_base, *read_base;
710	int try, i;
711	const unsigned int patterns[] = { ~0 };
712	const unsigned int *pattern;
713	int nr_pattern, pat_idx;
714	int got, used2, written;
715	blk_t save_currently_testing;
716	struct saved_blk_record *test_record;
717	/* This is static to prevent being clobbered by the longjmp */
718	static int num_saved;
719	jmp_buf terminate_env;
720	errcode_t errcode;
721	unsigned long buf_used;
722	static unsigned int bb_count;
723	int granularity = blocks_at_once;
724	blk_t recover_block = ~0;
725
726	bb_count = 0;
727	errcode = ext2fs_badblocks_list_iterate_begin(bb_list,&bb_iter);
728	if (errcode) {
729		com_err (program_name, errcode,
730			 _("while beginning bad block list iteration"));
731		exit (1);
732	}
733	do {
734		ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
735	} while (next_bad && next_bad < first_block);
736
737	blkbuf = allocate_buffer(3 * blocks_at_once * block_size);
738	test_record = malloc (blocks_at_once*sizeof(struct saved_blk_record));
739	if (!blkbuf || !test_record) {
740		com_err(program_name, ENOMEM, _("while allocating buffers"));
741		exit (1);
742	}
743
744	save_base = blkbuf;
745	test_base = blkbuf + (blocks_at_once * block_size);
746	read_base = blkbuf + (2 * blocks_at_once * block_size);
747
748	num_saved = 0;
749
750	flush_bufs();
751	if (v_flag) {
752	    fputs(_("Checking for bad blocks in non-destructive read-write mode\n"), stderr);
753	    fprintf (stderr, _("From block %lu to %lu\n"),
754		     (unsigned long) first_block,
755		     (unsigned long) last_block - 1);
756	}
757	if (s_flag || v_flag > 1) {
758		fputs(_("Checking for bad blocks (non-destructive read-write test)\n"), stderr);
759	}
760	if (setjmp(terminate_env)) {
761		/*
762		 * Abnormal termination by a signal is handled here.
763		 */
764		signal (SIGALRM, SIG_IGN);
765		fputs(_("\nInterrupt caught, cleaning up\n"), stderr);
766
767		save_ptr = save_base;
768		for (i=0; i < num_saved; i++) {
769			do_write(dev, save_ptr, test_record[i].num,
770				 block_size, test_record[i].block);
771			save_ptr += test_record[i].num * block_size;
772		}
773		fflush (out);
774		exit(1);
775	}
776
777	/* set up abend handler */
778	capture_terminate(terminate_env);
779
780	if (t_flag) {
781		pattern = t_patts;
782		nr_pattern = t_flag;
783	} else {
784		pattern = patterns;
785		nr_pattern = sizeof(patterns) / sizeof(patterns[0]);
786	}
787	for (pat_idx = 0; pat_idx < nr_pattern; pat_idx++) {
788		pattern_fill(test_base, pattern[pat_idx],
789			     blocks_at_once * block_size);
790
791		buf_used = 0;
792		bb_count = 0;
793		save_ptr = save_base;
794		test_ptr = test_base;
795		currently_testing = first_block;
796		num_blocks = last_block - 1;
797		if (s_flag && v_flag <= 1)
798			alarm_intr(SIGALRM);
799
800		while (currently_testing < last_block) {
801			if (max_bb && bb_count >= max_bb) {
802				if (s_flag || v_flag) {
803					fputs(_("Too many bad blocks, aborting test\n"), stderr);
804				}
805				break;
806			}
807			got = try = granularity - buf_used;
808			if (next_bad) {
809				if (currently_testing == next_bad) {
810					/* fprintf (out, "%lu\n", nextbad); */
811					ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
812					currently_testing++;
813					goto check_for_more;
814				}
815				else if (currently_testing + try > next_bad)
816					try = next_bad - currently_testing;
817			}
818			if (currently_testing + try > last_block)
819				try = last_block - currently_testing;
820			got = do_read (dev, save_ptr, try, block_size,
821				       currently_testing);
822			if (got == 0) {
823				if (recover_block == ~0)
824					recover_block = currently_testing +
825						blocks_at_once;
826				if (granularity != 1) {
827					granularity = 1;
828					continue;
829				}
830				/* First block must have been bad. */
831				bb_count += bb_output(currently_testing++);
832				goto check_for_more;
833			}
834
835			/*
836			 * Note the fact that we've saved this much data
837			 * *before* we overwrite it with test data
838			 */
839			test_record[num_saved].block = currently_testing;
840			test_record[num_saved].num = got;
841			num_saved++;
842
843			/* Write the test data */
844			written = do_write (dev, test_ptr, got, block_size,
845					    currently_testing);
846			if (written != got)
847				com_err (program_name, errno,
848					 _("during test data write, block %lu"),
849					 (unsigned long) currently_testing +
850					 written);
851
852			buf_used += got;
853			save_ptr += got * block_size;
854			test_ptr += got * block_size;
855			currently_testing += got;
856			if (got != try) {
857				try = 1;
858				if (recover_block == ~0)
859					recover_block = currently_testing -
860						got + blocks_at_once;
861				continue;
862			}
863
864		check_for_more:
865			/*
866			 * If there's room for more blocks to be tested this
867			 * around, and we're not done yet testing the disk, go
868			 * back and get some more blocks.
869			 */
870			if ((buf_used != granularity) &&
871			    (currently_testing < last_block))
872				continue;
873
874			if (currently_testing >= recover_block) {
875				granularity = blocks_at_once;
876				recover_block = ~0;
877			}
878
879			flush_bufs();
880			save_currently_testing = currently_testing;
881
882			/*
883			 * for each contiguous block that we read into the
884			 * buffer (and wrote test data into afterwards), read
885			 * it back (looping if necessary, to get past newly
886			 * discovered unreadable blocks, of which there should
887			 * be none, but with a hard drive which is unreliable,
888			 * it has happened), and compare with the test data
889			 * that was written; output to the bad block list if
890			 * it doesn't match.
891			 */
892			used2 = 0;
893			save_ptr = save_base;
894			test_ptr = test_base;
895			read_ptr = read_base;
896			try = 0;
897
898			while (1) {
899				if (try == 0) {
900					if (used2 >= num_saved)
901						break;
902					currently_testing = test_record[used2].block;
903					try = test_record[used2].num;
904					used2++;
905				}
906
907				got = do_read (dev, read_ptr, try,
908					       block_size, currently_testing);
909
910				/* test the comparison between all the
911				   blocks successfully read  */
912				for (i = 0; i < got; ++i)
913					if (memcmp (test_ptr+i*block_size,
914						    read_ptr+i*block_size, block_size))
915						bb_count += bb_output(currently_testing + i);
916				if (got < try) {
917					bb_count += bb_output(currently_testing + got);
918					got++;
919				}
920
921				/* write back original data */
922				do_write (dev, save_ptr, got,
923					  block_size, currently_testing);
924				save_ptr += got * block_size;
925
926				currently_testing += got;
927				test_ptr += got * block_size;
928				read_ptr += got * block_size;
929				try -= got;
930			}
931
932			/* empty the buffer so it can be reused */
933			num_saved = 0;
934			buf_used = 0;
935			save_ptr = save_base;
936			test_ptr = test_base;
937			currently_testing = save_currently_testing;
938		}
939		num_blocks = 0;
940		alarm(0);
941		if (s_flag || v_flag > 1)
942			fputs(_(done_string), stderr);
943
944		flush_bufs();
945	}
946	uncapture_terminate();
947	fflush(stderr);
948	free(blkbuf);
949	free(test_record);
950
951	ext2fs_badblocks_list_iterate_end(bb_iter);
952
953	return bb_count;
954}
955
956static void check_mount(char *device_name)
957{
958	errcode_t	retval;
959	int		mount_flags;
960
961	retval = ext2fs_check_if_mounted(device_name, &mount_flags);
962	if (retval) {
963		com_err("ext2fs_check_if_mount", retval,
964			_("while determining whether %s is mounted."),
965			device_name);
966		return;
967	}
968	if (mount_flags & EXT2_MF_MOUNTED) {
969		fprintf(stderr, _("%s is mounted; "), device_name);
970		if (force) {
971			fputs(_("badblocks forced anyway.  "
972				"Hope /etc/mtab is incorrect.\n"), stderr);
973			return;
974		}
975	abort_badblocks:
976		fputs(_("it's not safe to run badblocks!\n"), stderr);
977		exit(1);
978	}
979
980	if ((mount_flags & EXT2_MF_BUSY) && !exclusive_ok) {
981		fprintf(stderr, _("%s is apparently in use by the system; "),
982			device_name);
983		if (force)
984			fputs(_("badblocks forced anyway.\n"), stderr);
985		else
986			goto abort_badblocks;
987	}
988
989}
990
991/*
992 * This function will convert a string to an unsigned long, printing
993 * an error message if it fails, and returning success or failure in err.
994 */
995static unsigned int parse_uint(const char *str, const char *descr)
996{
997	char		*tmp;
998	unsigned long	ret;
999
1000	errno = 0;
1001	ret = strtoul(str, &tmp, 0);
1002	if (*tmp || errno || (ret > UINT_MAX) ||
1003	    (ret == ULONG_MAX && errno == ERANGE)) {
1004		com_err (program_name, 0, _("invalid %s - %s"), descr, str);
1005		exit (1);
1006	}
1007	return ret;
1008}
1009
1010int main (int argc, char ** argv)
1011{
1012	int c;
1013	char * device_name;
1014	char * host_device_name = NULL;
1015	char * input_file = NULL;
1016	char * output_file = NULL;
1017	FILE * in = NULL;
1018	int block_size = 1024;
1019	unsigned int blocks_at_once = 64;
1020	blk_t last_block, first_block;
1021	int num_passes = 0;
1022	int passes_clean = 0;
1023	int dev;
1024	errcode_t errcode;
1025	unsigned int pattern;
1026	unsigned int (*test_func)(int, blk_t,
1027				  int, blk_t,
1028				  unsigned int);
1029	int open_flag;
1030	long sysval;
1031
1032	setbuf(stdout, NULL);
1033	setbuf(stderr, NULL);
1034#ifdef ENABLE_NLS
1035	setlocale(LC_MESSAGES, "");
1036	setlocale(LC_CTYPE, "");
1037	bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
1038	textdomain(NLS_CAT_NAME);
1039#endif
1040	srandom((unsigned int)time(NULL));  /* simple randomness is enough */
1041	test_func = test_ro;
1042
1043	/* Determine the system page size if possible */
1044#ifdef HAVE_SYSCONF
1045#if (!defined(_SC_PAGESIZE) && defined(_SC_PAGE_SIZE))
1046#define _SC_PAGESIZE _SC_PAGE_SIZE
1047#endif
1048#ifdef _SC_PAGESIZE
1049	sysval = sysconf(_SC_PAGESIZE);
1050	if (sysval > 0)
1051		sys_page_size = sysval;
1052#endif /* _SC_PAGESIZE */
1053#endif /* HAVE_SYSCONF */
1054
1055	if (argc && *argv)
1056		program_name = *argv;
1057	while ((c = getopt (argc, argv, "b:d:e:fi:o:svwnc:p:h:t:BX")) != EOF) {
1058		switch (c) {
1059		case 'b':
1060			block_size = parse_uint(optarg, "block size");
1061			break;
1062		case 'f':
1063			force++;
1064			break;
1065		case 'i':
1066			input_file = optarg;
1067			break;
1068		case 'o':
1069			output_file = optarg;
1070			break;
1071		case 's':
1072			s_flag = 1;
1073			break;
1074		case 'v':
1075			v_flag++;
1076			break;
1077		case 'w':
1078			if (w_flag)
1079				exclusive_usage();
1080			test_func = test_rw;
1081			w_flag = 1;
1082			break;
1083		case 'n':
1084			if (w_flag)
1085				exclusive_usage();
1086			test_func = test_nd;
1087			w_flag = 2;
1088			break;
1089		case 'c':
1090			blocks_at_once = parse_uint(optarg, "blocks at once");
1091			break;
1092		case 'e':
1093			max_bb = parse_uint(optarg, "max bad block count");
1094			break;
1095		case 'd':
1096			d_flag = parse_uint(optarg, "read delay factor");
1097			break;
1098		case 'p':
1099			num_passes = parse_uint(optarg,
1100						"number of clean passes");
1101			break;
1102		case 'h':
1103			host_device_name = optarg;
1104			break;
1105		case 't':
1106			if (t_flag + 1 > t_max) {
1107				unsigned int *t_patts_new;
1108
1109				t_patts_new = realloc(t_patts, sizeof(int) *
1110						      (t_max + T_INC));
1111				if (!t_patts_new) {
1112					com_err(program_name, ENOMEM,
1113						_("can't allocate memory for "
1114						  "test_pattern - %s"),
1115						optarg);
1116					exit(1);
1117				}
1118				t_patts = t_patts_new;
1119				t_max += T_INC;
1120			}
1121			if (!strcmp(optarg, "r") || !strcmp(optarg,"random")) {
1122				t_patts[t_flag++] = ~0;
1123			} else {
1124				pattern = parse_uint(optarg, "test pattern");
1125				if (pattern == (unsigned int) ~0)
1126					pattern = 0xffff;
1127				t_patts[t_flag++] = pattern;
1128			}
1129			break;
1130		case 'B':
1131			use_buffered_io = 1;
1132			break;
1133		case 'X':
1134			exclusive_ok++;
1135			break;
1136		default:
1137			usage();
1138		}
1139	}
1140	if (!w_flag) {
1141		if (t_flag > 1) {
1142			com_err(program_name, 0,
1143			_("Maximum of one test_pattern may be specified "
1144			  "in read-only mode"));
1145			exit(1);
1146		}
1147		if (t_patts && (t_patts[0] == (unsigned int) ~0)) {
1148			com_err(program_name, 0,
1149			_("Random test_pattern is not allowed "
1150			  "in read-only mode"));
1151			exit(1);
1152		}
1153	}
1154	if (optind > argc - 1)
1155		usage();
1156	device_name = argv[optind++];
1157	if (optind > argc - 1) {
1158		errcode = ext2fs_get_device_size(device_name,
1159						 block_size,
1160						 &last_block);
1161		if (errcode == EXT2_ET_UNIMPLEMENTED) {
1162			com_err(program_name, 0,
1163				_("Couldn't determine device size; you "
1164				  "must specify\nthe size manually\n"));
1165			exit(1);
1166		}
1167		if (errcode) {
1168			com_err(program_name, errcode,
1169				_("while trying to determine device size"));
1170			exit(1);
1171		}
1172	} else {
1173		errno = 0;
1174		last_block = parse_uint(argv[optind], _("last block"));
1175		last_block++;
1176		optind++;
1177	}
1178	if (optind <= argc-1) {
1179		errno = 0;
1180		first_block = parse_uint(argv[optind], _("first block"));
1181	} else first_block = 0;
1182	if (first_block >= last_block) {
1183	    com_err (program_name, 0, _("invalid starting block (%lu): must be less than %lu"),
1184		     (unsigned long) first_block, (unsigned long) last_block);
1185	    exit (1);
1186	}
1187	if (w_flag)
1188		check_mount(device_name);
1189
1190	gettimeofday(&time_start, 0);
1191	open_flag = O_LARGEFILE | (w_flag ? O_RDWR : O_RDONLY);
1192	dev = open (device_name, open_flag);
1193	if (dev == -1) {
1194		com_err (program_name, errno, _("while trying to open %s"),
1195			 device_name);
1196		exit (1);
1197	}
1198	if (host_device_name) {
1199		host_dev = open (host_device_name, open_flag);
1200		if (host_dev == -1) {
1201			com_err (program_name, errno,
1202				 _("while trying to open %s"),
1203				 host_device_name);
1204			exit (1);
1205		}
1206	} else
1207		host_dev = dev;
1208	if (input_file) {
1209		if (strcmp (input_file, "-") == 0)
1210			in = stdin;
1211		else {
1212			in = fopen (input_file, "r");
1213			if (in == NULL)
1214			{
1215				com_err (program_name, errno,
1216					 _("while trying to open %s"),
1217					 input_file);
1218				exit (1);
1219			}
1220		}
1221	}
1222	if (output_file && strcmp (output_file, "-") != 0)
1223	{
1224		out = fopen (output_file, "w");
1225		if (out == NULL)
1226		{
1227			com_err (program_name, errno,
1228				 _("while trying to open %s"),
1229				 output_file);
1230			exit (1);
1231		}
1232	}
1233	else
1234		out = stdout;
1235
1236	errcode = ext2fs_badblocks_list_create(&bb_list,0);
1237	if (errcode) {
1238		com_err (program_name, errcode,
1239			 _("while creating in-memory bad blocks list"));
1240		exit (1);
1241	}
1242
1243	if (in) {
1244		for(;;) {
1245			switch(fscanf (in, "%u\n", &next_bad)) {
1246				case 0:
1247					com_err (program_name, 0, "input file - bad format");
1248					exit (1);
1249				case EOF:
1250					break;
1251				default:
1252					errcode = ext2fs_badblocks_list_add(bb_list,next_bad);
1253					if (errcode) {
1254						com_err (program_name, errcode, _("while adding to in-memory bad block list"));
1255						exit (1);
1256					}
1257					continue;
1258			}
1259			break;
1260		}
1261
1262		if (in != stdin)
1263			fclose (in);
1264	}
1265
1266	do {
1267		unsigned int bb_count;
1268
1269		bb_count = test_func(dev, last_block, block_size,
1270				     first_block, blocks_at_once);
1271		if (bb_count)
1272			passes_clean = 0;
1273		else
1274			++passes_clean;
1275
1276		if (v_flag)
1277			fprintf(stderr,
1278				_("Pass completed, %u bad blocks found.\n"),
1279				bb_count);
1280
1281	} while (passes_clean < num_passes);
1282
1283	close (dev);
1284	if (out != stdout)
1285		fclose (out);
1286	free(t_patts);
1287	return 0;
1288}
1289