| /* |
| * badblocks.c - Bad blocks checker |
| * |
| * Copyright (C) 1992, 1993, 1994 Remy Card <card@masi.ibp.fr> |
| * Laboratoire MASI, Institut Blaise Pascal |
| * Universite Pierre et Marie Curie (Paris VI) |
| * |
| * Copyright 1995, 1996, 1997 by Theodore Ts'o |
| * |
| * This file is based on the minix file system programs fsck and mkfs |
| * written and copyrighted by Linus Torvalds <Linus.Torvalds@cs.helsinki.fi> |
| * |
| * %Begin-Header% |
| * This file may be redistributed under the terms of the GNU Public |
| * License. |
| * %End-Header% |
| */ |
| |
| /* |
| * History: |
| * 93/05/26 - Creation from e2fsck |
| * 94/02/27 - Made a separate bad blocks checker |
| */ |
| |
| #include <errno.h> |
| #include <fcntl.h> |
| #ifdef HAVE_GETOPT_H |
| #include <getopt.h> |
| #endif |
| #include <signal.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| |
| #include <sys/ioctl.h> |
| #include <sys/types.h> |
| |
| #if HAVE_LINUX_FS_H |
| #include <linux/fd.h> |
| #include <linux/fs.h> |
| #endif |
| |
| #include "et/com_err.h" |
| #include "ext2fs/ext2_io.h" |
| |
| const char * program_name = "badblocks"; |
| const char * done_string = "done \n"; |
| |
| int v_flag = 0; /* verbose */ |
| int w_flag = 0; /* do r/w test */ |
| int s_flag = 0; /* show progress of test */ |
| |
| static void usage(void) |
| { |
| fprintf (stderr, "Usage: %s [-b block_size] [-o output_file] [-svw] device blocks_count\n [start_count]\n", |
| program_name); |
| exit (1); |
| } |
| |
| static unsigned long currently_testing = 0; |
| static unsigned long num_blocks = 0; |
| |
| static void print_status(void) |
| { |
| fprintf(stderr, "%9ld/%9ld", currently_testing, num_blocks); |
| fprintf(stderr, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); |
| fflush (stderr); |
| } |
| |
| static void alarm_intr (int alnum) |
| { |
| signal (SIGALRM, alarm_intr); |
| alarm(1); |
| if (!num_blocks) |
| return; |
| fprintf(stderr, "%9ld/%9ld", currently_testing, num_blocks); |
| fprintf(stderr, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); |
| fflush (stderr); |
| } |
| |
| /* |
| * Perform a test of a block; return the number of blocks readable/writeable. |
| */ |
| static long do_test (int dev, char * buffer, int try, unsigned long block_size, |
| unsigned long current_block) |
| { |
| long got; |
| |
| if (v_flag > 1) |
| print_status(); |
| |
| /* Seek to the correct loc. */ |
| if (ext2fs_llseek (dev, (ext2_loff_t) current_block * block_size, |
| SEEK_SET) != (ext2_loff_t) current_block * block_size) |
| com_err (program_name, errno, "during seek"); |
| |
| /* Try the read */ |
| got = read (dev, buffer, try * block_size); |
| if (got < 0) |
| got = 0; |
| if (got & (block_size - 1)) |
| fprintf (stderr, |
| "Weird value (%ld) in do_test: probably bugs\n", |
| got); |
| got /= block_size; |
| return got; |
| } |
| |
| static void flush_bufs (int dev, int sync) |
| { |
| if (v_flag |
| #if !defined (BLKFLSBUF) && !defined (FDFLUSH) |
| && sync |
| #endif |
| ) |
| fprintf (stderr, "Flushing buffers\n"); |
| |
| if (sync && fsync (dev) == -1) |
| com_err (program_name, errno, "during fsync"); |
| |
| #ifdef BLKLSBUF |
| ioctl (dev, BLKFLSBUF, 0); /* In case this is a HD */ |
| #endif |
| #ifdef FDFLUSH |
| ioctl (dev, FDFLUSH, 0); /* In case this is floppy */ |
| #endif |
| } |
| |
| static void test_ro (int dev, unsigned long blocks_count, |
| unsigned long block_size, FILE * out, |
| unsigned long from_count) |
| { |
| #define TEST_BUFFER_BLOCKS 16 |
| char * blkbuf; |
| int try; |
| long got; |
| |
| blkbuf = malloc (TEST_BUFFER_BLOCKS * block_size); |
| if (!blkbuf) |
| { |
| com_err (program_name, ENOMEM, "while allocating buffers"); |
| exit (1); |
| } |
| flush_bufs (dev, 0); |
| if (v_flag) { |
| fprintf (stderr, |
| "Checking for bad blocks in read-only mode\n"); |
| fprintf (stderr, "From block %lu to %lu\n", from_count, blocks_count); |
| } |
| try = TEST_BUFFER_BLOCKS; |
| currently_testing = from_count; |
| num_blocks = blocks_count; |
| if (s_flag || v_flag > 1) { |
| fprintf(stderr, "Checking for bad blocks (read-only test): "); |
| if (v_flag <= 1) |
| alarm_intr(SIGALRM); |
| } |
| while (currently_testing < blocks_count) |
| { |
| if (currently_testing + try > blocks_count) |
| try = blocks_count - currently_testing; |
| got = do_test (dev, blkbuf, try, block_size, currently_testing); |
| currently_testing += got; |
| if (got == try) { |
| try = TEST_BUFFER_BLOCKS; |
| continue; |
| } |
| else |
| try = 1; |
| if (got == 0) |
| fprintf (out, "%lu\n", currently_testing++); |
| } |
| num_blocks = 0; |
| alarm(0); |
| if (s_flag || v_flag > 1) |
| fprintf(stderr, done_string); |
| fflush (stderr); |
| free (blkbuf); |
| } |
| |
| static void test_rw (int dev, unsigned long blocks_count, |
| unsigned long block_size, FILE * out, |
| unsigned long from_count) |
| { |
| int i; |
| char * buffer; |
| unsigned char pattern[] = {0xaa, 0x55, 0xff, 0x00}; |
| |
| buffer = malloc (2 * block_size); |
| if (!buffer) |
| { |
| com_err (program_name, ENOMEM, "while allocating buffers"); |
| exit (1); |
| } |
| |
| flush_bufs (dev, 0); |
| |
| if (v_flag) { |
| fprintf(stderr, |
| "Checking for bad blocks in read-write mode\n"); |
| fprintf(stderr, "From block %lu to %lu\n", |
| from_count, blocks_count); |
| } |
| for (i = 0; i < sizeof (pattern); i++) { |
| memset (buffer, pattern[i], block_size); |
| if (s_flag | v_flag) |
| fprintf (stderr, "Writing pattern 0x%08x: ", |
| *((int *) buffer)); |
| num_blocks = blocks_count; |
| currently_testing = from_count; |
| if (s_flag && v_flag <= 1) |
| alarm_intr(SIGALRM); |
| for (; |
| currently_testing < blocks_count; |
| currently_testing++) |
| { |
| if (ext2fs_llseek (dev, (ext2_loff_t) currently_testing * |
| block_size, SEEK_SET) != |
| (ext2_loff_t) currently_testing * block_size) |
| com_err (program_name, errno, |
| "during seek on block %d", |
| currently_testing); |
| if (v_flag > 1) |
| print_status(); |
| write (dev, buffer, block_size); |
| } |
| num_blocks = 0; |
| alarm (0); |
| if (s_flag | v_flag) |
| fprintf(stderr, done_string); |
| flush_bufs (dev, 1); |
| if (s_flag | v_flag) |
| fprintf (stderr, "Reading and comparing: "); |
| num_blocks = blocks_count; |
| currently_testing = from_count; |
| if (s_flag && v_flag <= 1) |
| alarm_intr(SIGALRM); |
| for (; |
| currently_testing < blocks_count; |
| currently_testing++) |
| { |
| if (ext2fs_llseek (dev, (ext2_loff_t) currently_testing * |
| block_size, SEEK_SET) != |
| (ext2_loff_t) currently_testing * block_size) |
| com_err (program_name, errno, |
| "during seek on block %d", |
| currently_testing); |
| if (v_flag > 1) |
| print_status(); |
| if (read (dev, buffer + block_size, block_size) < block_size) |
| fprintf (out, "%ld\n", currently_testing); |
| else if (memcmp (buffer, buffer + block_size, block_size)) |
| fprintf (out, "%ld\n", currently_testing); |
| } |
| num_blocks = 0; |
| alarm (0); |
| if (s_flag | v_flag) |
| fprintf(stderr, done_string); |
| flush_bufs (dev, 0); |
| } |
| } |
| |
| int main (int argc, char ** argv) |
| { |
| int c; |
| char * tmp; |
| char * device_name; |
| char * output_file = NULL; |
| FILE * out; |
| unsigned long block_size = 1024; |
| unsigned long blocks_count, from_count; |
| int dev; |
| |
| setbuf(stdout, NULL); |
| setbuf(stderr, NULL); |
| if (argc && *argv) |
| program_name = *argv; |
| while ((c = getopt (argc, argv, "b:o:svw")) != EOF) { |
| switch (c) { |
| case 'b': |
| block_size = strtoul (optarg, &tmp, 0); |
| if (*tmp || block_size > 4096) { |
| com_err (program_name, 0, |
| "bad block size - %s", optarg); |
| exit (1); |
| } |
| break; |
| case 'o': |
| output_file = optarg; |
| break; |
| case 's': |
| s_flag = 1; |
| break; |
| case 'v': |
| v_flag++; |
| break; |
| case 'w': |
| w_flag = 1; |
| break; |
| default: |
| usage(); |
| } |
| } |
| if (optind > argc - 1) |
| usage(); |
| device_name = argv[optind++]; |
| if (optind > argc - 1) |
| usage(); |
| blocks_count = strtoul (argv[optind], &tmp, 0); |
| if (*tmp) |
| { |
| com_err (program_name, 0, "bad blocks count - %s", argv[optind]); |
| exit (1); |
| } |
| if (++optind <= argc-1) { |
| from_count = strtoul (argv[optind], &tmp, 0); |
| } else from_count = 0; |
| if (from_count >= blocks_count) { |
| com_err (program_name, 0, "bad blocks range: %lu-%lu", |
| from_count, blocks_count); |
| exit (1); |
| } |
| dev = open (device_name, w_flag ? O_RDWR : O_RDONLY); |
| if (dev == -1) |
| { |
| com_err (program_name, errno,"while trying to open %s", |
| device_name); |
| exit (1); |
| } |
| if (output_file && strcmp (output_file, "-") != 0) |
| { |
| out = fopen (output_file, "w"); |
| if (out == NULL) |
| { |
| com_err (program_name, errno,"while trying to open %s", |
| device_name); |
| exit (1); |
| } |
| } |
| else |
| out = stdout; |
| if (w_flag) |
| test_rw (dev, blocks_count, block_size, out, from_count); |
| else |
| test_ro (dev, blocks_count, block_size, out, from_count); |
| close (dev); |
| if (out != stdout) |
| fclose (out); |
| exit(0); |
| } |