badblocks.c, badblocks.8.in: Functional enhancements contributed
by Thayne Harbaugh. These patches allow the -t option to
control the test pattern(s) used when checking the disk.
Test patterns may be specified in all modes (r/w, r/o and
non-destructive).
diff --git a/misc/badblocks.c b/misc/badblocks.c
index 72ec2f9..8a81202 100644
--- a/misc/badblocks.c
+++ b/misc/badblocks.c
@@ -62,10 +62,14 @@
* 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 long *t_patts = NULL; /* test patterns */
+#define T_INC 32
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] device [last_block [start_block]]\n"),
+ fprintf(stderr, _("Usage: %s [-b block_size] [-i input_file] [-o output_file] [-svwnf]\n [-c blocks_at_once] [-p num_passes] [-t test_pattern [-t test_pattern [...]]]\n device [last_block [start_block]]\n"),
program_name);
exit (1);
}
@@ -155,6 +159,37 @@
signal (SIGUSR2, SIG_DFL);
}
+static void pattern_fill(unsigned char *buffer, int pattern, size_t n)
+{
+ int i, nb;
+ unsigned char bpattern[4], *ptr;
+
+ if (pattern == -1) {
+ for (ptr = buffer; ptr < buffer + n; ptr++) {
+ (*ptr) = random() % (1 << (8 * sizeof(char)));
+ }
+ if (s_flag | v_flag)
+ fprintf(stderr, _("Testing with random pattern: "));
+ } else {
+ bpattern[0] = 0;
+ for (i = 0; i < sizeof(bpattern); i++) {
+ if (pattern == 0)
+ break;
+ bpattern[i] = pattern & 0xFF;
+ pattern = pattern >> 8;
+ }
+ nb = i ? (i-1) : 0;
+ for (ptr = buffer, i = nb; ptr < buffer + n; ptr++) {
+ *ptr = bpattern[i--];
+ if (i < 0)
+ i = nb;
+ }
+ if (s_flag | v_flag)
+ fprintf(stderr, _("Testing with pattern 0x%02x%02x%02x%02x: "),
+ buffer[0], buffer[1], buffer[2], buffer[3]);
+ }
+}
+
/*
* Perform a read of a sequence of blocks; return the number of blocks
* successfully sequentially read.
@@ -241,22 +276,30 @@
ext2fs_badblocks_list_iterate (bb_iter, &next_bad);
} while (next_bad && next_bad < from_count);
- blkbuf = malloc (blocks_at_once * block_size);
+ if (t_flag) {
+ blkbuf = malloc ((blocks_at_once + 1) * block_size);
+ } else {
+ blkbuf = malloc (blocks_at_once * block_size);
+ }
if (!blkbuf)
{
com_err (program_name, ENOMEM, _("while allocating buffers"));
exit (1);
}
- flush_bufs();
if (v_flag) {
- fprintf(stderr, _("Checking for bad blocks in read-only mode\n"));
- fprintf (stderr, _("From block %lu to %lu\n"), from_count,
+ fprintf (stderr, _("Checking blocks %lu to %lu\n"), from_count,
last_block);
}
+ if (t_flag) {
+ fprintf(stderr, _("Checking for bad blocks in read-only mode\n"));
+ pattern_fill(blkbuf + blocks_at_once * block_size,
+ t_patts[0], block_size);
+ }
+ flush_bufs();
try = blocks_at_once;
currently_testing = from_count;
num_blocks = last_block;
- if (s_flag || v_flag > 1) {
+ if (!t_flag && (s_flag || v_flag)) {
fprintf(stderr,
_("Checking for bad blocks (read-only test): "));
if (v_flag <= 1)
@@ -277,6 +320,16 @@
if (currently_testing + try > last_block)
try = last_block - currently_testing;
got = do_read (dev, blkbuf, try, block_size, currently_testing);
+ if (t_flag) {
+ /* test the comparison between all the
+ blocks successfully read */
+ int i;
+ for (i = 0; i < got; ++i)
+ if (memcmp (blkbuf+i*block_size,
+ blkbuf+blocks_at_once*block_size,
+ block_size))
+ bb_count += bb_output(currently_testing + i);
+ }
currently_testing += got;
if (got == try) {
try = blocks_at_once;
@@ -290,7 +343,7 @@
}
num_blocks = 0;
alarm(0);
- if (s_flag || v_flag > 1)
+ if (s_flag || v_flag)
fprintf(stderr, _(done_string));
fflush (stderr);
@@ -305,9 +358,10 @@
int block_size, unsigned long from_count,
unsigned long blocks_at_once)
{
- int i;
char * buffer;
- static unsigned char pattern[] = {0xaa, 0x55, 0xff, 0x00};
+ const long patterns[] = {0xaa, 0x55, 0xff, 0x00};
+ const long *pattern;
+ int nr_pattern, pat_idx;
unsigned int bb_count = 0;
buffer = malloc (2 * block_size);
@@ -325,11 +379,15 @@
fprintf(stderr, _("From block %lu to %lu\n"),
from_count, last_block);
}
- for (i = 0; i < sizeof (pattern); i++) {
- memset (buffer, pattern[i], block_size);
- if (s_flag | v_flag)
- fprintf (stderr, _("Writing pattern 0x%08x: "),
- *((int *) buffer));
+ if (t_flag) {
+ pattern = t_patts;
+ nr_pattern = t_flag;
+ } else {
+ pattern = patterns;
+ nr_pattern = sizeof(patterns) / sizeof(patterns[0]);
+ }
+ for (pat_idx = 0; pat_idx < nr_pattern; pat_idx++) {
+ pattern_fill(buffer, pattern[pat_idx], block_size);
num_blocks = last_block;
currently_testing = from_count;
if (s_flag && v_flag <= 1)
@@ -382,7 +440,7 @@
fprintf(stderr, _(done_string));
flush_bufs();
}
-
+ uncapture_terminate();
free(buffer);
return bb_count;
}
@@ -397,8 +455,10 @@
unsigned long blocks_at_once)
{
char *blkbuf, *save_ptr, *test_ptr, *read_ptr;
- char * ptr;
int try, i;
+ const long patterns[] = {-1};
+ const long *pattern;
+ int nr_pattern, pat_idx;
long got, used2, written, save_currently_testing;
struct saved_blk_record *test_record;
/* This is static to prevent being clobbered by the longjmp */
@@ -426,16 +486,6 @@
}
num_saved = 0;
- /* inititalize the test data randomly: */
- if (v_flag) {
- fprintf (stderr, _("Initializing random test data\n"));
- }
- for(ptr = blkbuf + blocks_at_once * block_size;
- ptr < blkbuf + 2 * blocks_at_once * block_size;
- ++ptr) {
- (*ptr) = random() % (1 << (8 * sizeof(char)));
- }
-
flush_bufs();
if (v_flag) {
fprintf (stderr,
@@ -443,9 +493,7 @@
fprintf (stderr, _("From block %lu to %lu\n"), from_count, last_block);
}
if (s_flag || v_flag > 1) {
- fprintf(stderr, _("Checking for bad blocks (non-destructive read-write test): "));
- if (v_flag <= 1)
- alarm_intr(SIGALRM);
+ fprintf(stderr, _("Checking for bad blocks (non-destructive read-write test)\n"));
}
if (setjmp(terminate_env)) {
/*
@@ -467,134 +515,149 @@
/* set up abend handler */
capture_terminate(terminate_env);
- buf_used = 0;
- bb_count = 0;
- save_ptr = blkbuf;
- test_ptr = blkbuf + (blocks_at_once * block_size);
- currently_testing = from_count;
- num_blocks = last_block;
+ if (t_flag) {
+ pattern = t_patts;
+ nr_pattern = t_flag;
+ } else {
+ pattern = patterns;
+ nr_pattern = sizeof(patterns) / sizeof(patterns[0]);
+ }
+ for (pat_idx = 0; pat_idx < nr_pattern; pat_idx++) {
+ pattern_fill(blkbuf + blocks_at_once * block_size,
+ pattern[pat_idx], blocks_at_once * block_size);
- while (currently_testing < last_block) {
- 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);
- currently_testing++;
+ buf_used = 0;
+ bb_count = 0;
+ save_ptr = blkbuf;
+ test_ptr = blkbuf + (blocks_at_once * block_size);
+ currently_testing = from_count;
+ num_blocks = last_block;
+ if (s_flag && v_flag <= 1)
+ alarm_intr(SIGALRM);
+
+ while (currently_testing < last_block) {
+ 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);
+ currently_testing++;
+ goto check_for_more;
+ }
+ else if (currently_testing + try > next_bad)
+ try = next_bad - currently_testing;
+ }
+ if (currently_testing + try > last_block)
+ try = last_block - currently_testing;
+ got = do_read (dev, save_ptr, try, block_size,
+ currently_testing);
+ if (got == 0) {
+ /* First block must have been bad. */
+ bb_count += bb_output(currently_testing++);
goto check_for_more;
}
- else if (currently_testing + try > next_bad)
- try = next_bad - currently_testing;
- }
- if (currently_testing + try > last_block)
- try = last_block - currently_testing;
- got = do_read (dev, save_ptr, try, block_size,
- currently_testing);
- if (got == 0) {
- /* First block must have been bad. */
- bb_count += bb_output(currently_testing++);
- goto check_for_more;
- }
- /*
- * Note the fact that we've saved this much data
- * *before* we overwrite it with test data
- */
- test_record[num_saved].block = currently_testing;
- test_record[num_saved].num = got;
- num_saved++;
+ /*
+ * Note the fact that we've saved this much data
+ * *before* we overwrite it with test data
+ */
+ test_record[num_saved].block = currently_testing;
+ test_record[num_saved].num = got;
+ num_saved++;
- /* Write the test data */
- 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);
+ /* Write the test data */
+ 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);
- 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++);
-
- check_for_more:
- /*
- * 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 < last_block))
- continue;
-
- flush_bufs();
- save_currently_testing = currently_testing;
-
- /*
- * 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);
- try = 0;
-
- while (1) {
- if (try == 0) {
- if (used2 >= num_saved)
- break;
- currently_testing = test_record[used2].block;
- try = test_record[used2].num;
- used2++;
- }
-
- 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;
+ buf_used += got;
save_ptr += got * block_size;
test_ptr += got * block_size;
- read_ptr += got * block_size;
- try -= got;
+ currently_testing += got;
+ if (got != try)
+ bb_count += bb_output(currently_testing++);
+
+ check_for_more:
+ /*
+ * 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 < last_block))
+ continue;
+
+ flush_bufs();
+ save_currently_testing = currently_testing;
+
+ /*
+ * 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);
+ try = 0;
+
+ while (1) {
+ if (try == 0) {
+ if (used2 >= num_saved)
+ break;
+ currently_testing = test_record[used2].block;
+ try = test_record[used2].num;
+ used2++;
+ }
+
+ 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;
+ }
+
+ /* empty the buffer so it can be reused */
+ num_saved = 0;
+ buf_used = 0;
+ save_ptr = blkbuf;
+ test_ptr = blkbuf + (blocks_at_once * block_size);
+ currently_testing = save_currently_testing;
}
+ num_blocks = 0;
+ alarm(0);
+ if (s_flag || v_flag > 1)
+ fprintf(stderr, _(done_string));
- /* empty the buffer so it can be reused */
- num_saved = 0;
- buf_used = 0;
- save_ptr = blkbuf;
- test_ptr = blkbuf + (blocks_at_once * block_size);
- currently_testing = save_currently_testing;
+ flush_bufs();
}
- num_blocks = 0;
- alarm(0);
uncapture_terminate();
- if (s_flag || v_flag > 1)
- fprintf(stderr, _(done_string));
-
fflush(stderr);
free(blkbuf);
free(test_record);
@@ -646,6 +709,7 @@
int passes_clean = 0;
int dev;
errcode_t errcode;
+ long pattern;
unsigned int (*test_func)(int, unsigned long,
int, unsigned long,
unsigned long);
@@ -663,7 +727,7 @@
if (argc && *argv)
program_name = *argv;
- while ((c = getopt (argc, argv, "b:fi:o:svwnc:p:h:")) != EOF) {
+ while ((c = getopt (argc, argv, "b:fi:o:svwnc:p:h:t:")) != EOF) {
switch (c) {
case 'b':
block_size = strtoul (optarg, &tmp, 0);
@@ -719,10 +783,48 @@
case 'h':
host_device_name = optarg;
break;
+ case 't':
+ if (t_flag + 1 > t_max) {
+ long *t_patts_new;
+
+ t_patts_new = realloc(t_patts, t_max + T_INC);
+ if (!t_patts_new) {
+ com_err(program_name, ENOMEM,
+ _("can't allocate memory for "
+ "test_pattern - %s"),
+ optarg);
+ exit(1);
+ }
+ t_patts = t_patts_new;
+ t_max += T_INC;
+ }
+ pattern = strtol(optarg, &tmp, 0);
+ if (*tmp) {
+ com_err(program_name, 0,
+ _("invalid test_pattern: %s\n"),
+ optarg);
+ exit(1);
+ }
+ t_patts[t_flag++] = pattern;
+ break;
default:
usage();
}
}
+ if (!w_flag) {
+ if (t_flag > 1) {
+ com_err(program_name, 0,
+ _("Maximum of one test_pattern may be specified "
+ "in read-only mode"));
+ exit(1);
+ }
+ if (t_patts && (t_patts[0] == -1)) {
+ com_err(program_name, 0,
+ _("Random test_pattern is not allowed "
+ "in read-only mode"));
+ exit(1);
+ }
+ }
if (optind > argc - 1)
usage();
device_name = argv[optind++];
@@ -867,6 +969,8 @@
close (dev);
if (out != stdout)
fclose (out);
+ if (t_patts)
+ free(t_patts);
return 0;
}