mirror of
https://git.kernel.org/pub/scm/fs/ext2/e2fsprogs.git
synced 2024-12-14 12:25:51 +08:00
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:
parent
01fbc70141
commit
4d003982f9
@ -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.
|
||||
|
@ -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
|
||||
|
314
misc/badblocks.c
314
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"),
|
||||
|
Loading…
Reference in New Issue
Block a user