diff --git a/misc/ChangeLog b/misc/ChangeLog index 56b4e8fa..78fa469d 100644 --- a/misc/ChangeLog +++ b/misc/ChangeLog @@ -1,5 +1,13 @@ 2000-04-03 Theodore Ts'o + * badblocks.8.in: Updated manual page with suggestions from David + Beattie. + + * badblocks.c (test_nd): Generalized cleanup and bug-fixes. We + now explicitly clear out the signal handlers to prevent a + longjmp to a deactivated stack frame. + (test_rw): Fixed a signed vs. unsigned comparison error. + * badblocks.8.in, chattr.1.in, dumpe2fs.8.in, lsattr.1.in, mklost+found.8.in, tune2fs.8.in: Update Remy Card's e-mail address. diff --git a/misc/badblocks.8.in b/misc/badblocks.8.in index 9dfe0f27..264ccd82 100644 --- a/misc/badblocks.8.in +++ b/misc/badblocks.8.in @@ -44,10 +44,37 @@ Specify the size of blocks in bytes. .TP .BI \-c " number of blocks" is the number of blocks which are tested at a time. The default is 16. +Increasing this number will increase the efficiency of +.B badblocks +but also will increase its memory usage. +.B Badblocks +needs memory proportional to the number of blocks tested at once, in +read-only mode, proportional to twice that number in read-write mode, +and proportional to three times that number in non-destructive read-write +mode. If you set the number-of-blocks parameter to too high a value, +.B badblocks +will exit almost immediately with an out-of-memory error "while allocating +buffers". If you set it too low, however, for a non-destructive-write-mode +test, then you are less likely to detect questionable blocks on an unreliable +hard drive (i.e., one that holds its data for a while even in bad areas, but +which loses the data in truely bad blocks, over time), since the +read-and-compare stage of the test will occur sooner after the data is written. .TP .BI \-i " input_file" -Read a list of already existing known bad blocks. Badblocks will skip -testing these blocks since they are known bad. +Read a list of already existing known bad blocks. +.B Badblocks +will skip testing these blocks since they are known to be bad. If +.I input_file +is specified as "-", the list will be read from the standard input. +Blocks listed in this list will be omitted from the list of +.I new +bad blocks produced on the standard output or in the output file. +The +.B \-b +option of +.BR dumpe2fs (8) +can be used to retrieve the list of blocks currently marked bad on +an existing filesystem, in a format suitable for use with this option. .TP .BI \-o " output_file" Write the list of bad blocks to the specified file. Without this option, @@ -62,8 +89,11 @@ or .BR mke2fs (8). .TP .BI \-p " num_passes" -Repeat scanning the disk until there are no new blocks discovered after -num_passes scans of the disk. +Repeat scanning the disk until there are no new blocks discovered in +num_passes consecutive scans of the disk. +Default is 0, meaning +.B badblocks +will exit after the first pass. .TP .B \-n Use non-destructive read-write mode. @@ -84,12 +114,15 @@ every block of the device, reading every block and comparing the contents. Never use the .B \-w option on an device containing an existing file system. -This option erases data! +This option erases data! If you want to do write-mode testing on +an existing file system, use the +.B \-n +option. It is slower, but it will preserve your data. .SH AUTHOR .B badblocks was written by Remy Card . Current maintainer is Theodore Ts'o . Non-destructive read/write test -implemented by David Beattie . +implemented by David Beattie . .SH AVAILABILITY .B badblocks is part of the e2fsprogs package and is available for anonymous diff --git a/misc/badblocks.c b/misc/badblocks.c index 1a47984b..475c9c31 100644 --- a/misc/badblocks.c +++ b/misc/badblocks.c @@ -57,9 +57,13 @@ const char * program_name = "badblocks"; const char * done_string = N_("done \n"); -int v_flag = 0; /* verbose */ -int w_flag = 0; /* do r/w test: 0=no, 1=yes, 2=non-destructive */ -int s_flag = 0; /* show progress of test */ +static int v_flag = 0; /* verbose */ +static int w_flag = 0; /* do r/w test: 0=no, 1=yes, + * 2=non-destructive */ +static int s_flag = 0; /* show progress of test */ + +static char *blkbuf; /* Allocation array for bad block testing */ + static void usage(void) { @@ -141,6 +145,17 @@ static void capture_terminate (jmp_buf term_addr) signal (SIGUSR2, terminate_intr); } +static void uncapture_terminate() +{ + terminate_addr = NULL; + signal (SIGHUP, SIG_DFL); + signal (SIGINT, SIG_DFL); + signal (SIGPIPE, SIG_DFL); + signal (SIGTERM, SIG_DFL); + signal (SIGUSR1, SIG_DFL); + signal (SIGUSR2, SIG_DFL); +} + /* * Perform a read of a sequence of blocks; return the number of blocks * successfully sequentially read. @@ -369,9 +384,9 @@ static unsigned int test_rw (int dev, unsigned long blocks_count, if (v_flag > 1) print_status(); if ((read (dev, buffer + block_size, block_size) - < block_size) || + != block_size) || memcmp(buffer, buffer + block_size, block_size)) - bb_count = bb_output(currently_testing); + bb_count += bb_output(currently_testing); } num_blocks = 0; alarm (0); @@ -396,11 +411,8 @@ static unsigned int test_nd (int dev, unsigned long blocks_count, jmp_buf terminate_env; errcode_t errcode; /* These are static to prevent being clobbered by the longjmp */ - static long buf_used; - static unsigned int bb_count; - - bb_count = 0; - buf_used = 0; + static long buf_used = 0; + static unsigned int bb_count = 0; errcode = ext2fs_badblocks_list_iterate_begin(bb_list,&bb_iter); if (errcode) { @@ -441,132 +453,13 @@ static unsigned int test_nd (int dev, unsigned long blocks_count, if (v_flag <= 1) alarm_intr(SIGALRM); } - if (! setjmp(terminate_env)) { - /* set up abend handler */ - capture_terminate(terminate_env); - - buf_used = 0; save_ptr = blkbuf; - test_ptr = blkbuf + (blocks_at_once * block_size); - currently_testing = from_count; - num_blocks = blocks_count; - - while (currently_testing < blocks_count) { - try = blocks_at_once - buf_used; - if (next_bad) { - if (currently_testing == next_bad) { - /* fprintf (out, "%lu\n", nextbad); */ - ext2fs_badblocks_list_iterate (bb_iter, &next_bad); - bufblk[buf_used] = currently_testing++; - goto test_full_buf; - } - else if (currently_testing + try > next_bad) - try = next_bad - currently_testing; - } - if (currently_testing + try > blocks_count) - try = blocks_count - currently_testing; - got = do_read (dev, save_ptr, try, block_size, - currently_testing); - - /* if reading succeeded, write the test data */ - if (got) { - long written; - - written = do_write (dev, test_ptr, got, - block_size, - currently_testing); - if (written != got) - com_err (program_name, errno, - _("during test data write, block %lu"), - currently_testing + written); - } - - bufblk[buf_used] = currently_testing; - bufblks[buf_used] = got; - 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++); - - test_full_buf: - /* - * If there's room for more blocks to be - * tested this around, and we're not done yet - * testing the disk, go back and get some - * more blocks. - */ - if ((buf_used != blocks_at_once) && - (currently_testing != blocks_count)) - continue; - - flush_bufs (dev, 1); - - /* - * for each contiguous block that we read into - * the buffer (and wrote test data into - * afterwards), read it back (looping if - * necessary, to get past newly discovered - * unreadable blocks, of which there should be - * none, but with a hard drive which is - * unreliable, it has happened), and compare - * with the test data that was written; output - * to the bad block list if it doesn't match. - */ - used2 = 0; - save_ptr = blkbuf; - test_ptr = blkbuf + (blocks_at_once * block_size); - read_ptr = blkbuf + (2 * blocks_at_once * block_size); - currently_testing = bufblk[0]; - try = bufblks[0]; - while (currently_testing < blocks_count) { - - got = do_read (dev, read_ptr, try, - block_size, currently_testing); - - /* test the comparison between all the - blocks successfully read */ - for (i = 0; i < got; ++i) - if (memcmp (test_ptr, read_ptr, - block_size)) - bb_count += bb_output(currently_testing + i); - if (got == 0) { - bb_count += bb_output(currently_testing); - got = 1; - } - - /* when done, write back original data */ - do_write (dev, save_ptr, got, block_size, - currently_testing); - - currently_testing += got; - save_ptr += bufblks[got] * block_size; - test_ptr += bufblks[got] * block_size; - read_ptr += bufblks[got] * block_size; - try -= got; - - if (try == 0) { - used2 += bufblks[used2]; - if (used2 >= blocks_at_once) - break; - currently_testing = bufblk[used2]; - try = bufblks[used2]; - } - } - - /* empty the buffer so it can be reused */ - buf_used = 0; - } - num_blocks = 0; - alarm(0); - if (s_flag || v_flag > 1) - fprintf(stderr, _(done_string)); - - } else { - /* abnormal termination by a signal is handled here */ - /* logic is: write back the data that is in the buffer, - so that we can maintain data integrity on disk. Then - abort. */ + if (setjmp(terminate_env)) { + /* + * Abnormal termination by a signal is handled here. + * buf_used will always contain the number of blocks + * saved in a non-destructive test, so they can be + * rewritten back to the disk. + */ long buf_written; fprintf(stderr, _("Interrupt caught, cleaning up\n")); @@ -581,6 +474,124 @@ static unsigned int test_nd (int dev, unsigned long blocks_count, fflush (out); exit(1); } + + /* set up abend handler */ + capture_terminate(terminate_env); + + buf_used = 0; save_ptr = blkbuf; + test_ptr = blkbuf + (blocks_at_once * block_size); + currently_testing = from_count; + num_blocks = blocks_count; + + while (currently_testing < blocks_count) { + try = blocks_at_once - buf_used; + if (next_bad) { + if (currently_testing == next_bad) { + /* fprintf (out, "%lu\n", nextbad); */ + ext2fs_badblocks_list_iterate (bb_iter, &next_bad); + bufblk[buf_used] = currently_testing++; + goto test_full_buf; + } + else if (currently_testing + try > next_bad) + try = next_bad - currently_testing; + } + if (currently_testing + try > blocks_count) + try = blocks_count - currently_testing; + got = do_read (dev, save_ptr, try, block_size, + currently_testing); + + /* if reading succeeded, write the test data */ + if (got) { + long written; + + written = do_write (dev, test_ptr, got, block_size, + currently_testing); + if (written != got) + com_err (program_name, errno, + _("during test data write, block %lu"), + currently_testing + written); + } + + bufblk[buf_used] = currently_testing; + bufblks[buf_used] = got; + 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++); + + test_full_buf: + /* + * If there's room for more blocks to be tested this + * around, and we're not done yet testing the disk, go + * back and get some more blocks. + */ + if ((buf_used != blocks_at_once) && + (currently_testing != blocks_count)) + continue; + + flush_bufs (dev, 1); + + /* + * for each contiguous block that we read into the + * buffer (and wrote test data into afterwards), read + * it back (looping if necessary, to get past newly + * discovered unreadable blocks, of which there should + * be none, but with a hard drive which is unreliable, + * it has happened), and compare with the test data + * that was written; output to the bad block list if + * it doesn't match. + */ + used2 = 0; + save_ptr = blkbuf; + test_ptr = blkbuf + (blocks_at_once * block_size); + read_ptr = blkbuf + (2 * blocks_at_once * block_size); + currently_testing = bufblk[0]; + try = bufblks[0]; + + while (currently_testing < blocks_count) { + got = do_read (dev, read_ptr, try, + block_size, currently_testing); + + /* test the comparison between all the + blocks successfully read */ + 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); + if (got < try) { + bb_count += bb_output(currently_testing + got); + got++; + } + + /* when done, write back original data */ + do_write (dev, save_ptr, got, block_size, + currently_testing); + + currently_testing += got; + save_ptr += got * block_size; + test_ptr += got * block_size; + read_ptr += got * block_size; + try -= got; + + if (try == 0) { + used2 += bufblks[used2]; + if (used2 >= blocks_at_once) + break; + currently_testing = bufblk[used2]; + try = bufblks[used2]; + } + } + + /* empty the buffer so it can be reused */ + buf_used = 0; + } + num_blocks = 0; + alarm(0); + uncapture_terminate(); + if (s_flag || v_flag > 1) + fprintf(stderr, _(done_string)); fflush(stderr); free(blkbuf); @@ -608,6 +619,10 @@ int main (int argc, char ** argv) int passes_clean = 0; int dev; errcode_t errcode; + unsigned int (*test_func)(int dev, unsigned long blocks_count, + int block_size, unsigned long from_count, + unsigned long blocks_at_once); + size_t buf_size; setbuf(stdout, NULL); setbuf(stderr, NULL); @@ -616,6 +631,8 @@ int main (int argc, char ** argv) bindtextdomain(NLS_CAT_NAME, LOCALEDIR); textdomain(NLS_CAT_NAME); #endif + test_func = test_ro; + if (argc && *argv) program_name = *argv; while ((c = getopt (argc, argv, "b:i:o:svwnc:p:h:")) != EOF) { @@ -641,10 +658,15 @@ int main (int argc, char ** argv) v_flag++; break; case 'w': - if (! w_flag) - w_flag = 1; + if (w_flag) + usage(); + test_func = test_rw; + w_flag = 1; break; case 'n': + if (w_flag) + usage(); + test_func = test_nd; w_flag = 2; break; case 'c': @@ -768,15 +790,13 @@ int main (int argc, char ** argv) do { unsigned int bb_count; - (bb_count = - - (w_flag == 2 ? test_nd - : w_flag ? test_rw - : test_ro) - (dev, blocks_count, block_size, from_count, - blocks_at_once) - ) ? passes_clean = 0 : ++passes_clean; - + bb_count = test_func(dev, blocks_count, block_size, + from_count, blocks_at_once); + if (bb_count) + passes_clean = 0; + else + ++passes_clean; + if (v_flag) fprintf(stderr, _("Pass completed, %u bad blocks found.\n"),