ast@cs.vu.nl (Andy Tanenbaum) (08/16/87)
Here is a little program to check (hard) disks for bad sectors. It is not very
fancy, but will do as a first approximation. At some point we probably need
a more sophisticated program that checks a disk for bad sectors, creates a
file containing them, so the blocks containing the bad sectors will be removed
from the bit map. Heaven forbid that a bad sector occur in the bit map itself.
Fsck won't like this much either.
Andy Tanenbaum (ast@cs.vu.nl)
-------------------------- diskcheck.c -------------------------------
/* diskcheck - test a disk for bad blocks Author: Andy Tanenbaum */
/* Usage: diskcheck device starting-block block-count
* Example: diskcheck /dev/hd1 0 10000 # check blocks 0 to 9999 on hd1
*/
#include "signal.h"
#include "../fs/const.h"
#include "../h/const.h"
#include <errno.h>
#undef printf
#define PRINTFREQ 100
#define N 30
char purgebuf[BLOCK_SIZE * N];
char buf[BLOCK_SIZE], zero[BLOCK_SIZE];
int pat1[BLOCK_SIZE/2], pat2[BLOCK_SIZE/2];
int blk = -1; /* number of the block in buf, or -1 */
int pfd; /* file descriptor for purging */
int fd; /* file descriptor for data I/O */
unsigned initblock; /* first block to test */
unsigned limit; /* first block beyond test zone */
unsigned errors; /* # errors so far */
unsigned ct; /* # blocks read so far */
int intflag; /* set when signal seen */
extern errno;
long lseek(), pos;
char *purgefile = "/dev/ram";
main(argc, argv)
int argc;
char *argv[];
{
unsigned b;
int i;
int catch();
signal(SIGINT, catch);
signal(SIGQUIT, catch);
if (argc != 4) usage();
if ( (fd = open(argv[1], 2)) < 0) {
printf("Cannot open %s\n",argv[1]);
exit(1);
}
if ( (pfd = open(purgefile, 2)) < 0) {
printf("Cannot open %s\n",purgefile);
exit(1);
}
initblock = atoi(argv[2]);
limit = initblock + atoi(argv[3]);
if (limit <= initblock) usage();
for (i = 0; i < BLOCK_SIZE/2; i++) {
pat1[i] = i;
pat2[i] = 1000 - i;
}
for (b = initblock; b < limit; b++) {
if (intflag) break;
if (testblock(b) == ERROR) {
errors++;
if (blk == b) {
/* Read ok, write failed; try to restore block. */
lseek(fd, pos, 0);
write(fd, buf, BLOCK_SIZE);
}
}
ct++;
if (ct % PRINTFREQ == 0) status();
}
status();
exit(0);
}
int testblock(b)
unsigned b;
{
/* Read block b in, save it in buf. Then overwrite that block with a
* known test pattern and read it back. Finally, replace the block.
* Return OK or ERROR.
*/
int s;
blk = -1;
pos = (long) BLOCK_SIZE * (long) b;
purge_cache();
if (lseek(fd, pos, 0) != pos) fatal("Cannot seek to block ",b);
/* Read block b into 'buf'. */
s = read(fd, buf, BLOCK_SIZE);
/* Test for various outcomes of the read. */
if (s == BLOCK_SIZE) {
blk = b;
if (wtest(pos, pat1) == ERROR) return(ERROR);
if (wtest(pos, pat2) == ERROR) return(ERROR);
lseek(fd, pos, 0);
if (write(fd, buf, BLOCK_SIZE) != BLOCK_SIZE) {
nonfatal("Cannot rewrite block ", b);
return(ERROR);
} else {
return(OK);
}
}
if (s < 0) {
if (errno == EIO)
nonfatal("Read error on block ", b);
else {
printf("Error. Read returned %d. errno=%d. ", s,errno);
fatal("Block ", b);
}
return(ERROR);
}
if (s == 0) fatal("End of file reached trying to read block ", b);
nonfatal("Read size error on block ", b);
}
status()
{
printf("%5u blocks tested, %u errors detected\n",ct,errors);
}
nonfatal(s, b)
char *s;
unsigned b;
{
printf("%s%u\n",s,b);
}
fatal(s, b)
char *s;
unsigned b;
{
printf("%s%u\n",s,b);
status();
exit(1);
}
catch()
{
signal(SIGINT, catch);
signal(SIGQUIT, catch);
intflag = 1;
}
usage()
{
printf("Usage: diskcheck device start-block block-count\n");
exit(1);
}
wtest(pos, pat)
long pos;
int pat[];
{
int testb[BLOCK_SIZE/2];
int i;
lseek(fd, pos, 0);
if (write(fd, pat, BLOCK_SIZE) != BLOCK_SIZE) return(ERROR);
sync(); /* force the write to the disk */
purge_cache();
lseek(fd, pos, 0);
if (read(fd, testb, BLOCK_SIZE) != BLOCK_SIZE) return(ERROR);
for (i = 0; i < BLOCK_SIZE/2; i++)
if (testb[i] != pat[i]) {printf("%d %d\n",testb[i],pat[i]);return(ERROR);}
return(OK);
}
purge_cache()
{
/* Do enough reads that the cache is purged. */
int left, count,r;
pfd = open(purgefile, 0);
left = NR_BUFS;
while (left > 0) {
count = (left < N ? left : N);
if ((r=read(pfd, purgebuf, count* BLOCK_SIZE)) != count * BLOCK_SIZE) {
printf("ERROR: count=%d left=%d r=%d. ",count, left,r);
fatal("Cannot purge cache. errno= ", errno);
}
left -= count;
}
close(pfd);
}