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