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