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