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