195 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			195 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
/* readall - read a whole device fast		Author: Andy Tanenbaum */
 | 
						|
 | 
						|
/* Readall reads all the blocks on a device as fast as it can.  If it hits
 | 
						|
 * an error, it stops reading in large units and reads one block at a time.
 | 
						|
 * It reports on all errors it finds.
 | 
						|
 *
 | 
						|
 * If the -b flag is given, the output is a shell script that can be run
 | 
						|
 * to mark all the bad blocks.
 | 
						|
 *
 | 
						|
 * If the -t flag is given, only the total numbers of blocks is reported.
 | 
						|
 *
 | 
						|
 * Examples of usage:
 | 
						|
 *	readall /dev/hd1		# read /dev/hd1
 | 
						|
 *	readall -b /dev/hd2		# prepare bad block list on stdout
 | 
						|
 *	readall -t /dev/ram		# report size of ram disk
 | 
						|
 */
 | 
						|
 | 
						|
#include <sys/types.h>
 | 
						|
#include <sys/ioc_disk.h>
 | 
						|
#include <minix/partition.h>
 | 
						|
#include <minix/u64.h>
 | 
						|
#include <time.h>
 | 
						|
#include <fcntl.h>
 | 
						|
#include <unistd.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <stdio.h>
 | 
						|
 | 
						|
#define CHUNK    25		/* max number of blocks read at once */
 | 
						|
#define BLOCK_SIZE	1024	/* size of a block */
 | 
						|
#define RESUME  200		/* # good reads before going back to CHUNK */
 | 
						|
#define DIVISOR	 1000		/* how often to print statistics */
 | 
						|
#define STORE  4096		/* save this many bad blocks for summary */
 | 
						|
 | 
						|
int chunk = CHUNK;		/* current number of blocks being read */
 | 
						|
long goodies;			/* incremented on good reads */
 | 
						|
long errors;			/* number of errors so far */
 | 
						|
int normal = 1;			/* set unless -b flag is given */
 | 
						|
int total = 0;			/* unset unless -t flag is given */
 | 
						|
char *name;			/* name of special file being read */
 | 
						|
 | 
						|
char a[CHUNK * BLOCK_SIZE];	/* read buffer */
 | 
						|
long rotten[STORE];		/* list of bad blocks */
 | 
						|
 | 
						|
_PROTOTYPE(int main, (int argc, char **argv));
 | 
						|
static _PROTOTYPE(void output, (long blocks_read));
 | 
						|
 | 
						|
/* print pretty progress meter with remaining no. of blocks and ETA on
 | 
						|
 * stderr
 | 
						|
 */
 | 
						|
void
 | 
						|
prettyprogress(long b, long nblocks, time_t starttime)
 | 
						|
{
 | 
						|
  /* print progress indication */
 | 
						|
  time_t spent, now;
 | 
						|
  long bpsec;
 | 
						|
  time(&now);
 | 
						|
  spent = now - starttime;
 | 
						|
  if(spent > 0 && (bpsec = b / spent) > 0) {
 | 
						|
  	int len, i;
 | 
						|
  	long secremain, minremain, hremain;
 | 
						|
	  secremain = (nblocks - b) / bpsec;
 | 
						|
	  minremain = (secremain / 60) % 60;
 | 
						|
	  hremain = secremain / 3600;
 | 
						|
  	len = fprintf(stderr, "Remain %ld blocks. ETA: %d:%02d:%02d  [",
 | 
						|
  		nblocks - b,
 | 
						|
  		hremain, minremain, secremain % 60);
 | 
						|
#define WIDTH 77
 | 
						|
  	len = WIDTH - len;
 | 
						|
  	for(i = 0; i < (b * (len-1) / nblocks); i++) 
 | 
						|
  		fprintf(stderr, "=");
 | 
						|
 	fprintf(stderr, "|");
 | 
						|
  	for(; i < len-2; i++) 
 | 
						|
  		fprintf(stderr, "-");
 | 
						|
  	fprintf(stderr, "]\r");
 | 
						|
  	fflush(stderr);
 | 
						|
  }
 | 
						|
 | 
						|
  return;
 | 
						|
}
 | 
						|
 | 
						|
int main(argc, argv)
 | 
						|
int argc;
 | 
						|
