blob: 81dc891e181d0df8a2766318a2e550e545647a56 [file] [log] [blame]
Theodore Ts'o3839e651997-04-26 13:21:57 +00001/*
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 *
Theodore Ts'o19c78dc1997-04-29 16:17:09 +00008 * Copyright 1995, 1996, 1997 by Theodore Ts'o
9 *
Theodore Ts'o3839e651997-04-26 13:21:57 +000010 * 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>
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000012 *
13 * %Begin-Header%
14 * This file may be redistributed under the terms of the GNU Public
15 * License.
16 * %End-Header%
Theodore Ts'o3839e651997-04-26 13:21:57 +000017 */
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>
Theodore Ts'oa418d3a1997-04-26 14:00:26 +000027#ifdef HAVE_GETOPT_H
Theodore Ts'o3839e651997-04-26 13:21:57 +000028#include <getopt.h>
Theodore Ts'oa418d3a1997-04-26 14:00:26 +000029#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +000030#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>
Theodore Ts'of3db3561997-04-26 13:34:30 +000037#include <sys/types.h>
Theodore Ts'o3839e651997-04-26 13:21:57 +000038
Theodore Ts'oa418d3a1997-04-26 14:00:26 +000039#if HAVE_LINUX_FS_H
Theodore Ts'o3839e651997-04-26 13:21:57 +000040#include <linux/fd.h>
41#include <linux/fs.h>
Theodore Ts'oa418d3a1997-04-26 14:00:26 +000042#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +000043
44#include "et/com_err.h"
Theodore Ts'od40259f1997-10-20 00:44:26 +000045#include "ext2fs/ext2_io.h"
Theodore Ts'o3839e651997-04-26 13:21:57 +000046
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{
Theodore Ts'of3db3561997-04-26 13:34:30 +000055 fprintf (stderr, "Usage: %s [-b block_size] [-o output_file] [-svw] device blocks_count\n [start_count]\n",
Theodore Ts'o3839e651997-04-26 13:21:57 +000056 program_name);
57 exit (1);
58}
59
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000060static 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
Theodore Ts'o3839e651997-04-26 13:21:57 +000081/*
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
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000089 if (v_flag > 1)
90 print_status();
91
Theodore Ts'o3839e651997-04-26 13:21:57 +000092 /* Seek to the correct loc. */
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000093 if (ext2fs_llseek (dev, (ext2_loff_t) current_block * block_size,
Theodore Ts'of3db3561997-04-26 13:34:30 +000094 SEEK_SET) != (ext2_loff_t) current_block * block_size)
95 com_err (program_name, errno, "during seek");
Theodore Ts'o3839e651997-04-26 13:21:57 +000096
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))
Theodore Ts'of3db3561997-04-26 13:34:30 +0000102 fprintf (stderr,
103 "Weird value (%ld) in do_test: probably bugs\n",
104 got);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000105 got /= block_size;
106 return got;
107}
108
Theodore Ts'oa418d3a1997-04-26 14:00:26 +0000109static 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
Theodore Ts'o3839e651997-04-26 13:21:57 +0000129static void test_ro (int dev, unsigned long blocks_count,
Theodore Ts'of3db3561997-04-26 13:34:30 +0000130 unsigned long block_size, FILE * out,
131 unsigned long from_count)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000132{
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 }
Theodore Ts'oa418d3a1997-04-26 14:00:26 +0000144 flush_bufs (dev, 0);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000145 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 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000150 try = TEST_BUFFER_BLOCKS;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000151 currently_testing = from_count;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000152 num_blocks = blocks_count;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000153 if (s_flag || v_flag > 1) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000154 fprintf(stderr, "Checking for bad blocks (read-only test): ");
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000155 if (v_flag <= 1)
156 alarm_intr(SIGALRM);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000157 }
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);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000175 if (s_flag || v_flag > 1)
Theodore Ts'of3db3561997-04-26 13:34:30 +0000176 fprintf(stderr, "done \n");
177 fflush (stderr);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000178 free (blkbuf);
179}
180
181static void test_rw (int dev, unsigned long blocks_count,
Theodore Ts'of3db3561997-04-26 13:34:30 +0000182 unsigned long block_size, FILE * out,
183 unsigned long from_count)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000184{
185 int i;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000186 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
Theodore Ts'oa418d3a1997-04-26 14:00:26 +0000196 flush_bufs (dev, 0);
197
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000198 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++) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000205 memset (buffer, pattern[i], block_size);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000206 if (s_flag | v_flag)
207 fprintf (stderr, "Writing pattern 0x%08x: ",
Theodore Ts'o3839e651997-04-26 13:21:57 +0000208 *((int *) buffer));
Theodore Ts'of3db3561997-04-26 13:34:30 +0000209 num_blocks = blocks_count;
210 currently_testing = from_count;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000211 if (s_flag && v_flag <= 1)
Theodore Ts'of3db3561997-04-26 13:34:30 +0000212 alarm_intr(SIGALRM);
213 for (;
214 currently_testing < blocks_count;
215 currently_testing++)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000216 {
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000217 if (ext2fs_llseek (dev, (ext2_loff_t) currently_testing *
Theodore Ts'of3db3561997-04-26 13:34:30 +0000218 block_size, SEEK_SET) !=
219 (ext2_loff_t) currently_testing * block_size)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000220 com_err (program_name, errno,
Theodore Ts'of3db3561997-04-26 13:34:30 +0000221 "during seek on block %d",
222 currently_testing);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000223 if (v_flag > 1)
224 print_status();
Theodore Ts'o3839e651997-04-26 13:21:57 +0000225 write (dev, buffer, block_size);
226 }
Theodore Ts'of3db3561997-04-26 13:34:30 +0000227 num_blocks = 0;
228 alarm (0);
229 if (s_flag | v_flag)
230 fprintf(stderr, "done \n");
Theodore Ts'oa418d3a1997-04-26 14:00:26 +0000231 flush_bufs (dev, 1);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000232 if (s_flag | v_flag)
233 fprintf (stderr, "Reading and comparing: ");
234 num_blocks = blocks_count;
235 currently_testing = from_count;
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000236 if (s_flag && v_flag <= 1)
Theodore Ts'of3db3561997-04-26 13:34:30 +0000237 alarm_intr(SIGALRM);
238 for (;
239 currently_testing < blocks_count;
240 currently_testing++)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000241 {
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000242 if (ext2fs_llseek (dev, (ext2_loff_t) currently_testing *
Theodore Ts'of3db3561997-04-26 13:34:30 +0000243 block_size, SEEK_SET) !=
244 (ext2_loff_t) currently_testing * block_size)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000245 com_err (program_name, errno,
Theodore Ts'of3db3561997-04-26 13:34:30 +0000246 "during seek on block %d",
247 currently_testing);
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000248 if (v_flag > 1)
249 print_status();
Theodore Ts'o3839e651997-04-26 13:21:57 +0000250 if (read (dev, buffer + block_size, block_size) < block_size)
Theodore Ts'of3db3561997-04-26 13:34:30 +0000251 fprintf (out, "%ld\n", currently_testing);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000252 else if (memcmp (buffer, buffer + block_size, block_size))
Theodore Ts'of3db3561997-04-26 13:34:30 +0000253 fprintf (out, "%ld\n", currently_testing);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000254 }
Theodore Ts'of3db3561997-04-26 13:34:30 +0000255 num_blocks = 0;
256 alarm (0);
257 if (s_flag | v_flag)
258 fprintf(stderr, "done \n");
Theodore Ts'oa418d3a1997-04-26 14:00:26 +0000259 flush_bufs (dev, 0);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000260 }
261}
262
Theodore Ts'o00e54331997-09-16 02:13:52 +0000263int main (int argc, char ** argv)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000264{
Theodore Ts'o519149f1997-10-25 03:49:49 +0000265 int c;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000266 char * tmp;
267 char * device_name;
268 char * output_file = NULL;
269 FILE * out;
270 unsigned long block_size = 1024;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000271 unsigned long blocks_count, from_count;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000272 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':
Theodore Ts'o19c78dc1997-04-29 16:17:09 +0000295 v_flag++;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000296 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 }
Theodore Ts'of3db3561997-04-26 13:34:30 +0000315 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 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000323 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)
Theodore Ts'of3db3561997-04-26 13:34:30 +0000343 test_rw (dev, blocks_count, block_size, out, from_count);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000344 else
Theodore Ts'of3db3561997-04-26 13:34:30 +0000345 test_ro (dev, blocks_count, block_size, out, from_count);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000346 close (dev);
347 if (out != stdout)
348 fclose (out);
Theodore Ts'o00e54331997-09-16 02:13:52 +0000349 exit(0);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000350}