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