char *argv[];
 | 
						|
{
 | 
						|
  struct partition entry;
 | 
						|
  int fd, s, i, badprinted;
 | 
						|
  long b = 0, nblocks;
 | 
						|
  char *p;
 | 
						|
  time_t starttime;
 | 
						|
 | 
						|
  if (argc != 2 && argc != 3) {
 | 
						|
	fprintf(stderr, "Usage: readall [-b | -t] file\n");
 | 
						|
	exit(1);
 | 
						|
  }
 | 
						|
  i = 1;
 | 
						|
 | 
						|
  p = argv[1];
 | 
						|
  if (*p == '-' && *(p + 1) == 'b' && *(p + 2) == '\0') {
 | 
						|
	normal = 0;
 | 
						|
	i++;
 | 
						|
	name = argv[i];
 | 
						|
  }
 | 
						|
  if (*p == '-' && *(p + 1) == 't' && *(p + 2) == '\0') {
 | 
						|
	normal = 0;
 | 
						|
	total = 1;
 | 
						|
	i++;
 | 
						|
	name = argv[i];
 | 
						|
  }
 | 
						|
  fd = open(argv[i], O_RDONLY);
 | 
						|
  if (fd < 0) {
 | 
						|
  	perror(argv[i]);
 | 
						|
	fprintf(stderr, "%s is not readable\n", argv[i]);
 | 
						|
	exit(1);
 | 
						|
  }
 | 
						|
 | 
						|
  /* Get size of file */
 | 
						|
  if(ioctl(fd, DIOCGETP, &entry) < 0) {
 | 
						|
  	perror("ioctl DIOCGETP");
 | 
						|
  	return 1;
 | 
						|
  }
 | 
						|
  nblocks = div64u(entry.size, BLOCK_SIZE);
 | 
						|
 | 
						|
  time(&starttime);
 | 
						|
  /* Read the entire file. Try it in large chunks, but if an error
 | 
						|
   * occurs, go to single reads for a while. */
 | 
						|
  while (1) {
 | 
						|
	if(lseek(fd, BLOCK_SIZE * b, SEEK_SET) < 0) {
 | 
						|
		perror("lseek");
 | 
						|
		return 1;
 | 
						|
	}
 | 
						|
	s = read(fd, a, BLOCK_SIZE * chunk);
 | 
						|
	if (s == BLOCK_SIZE * chunk) {
 | 
						|
		/* Normal read, no errors. */
 | 
						|
		b += chunk;
 | 
						|
		goodies++;
 | 
						|
		if (chunk == 1) {
 | 
						|
			if (goodies >= RESUME && b % DIVISOR == 0)
 | 
						|
				chunk = CHUNK;
 | 
						|
		}
 | 
						|
  		if(b % DIVISOR == 0 && !normal) {
 | 
						|
  			prettyprogress(b, nblocks, starttime);
 | 
						|
  		}
 | 
						|
	} else if (s < 0) {
 | 
						|
		/* I/O error. */
 | 
						|
		if (chunk != 1) {
 | 
						|
			chunk = 1;	/* regress to single block mode */
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
		if (errors == STORE) {
 | 
						|
			fprintf(stderr,
 | 
						|
			 "\n%ld Bad blocks is too many.  Exiting\n",
 | 
						|
				errors);
 | 
						|
			exit(1);
 | 
						|
		}
 | 
						|
		rotten[(int) errors] = b;	/* log the error */
 | 
						|
		b += chunk;
 | 
						|
		errors++;
 | 
						|
	} else {
 | 
						|
		/* End of file. */
 | 
						|
		b += s / BLOCK_SIZE;
 | 
						|
		if (normal) {
 | 
						|
			output(b);
 | 
						|
			fprintf(stderr, "\n");
 | 
						|
		} else fprintf(stderr, "\r%*s\n", -WIDTH, "Done scanning.");
 | 
						|
		if (total) printf("%8ld\n", b);
 | 
						|
		if ((errors == 0) || total) exit(0);
 | 
						|
		badprinted = 0;
 | 
						|
		if (normal) printf("Summary of bad blocks\n");
 | 
						|
 | 
						|
		/* Print summary of bad blocks, possibly as shell script. */
 | 
						|
		for (i = 0; i < errors; i++) {
 | 
						|
			if (normal == 0 && badprinted == 0) {
 | 
						|
				printf("badblocks %s ", name);
 | 
						|
				badprinted = 1;
 | 
						|
			}
 | 
						|
			printf("%6ld ", rotten[i]);
 | 
						|
			if ((i + 1) % 7 == 0) {
 | 
						|
				printf("\n");
 | 
						|
				badprinted = 0;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		printf("\n");
 | 
						|
		exit(0);
 | 
						|
	}
 | 
						|
	if (normal && b % DIVISOR == 0) output(b);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
static void output(blocks_read)
 | 
						|
long blocks_read;
 | 
						|
{
 | 
						|
  fprintf(stderr, "%8ld blocks read, %5ld errors\r", blocks_read, errors);
 | 
						|
  fflush(stderr);
 | 
						|
}
 |