[9] | 1 | /* readall - read a whole device fast Author: Andy Tanenbaum */
|
---|
| 2 |
|
---|
| 3 | /* Readall reads all the blocks on a device as fast as it can. If it hits
|
---|
| 4 | * an error, it stops reading in large units and reads one block at a time.
|
---|
| 5 | * It reports on all errors it finds.
|
---|
| 6 | *
|
---|
| 7 | * If the -b flag is given, the output is a shell script that can be run
|
---|
| 8 | * to mark all the bad blocks.
|
---|
| 9 | *
|
---|
| 10 | * If the -t flag is given, only the total numbers of blocks is reported.
|
---|
| 11 | *
|
---|
| 12 | * Examples of usage:
|
---|
| 13 | * readall /dev/hd1 # read /dev/hd1
|
---|
| 14 | * readall -b /dev/hd2 # prepare bad block list on stdout
|
---|
| 15 | * readall -t /dev/ram # report size of ram disk
|
---|
| 16 | */
|
---|
| 17 |
|
---|
| 18 | #include <sys/types.h>
|
---|
| 19 | #include <sys/ioc_disk.h>
|
---|
| 20 | #include <minix/partition.h>
|
---|
| 21 | #include <minix/u64.h>
|
---|
| 22 | #include <time.h>
|
---|
| 23 | #include <fcntl.h>
|
---|
| 24 | #include <unistd.h>
|
---|
| 25 | #include <stdlib.h>
|
---|
| 26 | #include <stdio.h>
|
---|
| 27 |
|
---|
| 28 | #define CHUNK 25 /* max number of blocks read at once */
|
---|
| 29 | #define BLOCK_SIZE 1024 /* size of a block */
|
---|
| 30 | #define RESUME 200 /* # good reads before going back to CHUNK */
|
---|
| 31 | #define DIVISOR 1000 /* how often to print statistics */
|
---|
| 32 | #define STORE 4096 /* save this many bad blocks for summary */
|
---|
| 33 |
|
---|
| 34 | int chunk = CHUNK; /* current number of blocks being read */
|
---|
| 35 | long goodies; /* incremented on good reads */
|
---|
| 36 | long errors; /* number of errors so far */
|
---|
| 37 | int normal = 1; /* set unless -b flag is given */
|
---|
| 38 | int total = 0; /* unset unless -t flag is given */
|
---|
| 39 | char *name; /* name of special file being read */
|
---|
| 40 |
|
---|
| 41 | char a[CHUNK * BLOCK_SIZE]; /* read buffer */
|
---|
| 42 | long rotten[STORE]; /* list of bad blocks */
|
---|
| 43 |
|
---|
| 44 | _PROTOTYPE(int main, (int argc, char **argv));
|
---|
| 45 | static _PROTOTYPE(void output, (long blocks_read));
|
---|
| 46 |
|
---|
| 47 | /* print pretty progress meter with remaining no. of blocks and ETA on
|
---|
| 48 | * stderr
|
---|
| 49 | */
|
---|
| 50 | void
|
---|
| 51 | prettyprogress(long b, long nblocks, time_t starttime)
|
---|
| 52 | {
|
---|
| 53 | /* print progress indication */
|
---|
| 54 | time_t spent, now;
|
---|
| 55 | long bpsec;
|
---|
| 56 | time(&now);
|
---|
| 57 | spent = now - starttime;
|
---|
| 58 | if(spent > 0 && (bpsec = b / spent) > 0) {
|
---|
| 59 | int len, i;
|
---|
| 60 | long secremain, minremain, hremain;
|
---|
| 61 | secremain = (nblocks - b) / bpsec;
|
---|
| 62 | minremain = (secremain / 60) % 60;
|
---|
| 63 | hremain = secremain / 3600;
|
---|
| 64 | len = fprintf(stderr, "Remain %ld blocks. ETA: %d:%02d:%02d [",
|
---|
| 65 | nblocks - b,
|
---|
| 66 | hremain, minremain, secremain % 60);
|
---|
| 67 | #define WIDTH 77
|
---|
| 68 | len = WIDTH - len;
|
---|
| 69 | for(i = 0; i < (b * (len-1) / nblocks); i++)
|
---|
| 70 | fprintf(stderr, "=");
|
---|
| 71 | fprintf(stderr, "|");
|
---|
| 72 | for(; i < len-2; i++)
|
---|
| 73 | fprintf(stderr, "-");
|
---|
| 74 | fprintf(stderr, "]\r");
|
---|
| 75 | fflush(stderr);
|
---|
| 76 | }
|
---|
| 77 |
|
---|
| 78 | return;
|
---|
| 79 | }
|
---|
| 80 |
|
---|
| 81 | int main(argc, argv)
|
---|
| 82 | int argc;
|
---|
| 83 | char *argv[];
|
---|
| 84 | {
|
---|
| 85 | struct partition entry;
|
---|
| 86 | int fd, s, i, badprinted;
|
---|
| 87 | long b = 0, nblocks;
|
---|
| 88 | char *p;
|
---|
| 89 | time_t starttime;
|
---|
| 90 |
|
---|
| 91 | if (argc != 2 && argc != 3) {
|
---|
| 92 | fprintf(stderr, "Usage: readall [-b | -t] file\n");
|
---|
| 93 | exit(1);
|
---|
| 94 | }
|
---|
| 95 | i = 1;
|
---|
| 96 |
|
---|
| 97 | p = argv[1];
|
---|
| 98 | if (*p == '-' && *(p + 1) == 'b' && *(p + 2) == '\0') {
|
---|
| 99 | normal = 0;
|
---|
| 100 | i++;
|
---|
| 101 | name = argv[i];
|
---|
| 102 | }
|
---|
| 103 | if (*p == '-' && *(p + 1) == 't' && *(p + 2) == '\0') {
|
---|
| 104 | normal = 0;
|
---|
| 105 | total = 1;
|
---|
| 106 | i++;
|
---|
| 107 | name = argv[i];
|
---|
| 108 | }
|
---|
| 109 | fd = open(argv[i], O_RDONLY);
|
---|
| 110 | if (fd < 0) {
|
---|
| 111 | perror(argv[i]);
|
---|
| 112 | fprintf(stderr, "%s is not readable\n", argv[i]);
|
---|
| 113 | exit(1);
|
---|
| 114 | }
|
---|
| 115 |
|
---|
| 116 | /* Get size of file */
|
---|
| 117 | if(ioctl(fd, DIOCGETP, &entry) < 0) {
|
---|
| 118 | perror("ioctl DIOCGETP");
|
---|
| 119 | return 1;
|
---|
| 120 | }
|
---|
| 121 | nblocks = div64u(entry.size, BLOCK_SIZE);
|
---|
| 122 |
|
---|
| 123 | time(&starttime);
|
---|
| 124 | /* Read the entire file. Try it in large chunks, but if an error
|
---|
| 125 | * occurs, go to single reads for a while. */
|
---|
| 126 | while (1) {
|
---|
| 127 | if(lseek(fd, BLOCK_SIZE * b, SEEK_SET) < 0) {
|
---|
| 128 | perror("lseek");
|
---|
| 129 | return 1;
|
---|
| 130 | }
|
---|
| 131 | s = read(fd, a, BLOCK_SIZE * chunk);
|
---|
| 132 | if (s == BLOCK_SIZE * chunk) {
|
---|
| 133 | /* Normal read, no errors. */
|
---|
| 134 | b += chunk;
|
---|
| 135 | goodies++;
|
---|
| 136 | if (chunk == 1) {
|
---|
| 137 | if (goodies >= RESUME && b % DIVISOR == 0)
|
---|
| 138 | chunk = CHUNK;
|
---|
| 139 | }
|
---|
| 140 | if(b % DIVISOR == 0 && !normal) {
|
---|
| 141 | prettyprogress(b, nblocks, starttime);
|
---|
| 142 | }
|
---|
| 143 | } else if (s < 0) {
|
---|
| 144 | /* I/O error. */
|
---|
| 145 | if (chunk != 1) {
|
---|
| 146 | chunk = 1; /* regress to single block mode */
|
---|
| 147 | continue;
|
---|
| 148 | }
|
---|
| 149 | if (errors == STORE) {
|
---|
| 150 | fprintf(stderr,
|
---|
| 151 | "\n%ld Bad blocks is too many. Exiting\n",
|
---|
| 152 | errors);
|
---|
| 153 | exit(1);
|
---|
| 154 | }
|
---|
| 155 | rotten[(int) errors] = b; /* log the error */
|
---|
| 156 | b += chunk;
|
---|
| 157 | errors++;
|
---|
| 158 | } else {
|
---|
| 159 | /* End of file. */
|
---|
| 160 | b += s / BLOCK_SIZE;
|
---|
| 161 | if (normal) {
|
---|
| 162 | output(b);
|
---|
| 163 | fprintf(stderr, "\n");
|
---|
| 164 | } else fprintf(stderr, "\r%*s\n", -WIDTH, "Done scanning.");
|
---|
| 165 | if (total) printf("%8ld\n", b);
|
---|
| 166 | if ((errors == 0) || total) exit(0);
|
---|
| 167 | badprinted = 0;
|
---|
| 168 | if (normal) printf("Summary of bad blocks\n");
|
---|
| 169 |
|
---|
| 170 | /* Print summary of bad blocks, possibly as shell script. */
|
---|
| 171 | for (i = 0; i < errors; i++) {
|
---|
| 172 | if (normal == 0 && badprinted == 0) {
|
---|
| 173 | printf("badblocks %s ", name);
|
---|
| 174 | badprinted = 1;
|
---|
| 175 | }
|
---|
| 176 | printf("%6ld ", rotten[i]);
|
---|
| 177 | if ((i + 1) % 7 == 0) {
|
---|
| 178 | printf("\n");
|
---|
| 179 | badprinted = 0;
|
---|
| 180 | }
|
---|
| 181 | }
|
---|
| 182 | printf("\n");
|
---|
| 183 | exit(0);
|
---|
| 184 | }
|
---|
| 185 | if (normal && b % DIVISOR == 0) output(b);
|
---|
| 186 | }
|
---|
| 187 | }
|
---|
| 188 |
|
---|
| 189 | static void output(blocks_read)
|
---|
| 190 | long blocks_read;
|
---|
| 191 | {
|
---|
| 192 | fprintf(stderr, "%8ld blocks read, %5ld errors\r", blocks_read, errors);
|
---|
| 193 | fflush(stderr);
|
---|
| 194 | }
|
---|