X-Git-Url: https://git.whamcloud.com/?a=blobdiff_plain;f=misc%2Fbadblocks.c;h=afeb3da9d1ddd464e18ce3531ca491df2207a9e9;hb=b5bdc0432f064c10db6bb6fa1b5f370b3c1f0f2c;hp=32ff3b2e68476ce675d230583dc13ba9380f7b7a;hpb=175d43beada4f5c971fa4b495380620644705282;p=tools%2Fe2fsprogs.git diff --git a/misc/badblocks.c b/misc/badblocks.c index 32ff3b2..afeb3da 100644 --- a/misc/badblocks.c +++ b/misc/badblocks.c @@ -29,12 +29,11 @@ * list. (Work done by David Beattie) */ +#ifndef _GNU_SOURCE #define _GNU_SOURCE /* for O_DIRECT */ - -#ifndef O_LARGEFILE -#define O_LARGEFILE 0 #endif +#include "config.h" #include #include #ifdef HAVE_GETOPT_H @@ -51,6 +50,9 @@ extern int optind; #include #include #include +#ifdef HAVE_MBSTOWCS +#include +#endif #include #include @@ -60,34 +62,42 @@ extern int optind; #include "ext2fs/ext2_io.h" #include "ext2fs/ext2_fs.h" #include "ext2fs/ext2fs.h" -#include "nls-enable.h" +#include "support/nls-enable.h" -const char * program_name = "badblocks"; -const char * done_string = N_("done \n"); +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif + +/* Maximum number of bad blocks we support */ +#define MAX_BAD_BLOCKS (INT_MAX/2) -static int v_flag = 0; /* verbose */ -static int w_flag = 0; /* do r/w test: 0=no, 1=yes, +static const char * program_name = "badblocks"; +static const char * done_string = N_("done \n"); + +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 use_buffered_io = 0; -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" +"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"), @@ -105,17 +115,22 @@ static void exclusive_usage(void) 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 @@ -137,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; @@ -159,6 +174,14 @@ static int bb_output (blk_t bad) 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; } @@ -196,16 +219,26 @@ static void print_status(void) { 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"), + _("%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)); + time_diff_format(&time_end, &time_start, diff_buf), + num_read_errors, + num_write_errors, + num_corruption_errors); #ifdef HAVE_MBSTOWCS - len = mbstowcs(NULL, line_buf, sizeof(line_buf)); + 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); @@ -266,6 +299,7 @@ static void set_o_direct(int dev, unsigned char *buffer, size_t size, 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; @@ -280,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; } @@ -349,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) @@ -422,7 +457,7 @@ 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); @@ -446,7 +481,8 @@ static void flush_bufs(void) #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, @@ -465,8 +501,8 @@ static unsigned int test_ro (int dev, blk_t last_block, 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 { @@ -480,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); @@ -497,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); } @@ -531,14 +567,14 @@ 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++); + bb_count += bb_output(currently_testing++, READ_ERROR); currently_testing += got; if (got != try) { try = 1; - if (recover_block == ~0) + if (recover_block == ~0U) recover_block = currently_testing - got + blocks_at_once; continue; @@ -571,7 +607,7 @@ 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; + blk_t recover_block = ~0; /* set up abend handler */ capture_terminate(NULL); @@ -580,7 +616,8 @@ static unsigned int test_rw (int dev, blk_t last_block, 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); } @@ -610,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); } @@ -624,11 +661,11 @@ static unsigned int test_rw (int dev, blk_t last_block, print_status(); if (got == 0 && try == 1) - bb_count += bb_output(currently_testing++); + bb_count += bb_output(currently_testing++, WRITE_ERROR); currently_testing += got; if (got != try) { try = 1; - if (recover_block == ~0) + if (recover_block == ~0U) recover_block = currently_testing - got + blocks_at_once; continue; @@ -652,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); } @@ -663,34 +700,23 @@ static unsigned int test_rw (int dev, blk_t last_block, got = do_read (dev, read_buffer, try, block_size, currently_testing); if (got == 0 && try == 1) - bb_count += bb_output(currently_testing++); + bb_count += bb_output(currently_testing++, READ_ERROR); currently_testing += got; if (got != try) { try = 1; - if (recover_block == ~0) + 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; + 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); - } - currently_testing += got; - if (got != try) { - try = 1; - if (!recover_block) - recover_block = currently_testing - - got + blocks_at_once; - continue; - } else if (currently_testing == recover_block) { - try = blocks_at_once; - recover_block = 0; + bb_count += bb_output(currently_testing+i, CORRUPTION_ERROR); } if (v_flag > 1) print_status(); @@ -731,14 +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; - int granularity = blocks_at_once; - blk_t recover_block = 0; + 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 { @@ -746,9 +772,10 @@ 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); } @@ -809,7 +836,7 @@ 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); } @@ -831,8 +858,7 @@ 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 ((currently_testing == 0) || - (recover_block == 0)) + if (recover_block == ~0U) recover_block = currently_testing + blocks_at_once; if (granularity != 1) { @@ -840,7 +866,7 @@ static unsigned int test_nd (int dev, blk_t last_block, 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; } @@ -866,8 +892,7 @@ static unsigned int test_nd (int dev, blk_t last_block, test_ptr += got * block_size; currently_testing += got; if (got != try) { - try = 1; - if (!recover_block) + if (recover_block == ~0U) recover_block = currently_testing - got + blocks_at_once; continue; @@ -885,7 +910,7 @@ static unsigned int test_nd (int dev, blk_t last_block, if (currently_testing >= recover_block) { granularity = blocks_at_once; - recover_block = 0; + recover_block = ~0; } flush_bufs(); @@ -924,9 +949,9 @@ 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++; } @@ -1040,6 +1065,7 @@ int main (int argc, char ** argv) unsigned int); int open_flag; long sysval; + unsigned long long inblk; setbuf(stdout, NULL); setbuf(stderr, NULL); @@ -1048,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; @@ -1103,6 +1130,16 @@ 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"); @@ -1151,18 +1188,31 @@ 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++]; @@ -1171,13 +1221,13 @@ int main (int argc, char ** argv) 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); } @@ -1192,10 +1242,18 @@ 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); @@ -1247,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; @@ -1287,8 +1357,8 @@ int main (int argc, char ** argv) 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); @@ -1298,4 +1368,3 @@ int main (int argc, char ** argv) free(t_patts); return 0; } -