X-Git-Url: https://git.whamcloud.com/?a=blobdiff_plain;f=misc%2Fbadblocks.c;h=afeb3da9d1ddd464e18ce3531ca491df2207a9e9;hb=b5bdc0432f064c10db6bb6fa1b5f370b3c1f0f2c;hp=9a4c3629120a4df860134a6e50a9f2b317123eff;hpb=f56f32b0a505e34712c8b8fb908dffb616743726;p=tools%2Fe2fsprogs.git diff --git a/misc/badblocks.c b/misc/badblocks.c index 9a4c362..afeb3da 100644 --- a/misc/badblocks.c +++ b/misc/badblocks.c @@ -10,7 +10,7 @@ * * This file is based on the minix file system programs fsck and mkfs * written and copyrighted by Linus Torvalds - * + * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. @@ -24,13 +24,16 @@ * 99/06/30...99/07/26 - Added non-destructive write-testing, * configurable blocks-at-once parameter, * loading of badblocks list to avoid testing - * blocks known to be bad, multiple passes to + * blocks known to be bad, multiple passes to * make sure that no new blocks are added to the * list. (Work done by David Beattie) */ +#ifndef _GNU_SOURCE #define _GNU_SOURCE /* for O_DIRECT */ +#endif +#include "config.h" #include #include #ifdef HAVE_GETOPT_H @@ -47,66 +50,87 @@ extern int optind; #include #include #include +#ifdef HAVE_MBSTOWCS +#include +#endif #include #include #include -#include #include "et/com_err.h" #include "ext2fs/ext2_io.h" #include "ext2fs/ext2_fs.h" #include "ext2fs/ext2fs.h" -#include "nls-enable.h" +#include "support/nls-enable.h" + +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif + +/* Maximum number of bad blocks we support */ +#define MAX_BAD_BLOCKS (INT_MAX/2) -const char * program_name = "badblocks"; -const char * done_string = N_("done \n"); +static const char * program_name = "badblocks"; +static const char * done_string = N_("done \n"); -static int v_flag = 0; /* verbose */ -static int w_flag = 0; /* do r/w test: 0=no, 1=yes, +static int v_flag; /* verbose */ +static int w_flag; /* do r/w test: 0=no, 1=yes, * 2=non-destructive */ -static int s_flag = 0; /* show progress of test */ -static int force = 0; /* force check of mounted device */ -static int t_flag = 0; /* number of test patterns */ -static int t_max = 0; /* allocated test patterns */ -static unsigned int *t_patts = NULL; /* test patterns */ -static int current_O_DIRECT = 0; /* Current status of O_DIRECT flag */ -static int exclusive_ok = 0; -static unsigned int max_bb = 0; /* Abort test if more than this number of bad blocks has been encountered */ -static unsigned int d_flag = 0; /* delay factor between reads */ +static int s_flag; /* show progress of test */ +static int force; /* force check of mounted device */ +static int t_flag; /* number of test patterns */ +static int t_max; /* allocated test patterns */ +static unsigned int *t_patts; /* test patterns */ +static int use_buffered_io; +static int exclusive_ok; +static unsigned int max_bb = MAX_BAD_BLOCKS; /* Abort test if more than this + * number of bad blocks has been + * encountered */ +static unsigned int d_flag; /* delay factor between reads */ +static struct timeval time_start; #define T_INC 32 -unsigned int sys_page_size = 4096; +static unsigned int sys_page_size = 4096; static void usage(void) { - fprintf(stderr, _("Usage: %s [-b block_size] [-i input_file] [-o output_file] [-svwnf]\n [-c blocks_at_once] [-p num_passes] [-e max_bad_blocks] [-d delay_factor_between_reads] [-t test_pattern [-t test_pattern [...]]]\n device [last_block [first_block]]\n"), + fprintf(stderr, _( +"Usage: %s [-b block_size] [-i input_file] [-o output_file] [-svwnfBX]\n" +" [-c blocks_at_once] [-d delay_factor_between_reads] [-e max_bad_blocks]\n" +" [-p num_passes] [-t test_pattern [-t test_pattern [...]]]\n" +" device [last_block [first_block]]\n"), program_name); exit (1); } static void exclusive_usage(void) { - fprintf(stderr, - _("%s: The -n and -w options are mutually exclusive.\n\n"), + fprintf(stderr, + _("%s: The -n and -w options are mutually exclusive.\n\n"), program_name); exit(1); } static blk_t currently_testing = 0; static blk_t num_blocks = 0; +static blk_t num_read_errors = 0; +static blk_t num_write_errors = 0; +static blk_t num_corruption_errors = 0; static ext2_badblocks_list bb_list = NULL; static FILE *out; static blk_t next_bad = 0; static ext2_badblocks_iterate bb_iter = NULL; +enum error_types { READ_ERROR, WRITE_ERROR, CORRUPTION_ERROR }; + static void *allocate_buffer(size_t size) { void *ret = 0; - + #ifdef HAVE_POSIX_MEMALIGN - if (posix_memalign(&ret, sys_page_size, size) < 0) + if (posix_memalign(&ret, sys_page_size, size) != 0) ret = 0; #else #ifdef HAVE_MEMALIGN @@ -115,7 +139,7 @@ static void *allocate_buffer(size_t size) #ifdef HAVE_VALLOC ret = valloc(size); #endif /* HAVE_VALLOC */ -#endif /* HAVE_MEMALIGN */ +#endif /* HAVE_MEMALIGN */ #endif /* HAVE_POSIX_MEMALIGN */ if (!ret) @@ -128,7 +152,7 @@ static void *allocate_buffer(size_t size) * This routine reports a new bad block. If the bad block has already * been seen before, then it returns 0; otherwise it returns 1. */ -static int bb_output (blk_t bad) +static int bb_output (blk_t bad, enum error_types error_type) { errcode_t errcode; @@ -145,19 +169,81 @@ static int bb_output (blk_t bad) } /* kludge: - increment the iteration through the bb_list if + increment the iteration through the bb_list if an element was just added before the current iteration position. This should not cause next_bad to change. */ if (bb_iter && bad < next_bad) ext2fs_badblocks_list_iterate (bb_iter, &next_bad); + + if (error_type == READ_ERROR) { + num_read_errors++; + } else if (error_type == WRITE_ERROR) { + num_write_errors++; + } else if (error_type == CORRUPTION_ERROR) { + num_corruption_errors++; + } return 1; } +static char *time_diff_format(struct timeval *tv1, + struct timeval *tv2, char *buf) +{ + time_t diff = (tv1->tv_sec - tv2->tv_sec); + int hr,min,sec; + + sec = diff % 60; + diff /= 60; + min = diff % 60; + hr = diff / 60; + + if (hr) + sprintf(buf, "%d:%02d:%02d", hr, min, sec); + else + sprintf(buf, "%d:%02d", min, sec); + return buf; +} + +static float calc_percent(unsigned long current, unsigned long total) { + float percent = 0.0; + if (total <= 0) + return percent; + if (current >= total) { + percent = 100.0; + } else { + percent=(100.0*(float)current/(float)total); + } + return percent; +} + static void print_status(void) { - fprintf(stderr, "%15lu/%15lu", (unsigned long) currently_testing, - (unsigned long) num_blocks); - fputs("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", stderr); + struct timeval time_end; + char diff_buf[32], line_buf[128]; +#ifdef HAVE_MBSTOWCS + wchar_t wline_buf[128]; +#endif + int len; + + gettimeofday(&time_end, 0); + len = snprintf(line_buf, sizeof(line_buf), + _("%6.2f%% done, %s elapsed. " + "(%d/%d/%d errors)"), + calc_percent((unsigned long) currently_testing, + (unsigned long) num_blocks), + time_diff_format(&time_end, &time_start, diff_buf), + num_read_errors, + num_write_errors, + num_corruption_errors); +#ifdef HAVE_MBSTOWCS + mbstowcs(wline_buf, line_buf, sizeof(line_buf)); + len = wcswidth(wline_buf, sizeof(line_buf)); + if (len < 0) + len = strlen(line_buf); /* Should never happen... */ +#endif + fputs(line_buf, stderr); + memset(line_buf, '\b', len); + line_buf[len] = 0; + fputs(line_buf, stderr); fflush (stderr); } @@ -174,6 +260,10 @@ static void *terminate_addr = NULL; static void terminate_intr(int signo EXT2FS_ATTR((unused))) { + fflush(out); + fprintf(stderr, "\n\nInterrupted at block %llu\n", + (unsigned long long) currently_testing); + fflush(stderr); if (terminate_addr) longjmp(terminate_addr,1); exit(1); @@ -201,16 +291,22 @@ static void uncapture_terminate(void) signal (SIGUSR2, SIG_DFL); } +/* Linux requires that O_DIRECT I/Os be 512-byte sector aligned */ + +#define O_DIRECT_SIZE 512 + static void set_o_direct(int dev, unsigned char *buffer, size_t size, - blk_t current_block) + ext2_loff_t offset) { #ifdef O_DIRECT + static int current_O_DIRECT; /* Current status of O_DIRECT flag */ int new_flag = O_DIRECT; int flag; - - if ((((unsigned long) buffer & (sys_page_size - 1)) != 0) || + + if ((use_buffered_io != 0) || + (((unsigned long) buffer & (sys_page_size - 1)) != 0) || ((size & (sys_page_size - 1)) != 0) || - ((current_block & ((sys_page_size >> 9)-1)) != 0)) + ((offset & (O_DIRECT_SIZE - 1)) != 0)) new_flag = 0; if (new_flag != current_O_DIRECT) { @@ -218,7 +314,8 @@ static void set_o_direct(int dev, unsigned char *buffer, size_t size, flag = fcntl(dev, F_GETFL); if (flag > 0) { flag = (flag & ~O_DIRECT) | new_flag; - fcntl(dev, F_SETFL, flag); + if (fcntl(dev, F_SETFL, flag) < 0) + perror("set_o_direct"); } current_O_DIRECT = new_flag; } @@ -231,7 +328,7 @@ static void pattern_fill(unsigned char *buffer, unsigned int pattern, { unsigned int i, nb; unsigned char bpattern[sizeof(pattern)], *ptr; - + if (pattern == (unsigned int) ~0) { for (ptr = buffer; ptr < buffer + n; ptr++) { (*ptr) = random() % (1 << (8 * sizeof(char))); @@ -275,7 +372,11 @@ static int do_read (int dev, unsigned char * buffer, int try, int block_size, #define NANOSEC (1000000000L) #define MILISEC (1000L) - set_o_direct(dev, buffer, try * block_size, current_block); +#if 0 + printf("do_read: block %d, try %d\n", current_block, try); +#endif + set_o_direct(dev, buffer, try * block_size, + ((ext2_loff_t) current_block) * block_size); if (v_flag > 1) print_status(); @@ -283,7 +384,7 @@ static int do_read (int dev, unsigned char * buffer, int try, int block_size, /* 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")); + com_err (program_name, errno, "%s", _("during seek")); /* Try the read */ if (d_flag) @@ -292,11 +393,12 @@ static int do_read (int dev, unsigned char * buffer, int try, int block_size, if (d_flag) gettimeofday(&tv2, NULL); if (got < 0) - got = 0; + got = 0; if (got & 511) fprintf(stderr, _("Weird value (%ld) in do_read\n"), got); got /= block_size; if (d_flag && got == try) { +#ifdef HAVE_NANOSLEEP struct timespec ts; ts.tv_sec = tv2.tv_sec - tv1.tv_sec; ts.tv_nsec = (tv2.tv_usec - tv1.tv_usec) * MILISEC; @@ -313,6 +415,23 @@ static int do_read (int dev, unsigned char * buffer, int try, int block_size, } if (ts.tv_sec || ts.tv_nsec) nanosleep(&ts, NULL); +#else +#ifdef HAVE_USLEEP + struct timeval tv; + tv.tv_sec = tv2.tv_sec - tv1.tv_sec; + tv.tv_usec = tv2.tv_usec - tv1.tv_usec; + tv.tv_sec = tv.tv_sec * d_flag / 100; + tv.tv_usec = tv.tv_usec * d_flag / 100; + if (tv.tv_usec > 1000000) { + tv.tv_sec += tv.tv_usec / 1000000; + tv.tv_usec %= 1000000; + } + if (tv.tv_sec) + sleep(tv.tv_sec); + if (tv.tv_usec) + usleep(tv.tv_usec); +#endif +#endif } return got; } @@ -326,7 +445,11 @@ static int do_write(int dev, unsigned char * buffer, int try, int block_size, { long got; - set_o_direct(dev, buffer, try * block_size, current_block); +#if 0 + printf("do_write: block %lu, try %d\n", current_block, try); +#endif + set_o_direct(dev, buffer, try * block_size, + ((ext2_loff_t) current_block) * block_size); if (v_flag > 1) print_status(); @@ -334,12 +457,12 @@ static int do_write(int dev, unsigned char * buffer, int try, int block_size, /* 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")); + com_err (program_name, errno, "%s", _("during seek")); /* Try the write */ got = write (dev, buffer, try * block_size); if (got < 0) - got = 0; + got = 0; if (got & 511) fprintf(stderr, "Weird value (%ld) in do_write\n", got); got /= block_size; @@ -352,9 +475,14 @@ static void flush_bufs(void) { errcode_t retval; +#ifdef O_DIRECT + if (!use_buffered_io) + return; +#endif retval = ext2fs_sync_device(host_dev, 1); if (retval) - com_err(program_name, retval, _("during ext2fs_sync_device")); + com_err(program_name, retval, "%s", + _("during ext2fs_sync_device")); } static unsigned int test_ro (int dev, blk_t last_block, @@ -366,11 +494,15 @@ static unsigned int test_ro (int dev, blk_t last_block, int got; unsigned int bb_count = 0; errcode_t errcode; + blk_t recover_block = ~0; + + /* set up abend handler */ + capture_terminate(NULL); errcode = ext2fs_badblocks_list_iterate_begin(bb_list,&bb_iter); if (errcode) { - com_err (program_name, errcode, - _("while beginning bad block list iteration")); + com_err(program_name, errcode, "%s", + _("while beginning bad block list iteration")); exit (1); } do { @@ -384,13 +516,14 @@ static unsigned int test_ro (int dev, blk_t last_block, } if (!blkbuf) { - com_err (program_name, ENOMEM, _("while allocating buffers")); + com_err(program_name, ENOMEM, "%s", + _("while allocating buffers")); exit (1); } if (v_flag) { - fprintf (stderr, _("Checking blocks %lu to %lu\n"), - (unsigned long) first_block, - (unsigned long) last_block - 1); + fprintf(stderr, _("Checking blocks %lu to %lu\n"), + (unsigned long)first_block, + (unsigned long)last_block - 1); } if (t_flag) { fputs(_("Checking for bad blocks in read-only mode\n"), stderr); @@ -401,14 +534,13 @@ static unsigned int test_ro (int dev, blk_t last_block, try = blocks_at_once; currently_testing = first_block; num_blocks = last_block - 1; - if (!t_flag && (s_flag || v_flag)) { + if (!t_flag && (s_flag || v_flag)) fputs(_("Checking for bad blocks (read-only test): "), stderr); - if (v_flag <= 1) - alarm_intr(SIGALRM); - } + if (s_flag && v_flag <= 1) + alarm_intr(SIGALRM); while (currently_testing < last_block) { - if (max_bb && bb_count >= max_bb) { + if (bb_count >= max_bb) { if (s_flag || v_flag) { fputs(_("Too many bad blocks, aborting test\n"), stderr); } @@ -435,23 +567,20 @@ static unsigned int test_ro (int dev, blk_t last_block, if (memcmp (blkbuf+i*block_size, blkbuf+blocks_at_once*block_size, block_size)) - bb_count += bb_output(currently_testing + i); + bb_count += bb_output(currently_testing + i, CORRUPTION_ERROR); } + if (got == 0 && try == 1) + bb_count += bb_output(currently_testing++, READ_ERROR); currently_testing += got; - if (got == try) { - try = blocks_at_once; - /* recover page-aligned offset for O_DIRECT */ - if ( (blocks_at_once >= sys_page_size >> 9) - && (currently_testing % (sys_page_size >> 9)!= 0)) - try -= (sys_page_size >> 9) - - (currently_testing - % (sys_page_size >> 9)); - continue; - } - else + if (got != try) { try = 1; - if (got == 0) { - bb_count += bb_output(currently_testing++); + if (recover_block == ~0U) + recover_block = currently_testing - got + + blocks_at_once; + continue; + } else if (currently_testing == recover_block) { + try = blocks_at_once; + recover_block = ~0; } } num_blocks = 0; @@ -464,6 +593,8 @@ static unsigned int test_ro (int dev, blk_t last_block, ext2fs_badblocks_list_iterate_end(bb_iter); + uncapture_terminate(); + return bb_count; } @@ -476,22 +607,27 @@ static unsigned int test_rw (int dev, blk_t last_block, const unsigned int *pattern; int i, try, got, nr_pattern, pat_idx; unsigned int bb_count = 0; + blk_t recover_block = ~0; + + /* set up abend handler */ + capture_terminate(NULL); buffer = allocate_buffer(2 * blocks_at_once * block_size); read_buffer = buffer + blocks_at_once * block_size; - + if (!buffer) { - com_err (program_name, ENOMEM, _("while allocating buffers")); + com_err(program_name, ENOMEM, "%s", + _("while allocating buffers")); exit (1); } flush_bufs(); if (v_flag) { - fputs(_("Checking for bad blocks in read-write mode\n"), + fputs(_("Checking for bad blocks in read-write mode\n"), stderr); fprintf(stderr, _("From block %lu to %lu\n"), - (unsigned long) first_block, + (unsigned long) first_block, (unsigned long) last_block - 1); } if (t_flag) { @@ -511,7 +647,7 @@ static unsigned int test_rw (int dev, blk_t last_block, try = blocks_at_once; while (currently_testing < last_block) { - if (max_bb && bb_count >= max_bb) { + if (bb_count >= max_bb) { if (s_flag || v_flag) { fputs(_("Too many bad blocks, aborting test\n"), stderr); } @@ -524,24 +660,21 @@ static unsigned int test_rw (int dev, blk_t last_block, if (v_flag > 1) print_status(); + if (got == 0 && try == 1) + bb_count += bb_output(currently_testing++, WRITE_ERROR); currently_testing += got; - if (got == try) { - try = blocks_at_once; - /* recover page-aligned offset for O_DIRECT */ - if ( (blocks_at_once >= sys_page_size >> 9) - && (currently_testing % - (sys_page_size >> 9)!= 0)) - try -= (sys_page_size >> 9) - - (currently_testing - % (sys_page_size >> 9)); - continue; - } else + if (got != try) { try = 1; - if (got == 0) { - bb_count += bb_output(currently_testing++); + if (recover_block == ~0U) + recover_block = currently_testing - + got + blocks_at_once; + continue; + } else if (currently_testing == recover_block) { + try = blocks_at_once; + recover_block = ~0; } } - + num_blocks = 0; alarm (0); if (s_flag | v_flag) @@ -556,7 +689,7 @@ static unsigned int test_rw (int dev, blk_t last_block, try = blocks_at_once; while (currently_testing < last_block) { - if (max_bb && bb_count >= max_bb) { + if (bb_count >= max_bb) { if (s_flag || v_flag) { fputs(_("Too many bad blocks, aborting test\n"), stderr); } @@ -566,29 +699,29 @@ static unsigned int test_rw (int dev, blk_t last_block, try = last_block - currently_testing; got = do_read (dev, read_buffer, try, block_size, currently_testing); - if (got == 0) { - bb_count += bb_output(currently_testing++); + if (got == 0 && try == 1) + bb_count += bb_output(currently_testing++, READ_ERROR); + currently_testing += got; + if (got != try) { + try = 1; + if (recover_block == ~0U) + recover_block = currently_testing - + got + blocks_at_once; continue; + } else if (currently_testing == recover_block) { + try = blocks_at_once; + recover_block = ~0U; } for (i=0; i < got; i++) { if (memcmp(read_buffer + i * block_size, buffer + i * block_size, block_size)) - bb_count += bb_output(currently_testing+i); + bb_count += bb_output(currently_testing+i, CORRUPTION_ERROR); } - currently_testing += got; - /* recover page-aligned offset for O_DIRECT */ - if ( (blocks_at_once >= sys_page_size >> 9) - && (currently_testing % (sys_page_size >> 9)!= 0)) - try = blocks_at_once - (sys_page_size >> 9) - - (currently_testing - % (sys_page_size >> 9)); - else - try = blocks_at_once; if (v_flag > 1) print_status(); } - + num_blocks = 0; alarm (0); if (s_flag | v_flag) @@ -624,12 +757,14 @@ static unsigned int test_nd (int dev, blk_t last_block, errcode_t errcode; unsigned long buf_used; static unsigned int bb_count; + unsigned int granularity = blocks_at_once; + blk_t recover_block = ~0U; bb_count = 0; errcode = ext2fs_badblocks_list_iterate_begin(bb_list,&bb_iter); if (errcode) { - com_err (program_name, errcode, - _("while beginning bad block list iteration")); + com_err(program_name, errcode, "%s", + _("while beginning bad block list iteration")); exit (1); } do { @@ -637,22 +772,23 @@ static unsigned int test_nd (int dev, blk_t last_block, } while (next_bad && next_bad < first_block); blkbuf = allocate_buffer(3 * blocks_at_once * block_size); - test_record = malloc (blocks_at_once*sizeof(struct saved_blk_record)); + test_record = malloc(blocks_at_once * sizeof(struct saved_blk_record)); if (!blkbuf || !test_record) { - com_err(program_name, ENOMEM, _("while allocating buffers")); + com_err(program_name, ENOMEM, "%s", + _("while allocating buffers")); exit (1); } save_base = blkbuf; test_base = blkbuf + (blocks_at_once * block_size); read_base = blkbuf + (2 * blocks_at_once * block_size); - + num_saved = 0; flush_bufs(); if (v_flag) { fputs(_("Checking for bad blocks in non-destructive read-write mode\n"), stderr); - fprintf (stderr, _("From block %lu to %lu\n"), + fprintf (stderr, _("From block %lu to %lu\n"), (unsigned long) first_block, (unsigned long) last_block - 1); } @@ -675,7 +811,7 @@ static unsigned int test_nd (int dev, blk_t last_block, fflush (out); exit(1); } - + /* set up abend handler */ capture_terminate(terminate_env); @@ -700,13 +836,13 @@ static unsigned int test_nd (int dev, blk_t last_block, alarm_intr(SIGALRM); while (currently_testing < last_block) { - if (max_bb && bb_count >= max_bb) { + if (bb_count >= max_bb) { if (s_flag || v_flag) { fputs(_("Too many bad blocks, aborting test\n"), stderr); } break; } - got = try = blocks_at_once - buf_used; + got = try = granularity - buf_used; if (next_bad) { if (currently_testing == next_bad) { /* fprintf (out, "%lu\n", nextbad); */ @@ -722,8 +858,15 @@ static unsigned int test_nd (int dev, blk_t last_block, got = do_read (dev, save_ptr, try, block_size, currently_testing); if (got == 0) { + if (recover_block == ~0U) + recover_block = currently_testing + + blocks_at_once; + if (granularity != 1) { + granularity = 1; + continue; + } /* First block must have been bad. */ - bb_count += bb_output(currently_testing++); + bb_count += bb_output(currently_testing++, READ_ERROR); goto check_for_more; } @@ -741,15 +884,19 @@ static unsigned int test_nd (int dev, blk_t last_block, if (written != got) com_err (program_name, errno, _("during test data write, block %lu"), - (unsigned long) currently_testing + + (unsigned long) currently_testing + written); buf_used += got; save_ptr += got * block_size; test_ptr += got * block_size; currently_testing += got; - if (got != try) - bb_count += bb_output(currently_testing++); + if (got != try) { + if (recover_block == ~0U) + recover_block = currently_testing - + got + blocks_at_once; + continue; + } check_for_more: /* @@ -757,10 +904,15 @@ static unsigned int test_nd (int dev, blk_t last_block, * around, and we're not done yet testing the disk, go * back and get some more blocks. */ - if ((buf_used != blocks_at_once) && + if ((buf_used != granularity) && (currently_testing < last_block)) continue; + if (currently_testing >= recover_block) { + granularity = blocks_at_once; + recover_block = ~0; + } + flush_bufs(); save_currently_testing = currently_testing; @@ -788,7 +940,7 @@ static unsigned int test_nd (int dev, blk_t last_block, try = test_record[used2].num; used2++; } - + got = do_read (dev, read_ptr, try, block_size, currently_testing); @@ -797,12 +949,12 @@ static unsigned int test_nd (int dev, blk_t last_block, for (i = 0; i < got; ++i) if (memcmp (test_ptr+i*block_size, read_ptr+i*block_size, block_size)) - bb_count += bb_output(currently_testing + i); + bb_count += bb_output(currently_testing + i, CORRUPTION_ERROR); if (got < try) { - bb_count += bb_output(currently_testing + got); + bb_count += bb_output(currently_testing + got, READ_ERROR); got++; } - + /* write back original data */ do_write (dev, save_ptr, got, block_size, currently_testing); @@ -881,7 +1033,7 @@ static unsigned int parse_uint(const char *str, const char *descr) { char *tmp; unsigned long ret; - + errno = 0; ret = strtoul(str, &tmp, 0); if (*tmp || errno || (ret > UINT_MAX) || @@ -902,7 +1054,7 @@ int main (int argc, char ** argv) FILE * in = NULL; int block_size = 1024; unsigned int blocks_at_once = 64; - blk_t last_block, first_block; + blk64_t last_block, first_block; int num_passes = 0; int passes_clean = 0; int dev; @@ -911,8 +1063,9 @@ int main (int argc, char ** argv) unsigned int (*test_func)(int, blk_t, int, blk_t, unsigned int); - int open_flag = 0; + int open_flag; long sysval; + unsigned long long inblk; setbuf(stdout, NULL); setbuf(stderr, NULL); @@ -921,6 +1074,7 @@ int main (int argc, char ** argv) setlocale(LC_CTYPE, ""); bindtextdomain(NLS_CAT_NAME, LOCALEDIR); textdomain(NLS_CAT_NAME); + set_com_err_gettext(gettext); #endif srandom((unsigned int)time(NULL)); /* simple randomness is enough */ test_func = test_ro; @@ -936,18 +1090,13 @@ int main (int argc, char ** argv) sys_page_size = sysval; #endif /* _SC_PAGESIZE */ #endif /* HAVE_SYSCONF */ - + if (argc && *argv) program_name = *argv; - while ((c = getopt (argc, argv, "b:d:e:fi:o:svwnc:p:h:t:X")) != EOF) { + while ((c = getopt (argc, argv, "b:d:e:fi:o:svwnc:p:h:t:BX")) != EOF) { switch (c) { case 'b': block_size = parse_uint(optarg, "block size"); - if (block_size > 4096) { - com_err (program_name, 0, - _("bad block size - %s"), optarg); - exit (1); - } break; case 'f': force++; @@ -981,12 +1130,22 @@ int main (int argc, char ** argv) break; case 'e': max_bb = parse_uint(optarg, "max bad block count"); + if (max_bb > MAX_BAD_BLOCKS) { + com_err (program_name, 0, + _("Too big max bad blocks count %u - " + "maximum is %u"), max_bb, + MAX_BAD_BLOCKS); + exit (1); + } + /* 0 really means unlimited but we cannot do that much... */ + if (max_bb == 0) + max_bb = MAX_BAD_BLOCKS; break; case 'd': d_flag = parse_uint(optarg, "read delay factor"); break; case 'p': - num_passes = parse_uint(optarg, + num_passes = parse_uint(optarg, "number of clean passes"); break; case 'h': @@ -996,7 +1155,7 @@ int main (int argc, char ** argv) if (t_flag + 1 > t_max) { unsigned int *t_patts_new; - t_patts_new = realloc(t_patts, sizeof(int) * + t_patts_new = realloc(t_patts, sizeof(int) * (t_max + T_INC)); if (!t_patts_new) { com_err(program_name, ENOMEM, @@ -1017,6 +1176,9 @@ int main (int argc, char ** argv) t_patts[t_flag++] = pattern; } break; + case 'B': + use_buffered_io = 1; + break; case 'X': exclusive_ok++; break; @@ -1026,33 +1188,46 @@ int main (int argc, char ** argv) } if (!w_flag) { if (t_flag > 1) { - com_err(program_name, 0, - _("Maximum of one test_pattern may be specified " - "in read-only mode")); + com_err(program_name, 0, "%s", + _("Maximum of one test_pattern may be " + "specified in read-only mode")); exit(1); } if (t_patts && (t_patts[0] == (unsigned int) ~0)) { - com_err(program_name, 0, - _("Random test_pattern is not allowed " - "in read-only mode")); + com_err(program_name, 0, "%s", + _("Random test_pattern is not allowed " + "in read-only mode")); exit(1); } } + if ((block_size <= 0) || (block_size > (1 << 24)) || + (block_size & (block_size - 1))) { + com_err(program_name, 0, _("Invalid block size: %d\n"), + block_size); + exit(1); + } + if ((blocks_at_once <= 0) || + (((unsigned long long) block_size * blocks_at_once) > 0xFFFFFFFF)) { + com_err(program_name, 0, _("Invalid blocks_at_once: %d\n"), + blocks_at_once); + exit(1); + } + if (optind > argc - 1) usage(); device_name = argv[optind++]; if (optind > argc - 1) { - errcode = ext2fs_get_device_size(device_name, + errcode = ext2fs_get_device_size2(device_name, block_size, &last_block); if (errcode == EXT2_ET_UNIMPLEMENTED) { - com_err(program_name, 0, + com_err(program_name, 0, "%s", _("Couldn't determine device size; you " "must specify\nthe size manually\n")); exit(1); } if (errcode) { - com_err(program_name, errcode, + com_err(program_name, errcode, "%s", _("while trying to determine device size")); exit(1); } @@ -1067,14 +1242,23 @@ int main (int argc, char ** argv) first_block = parse_uint(argv[optind], _("first block")); } else first_block = 0; if (first_block >= last_block) { - com_err (program_name, 0, _("invalid starting block (%lu): must be less than %lu"), - (unsigned long) first_block, (unsigned long) last_block); + com_err (program_name, 0, _("invalid starting block (%llu): must be less than %llu"), + (unsigned long long) first_block, + (unsigned long long) last_block); exit (1); } + /* ext2 badblocks file can't handle large values */ + if (last_block >> 32) { + com_err(program_name, EOVERFLOW, + _("invalid end block (%llu): must be 32-bit value"), + (unsigned long long) last_block); + exit(1); + } if (w_flag) check_mount(device_name); - - open_flag = w_flag ? O_RDWR : O_RDONLY; + + gettimeofday(&time_start, 0); + open_flag = O_LARGEFILE | (w_flag ? O_RDWR : O_RDONLY); dev = open (device_name, open_flag); if (dev == -1) { com_err (program_name, errno, _("while trying to open %s"), @@ -1121,23 +1305,35 @@ int main (int argc, char ** argv) errcode = ext2fs_badblocks_list_create(&bb_list,0); if (errcode) { - com_err (program_name, errcode, - _("while creating in-memory bad blocks list")); + com_err(program_name, errcode, "%s", + _("while creating in-memory bad blocks list")); exit (1); } if (in) { for(;;) { - switch(fscanf (in, "%u\n", &next_bad)) { + switch (fscanf(in, "%llu\n", &inblk)) { case 0: - com_err (program_name, 0, "input file - bad format"); + com_err(program_name, 0, "%s", + _("input file - bad format")); exit (1); case EOF: break; default: + if (inblk >> 32) { + com_err(program_name, + EOVERFLOW, "%s", + _("while adding to in-memory " + "bad block list")); + exit(1); + } + next_bad = inblk; errcode = ext2fs_badblocks_list_add(bb_list,next_bad); if (errcode) { - com_err (program_name, errcode, _("while adding to in-memory bad block list")); + com_err(program_name, errcode, + "%s", + _("while adding to in-memory " + "bad block list")); exit (1); } continue; @@ -1158,19 +1354,17 @@ int main (int argc, char ** argv) passes_clean = 0; else ++passes_clean; - + if (v_flag) fprintf(stderr, - _("Pass completed, %u bad blocks found.\n"), - bb_count); + _("Pass completed, %u bad blocks found. (%d/%d/%d errors)\n"), + bb_count, num_read_errors, num_write_errors, num_corruption_errors); } while (passes_clean < num_passes); close (dev); if (out != stdout) fclose (out); - if (t_patts) - free(t_patts); + free(t_patts); return 0; } -