ChangeLog, badblocks.8.in, badblocks.c:

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.
This commit is contained in:
Theodore Ts'o 2000-04-03 16:01:11 +00:00
parent 01fbc70141
commit 4d003982f9
3 changed files with 214 additions and 153 deletions

View File

@ -1,5 +1,13 @@
2000-04-03 Theodore Ts'o <tytso@valinux.com>
* 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.

View File

@ -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 <Remy.Card@linux.org>. Current maintainer is
Theodore Ts'o <tytso@alum.mit.edu>. Non-destructive read/write test
implemented by David Beattie <dbeattie@usa.net>.
implemented by David Beattie <dbeattie@softhome.net>.
.SH AVAILABILITY
.B badblocks
is part of the e2fsprogs package and is available for anonymous

View File

@ -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"),