badblocks.c revision d40259fd552d942903f2fd0b426c75a5c2516017
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 by Theodore Ts'o
9 *
10 * This file is based on the minix file system programs fsck and mkfs
11 * written and copyrighted by Linus Torvalds <Linus.Torvalds@cs.helsinki.fi>
12 *
13 * %Begin-Header%
14 * This file may be redistributed under the terms of the GNU Public
15 * License.
16 * %End-Header%
17 */
18
19/*
20 * History:
21 * 93/05/26	- Creation from e2fsck
22 * 94/02/27	- Made a separate bad blocks checker
23 */
24
25#include <errno.h>
26#include <fcntl.h>
27#ifdef HAVE_GETOPT_H
28#include <getopt.h>
29#endif
30#include <signal.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34#include <unistd.h>
35
36#include <sys/ioctl.h>
37#include <sys/types.h>
38
39#if HAVE_LINUX_FS_H
40#include <linux/fd.h>
41#include <linux/fs.h>
42#endif
43
44#include "et/com_err.h"
45#include "ext2fs/ext2_io.h"
46
47const char * program_name = "badblocks";
48
49int v_flag = 0;			/* verbose */
50int w_flag = 0;			/* do r/w test */
51int s_flag = 0;			/* show progress of test */
52
53static volatile void usage (void)
54{
55	fprintf (stderr, "Usage: %s [-b block_size] [-o output_file] [-svw] device blocks_count\n [start_count]\n",
56		 program_name);
57	exit (1);
58}
59
60static unsigned long currently_testing = 0;
61static unsigned long num_blocks = 0;
62
63static void print_status(void)
64{
65	fprintf(stderr, "%9ld/%9ld", currently_testing, num_blocks);
66	fprintf(stderr, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
67	fflush (stderr);
68}
69
70static void alarm_intr (int alnum)
71{
72	signal (SIGALRM, alarm_intr);
73	alarm(1);
74	if (!num_blocks)
75		return;
76	fprintf(stderr, "%9ld/%9ld", currently_testing, num_blocks);
77	fprintf(stderr, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
78	fflush (stderr);
79}
80
81/*
82 * Perform a test of a block; return the number of blocks readable/writeable.
83 */
84static long do_test (int dev, char * buffer, int try, unsigned long block_size,
85		     unsigned long current_block)
86{
87	long got;
88
89	if (v_flag > 1)
90		print_status();
91
92	/* Seek to the correct loc. */
93	if (ext2fs_llseek (dev, (ext2_loff_t) current_block * block_size,
94			 SEEK_SET) != (ext2_loff_t) current_block * block_size)
95		com_err (program_name, errno, "during seek");
96
97	/* Try the read */
98	got = read (dev, buffer, try * block_size);
99	if (got < 0)
100		got = 0;
101	if (got & (block_size - 1))
102		fprintf (stderr,
103			 "Weird value (%ld) in do_test: probably bugs\n",
104			 got);
105	got /= block_size;
106	return got;
107}
108
109static void flush_bufs (int dev, int sync)
110{
111  if (v_flag
112#if !defined (BLKFLSBUF) && !defined (FDFLUSH)
113      && sync
114#endif
115      )
116    fprintf (stderr, "Flushing buffers\n");
117
118  if (sync && fsync (dev) == -1)
119    com_err (program_name, errno, "during fsync");
120
121#ifdef BLKLSBUF
122  ioctl (dev, BLKFLSBUF, 0);	/* In case this is a HD */
123#endif
124#ifdef FDFLUSH
125  ioctl (dev, FDFLUSH, 0);	/* In case this is floppy */
126#endif
127}
128
129static void test_ro (int dev, unsigned long blocks_count,
130		     unsigned long block_size, FILE * out,
131		     unsigned long from_count)
132{
133#define TEST_BUFFER_BLOCKS 16
134	char * blkbuf;
135	int try;
136	long got;
137
138	blkbuf = malloc (TEST_BUFFER_BLOCKS * block_size);
139	if (!blkbuf)
140	{
141		com_err (program_name, ENOMEM, "while allocating buffers");
142		exit (1);
143	}
144	flush_bufs (dev, 0);
145	if (v_flag) {
146	    fprintf (stderr,
147		     "Checking for bad blocks in read-only mode\n");
148	    fprintf (stderr, "From block %lu to %lu\n", from_count, blocks_count);
149	}
150	try = TEST_BUFFER_BLOCKS;
151	currently_testing = from_count;
152	num_blocks = blocks_count;
153	if (s_flag || v_flag > 1) {
154		fprintf(stderr, "Checking for bad blocks (read-only test): ");
155		if (v_flag <= 1)
156			alarm_intr(SIGALRM);
157	}
158	while (currently_testing < blocks_count)
159	{
160		if (currently_testing + try > blocks_count)
161			try = blocks_count - currently_testing;
162		got = do_test (dev, blkbuf, try, block_size, currently_testing);
163		currently_testing += got;
164		if (got == try) {
165			try = TEST_BUFFER_BLOCKS;
166			continue;
167		}
168		else
169			try = 1;
170		if (got == 0)
171			fprintf (out, "%lu\n", currently_testing++);
172	}
173	num_blocks = 0;
174	alarm(0);
175	if (s_flag || v_flag > 1)
176		fprintf(stderr, "done               \n");
177	fflush (stderr);
178	free (blkbuf);
179}
180
181static void test_rw (int dev, unsigned long blocks_count,
182		     unsigned long block_size, FILE * out,
183		     unsigned long from_count)
184{
185	int i;
186	char * buffer;
187	unsigned char pattern[] = {0xaa, 0x55, 0xff, 0x00};
188
189	buffer = malloc (2 * block_size);
190	if (!buffer)
191	{
192		com_err (program_name, ENOMEM, "while allocating buffers");
193		exit (1);
194	}
195
196	flush_bufs (dev, 0);
197
198	if (v_flag) {
199		fprintf(stderr,
200			"Checking for bad blocks in read-write mode\n");
201		fprintf(stderr, "From block %lu to %lu\n",
202			 from_count, blocks_count);
203	}
204	for (i = 0; i < sizeof (pattern); i++) {
205		memset (buffer, pattern[i], block_size);
206		if (s_flag | v_flag)
207			fprintf (stderr, "Writing pattern 0x%08x: ",
208				 *((int *) buffer));
209		num_blocks = blocks_count;
210		currently_testing = from_count;
211		if (s_flag && v_flag <= 1)
212			alarm_intr(SIGALRM);
213		for (;
214		     currently_testing < blocks_count;
215		     currently_testing++)
216		{
217			if (ext2fs_llseek (dev, (ext2_loff_t) currently_testing *
218					 block_size, SEEK_SET) !=
219			    (ext2_loff_t) currently_testing * block_size)
220				com_err (program_name, errno,
221					 "during seek on block %d",
222					 currently_testing);
223			if (v_flag > 1)
224				print_status();
225			write (dev, buffer, block_size);
226		}
227		num_blocks = 0;
228		alarm (0);
229		if (s_flag | v_flag)
230			fprintf(stderr, "done               \n");
231		flush_bufs (dev, 1);
232		if (s_flag | v_flag)
233			fprintf (stderr, "Reading and comparing: ");
234		num_blocks = blocks_count;
235		currently_testing = from_count;
236		if (s_flag && v_flag <= 1)
237			alarm_intr(SIGALRM);
238		for (;
239		     currently_testing < blocks_count;
240		     currently_testing++)
241		{
242			if (ext2fs_llseek (dev, (ext2_loff_t) currently_testing *
243					 block_size, SEEK_SET) !=
244			    (ext2_loff_t) currently_testing * block_size)
245				com_err (program_name, errno,
246					 "during seek on block %d",
247					 currently_testing);
248			if (v_flag > 1)
249				print_status();
250			if (read (dev, buffer + block_size, block_size) < block_size)
251				fprintf (out, "%ld\n", currently_testing);
252			else if (memcmp (buffer, buffer + block_size, block_size))
253				fprintf (out, "%ld\n", currently_testing);
254		}
255		num_blocks = 0;
256		alarm (0);
257		if (s_flag | v_flag)
258			fprintf(stderr, "done           \n");
259		flush_bufs (dev, 0);
260	}
261}
262
263int main (int argc, char ** argv)
264{
265	char c;
266	char * tmp;
267	char * device_name;
268	char * output_file = NULL;
269	FILE * out;
270	unsigned long block_size = 1024;
271	unsigned long blocks_count, from_count;
272	int dev;
273
274	setbuf(stdout, NULL);
275	setbuf(stderr, NULL);
276	if (argc && *argv)
277		program_name = *argv;
278	while ((c = getopt (argc, argv, "b:o:svw")) != EOF) {
279		switch (c) {
280		case 'b':
281			block_size = strtoul (optarg, &tmp, 0);
282			if (*tmp || block_size > 4096) {
283				com_err (program_name, 0,
284					 "bad block size - %s", optarg);
285				exit (1);
286			}
287			break;
288		case 'o':
289			output_file = optarg;
290			break;
291		case 's':
292			s_flag = 1;
293			break;
294		case 'v':
295			v_flag++;
296			break;
297		case 'w':
298			w_flag = 1;
299			break;
300		default:
301			usage ();
302		}
303	}
304	if (optind > argc - 1)
305		usage ();
306	device_name = argv[optind++];
307	if (optind > argc - 1)
308		usage ();
309	blocks_count = strtoul (argv[optind], &tmp, 0);
310	if (*tmp)
311	{
312		com_err (program_name, 0, "bad blocks count - %s", argv[optind]);
313		exit (1);
314	}
315	if (++optind <= argc-1) {
316		from_count = strtoul (argv[optind], &tmp, 0);
317	} else from_count = 0;
318	if (from_count >= blocks_count) {
319	    com_err (program_name, 0, "bad blocks range: %lu-%lu",
320		     from_count, blocks_count);
321	    exit (1);
322	}
323	dev = open (device_name, w_flag ? O_RDWR : O_RDONLY);
324	if (dev == -1)
325	{
326		com_err (program_name, errno,"while trying to open %s",
327			 device_name);
328		exit (1);
329	}
330	if (output_file && strcmp (output_file, "-") != 0)
331	{
332		out = fopen (output_file, "w");
333		if (out == NULL)
334		{
335			com_err (program_name, errno,"while trying to open %s",
336				 device_name);
337			exit (1);
338		}
339	}
340	else
341		out = stdout;
342	if (w_flag)
343		test_rw (dev, blocks_count, block_size, out, from_count);
344	else
345		test_ro (dev, blocks_count, block_size, out, from_count);
346	close (dev);
347	if (out != stdout)
348		fclose (out);
349	exit(0);
350}
351