Whamcloud - gitweb
Many files:
[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/io.h"
46
47 const char * program_name = "badblocks";
48
49 int v_flag = 0;                 /* verbose */
50 int w_flag = 0;                 /* do r/w test */
51 int s_flag = 0;                 /* show progress of test */
52
53 static 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
60 static unsigned long currently_testing = 0;
61 static unsigned long num_blocks = 0;
62
63 static 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
70 static 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  */
84 static 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
109 static 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
129 static 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
181 static 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
263 void 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 }