1 /*- 2 * ---------------------------------------------------------------------------- 3 * "THE BEER-WARE LICENSE" (Revision 42): 4 * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you 5 * can do whatever you want with this stuff. If we meet some day, and you think 6 * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 7 * ---------------------------------------------------------------------------- 8 * 9 * $FreeBSD$ 10 */ 11 #include <sys/param.h> 12 #include <sys/queue.h> 13 #include <sys/disk.h> 14 #include <sys/stat.h> 15 16 #include <err.h> 17 #include <errno.h> 18 #include <fcntl.h> 19 #include <signal.h> 20 #include <stdint.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #include <time.h> 25 #include <unistd.h> 26 27 static volatile sig_atomic_t aborting = 0; 28 static size_t bigsize = 1024 * 1024; 29 static size_t medsize; 30 static size_t minsize = 512; 31 32 struct lump { 33 off_t start; 34 off_t len; 35 int state; 36 TAILQ_ENTRY(lump) list; 37 }; 38 39 static TAILQ_HEAD(, lump) lumps = TAILQ_HEAD_INITIALIZER(lumps); 40 41 static void 42 new_lump(off_t start, off_t len, int state) 43 { 44 struct lump *lp; 45 46 lp = malloc(sizeof *lp); 47 if (lp == NULL) 48 err(1, "Malloc failed"); 49 lp->start = start; 50 lp->len = len; 51 lp->state = state; 52 TAILQ_INSERT_TAIL(&lumps, lp, list); 53 } 54 55 static struct lump *lp; 56 static char *wworklist = NULL; 57 static char *rworklist = NULL; 58 59 60 #define PRINT_HEADER \ 61 printf("%13s %7s %13s %5s %13s %13s %9s\n", \ 62 "start", "size", "block-len", "state", "done", "remaining", "% done") 63 64 #define PRINT_STATUS(start, i, len, state, d, t) \ 65 printf("\r%13jd %7zu %13jd %5d %13jd %13jd %9.5f", \ 66 (intmax_t)start, \ 67 i, \ 68 (intmax_t)len, \ 69 state, \ 70 (intmax_t)d, \ 71 (intmax_t)(t - d), \ 72 100*(double)d/(double)t) 73 74 /* Save the worklist if -w was given */ 75 static void 76 save_worklist(void) 77 { 78 FILE *file; 79 struct lump *llp; 80 81 if (wworklist != NULL) { 82 (void)fprintf(stderr, "\nSaving worklist ..."); 83 fflush(stderr); 84 85 file = fopen(wworklist, "w"); 86 if (file == NULL) 87 err(1, "Error opening file %s", wworklist); 88 89 TAILQ_FOREACH(llp, &lumps, list) 90 fprintf(file, "%jd %jd %d\n", 91 (intmax_t)llp->start, (intmax_t)llp->len, 92 llp->state); 93 fclose(file); 94 (void)fprintf(stderr, " done.\n"); 95 } 96 } 97 98 /* Read the worklist if -r was given */ 99 static off_t 100 read_worklist(off_t t) 101 { 102 off_t s, l, d; 103 int state, lines; 104 FILE *file; 105 106 (void)fprintf(stderr, "Reading worklist ..."); 107 fflush(stderr); 108 file = fopen(rworklist, "r"); 109 if (file == NULL) 110 err(1, "Error opening file %s", rworklist); 111 112 lines = 0; 113 d = t; 114 for (;;) { 115 ++lines; 116 if (3 != fscanf(file, "%jd %jd %d\n", &s, &l, &state)) { 117 if (!feof(file)) 118 err(1, "Error parsing file %s at line %d", 119 rworklist, lines); 120 else 121 break; 122 } 123 new_lump(s, l, state); 124 d -= l; 125 } 126 (void)fprintf(stderr, " done.\n"); 127 /* 128 * Return the number of bytes already read 129 * (at least not in worklist). 130 */ 131 return (d); 132 } 133 134 static void 135 usage(void) 136 { 137 (void)fprintf(stderr, "usage: recoverdisk [-b bigsize] [-r readlist] " 138 "[-s interval] [-w writelist] source [destination]\n"); 139 exit(1); 140 } 141 142 static void 143 sighandler(__unused int sig) 144 { 145 146 aborting = 1; 147 } 148 149 int 150 main(int argc, char * const argv[]) 151 { 152 int ch; 153 int fdr, fdw; 154 off_t t, d, start, len; 155 size_t i, j; 156 int error, state; 157 u_char *buf; 158 u_int sectorsize; 159 off_t stripesize; 160 time_t t1, t2; 161 struct stat sb; 162 u_int n, snapshot = 60; 163 164 while ((ch = getopt(argc, argv, "b:r:w:s:")) != -1) { 165 switch (ch) { 166 case 'b': 167 bigsize = strtoul(optarg, NULL, 0); 168 break; 169 case 'r': 170 rworklist = strdup(optarg); 171 if (rworklist == NULL) 172 err(1, "Cannot allocate enough memory"); 173 break; 174 case 's': 175 snapshot = strtoul(optarg, NULL, 0); 176 break; 177 case 'w': 178 wworklist = strdup(optarg); 179 if (wworklist == NULL) 180 err(1, "Cannot allocate enough memory"); 181 break; 182 default: 183 usage(); 184 /* NOTREACHED */ 185 } 186 } 187 argc -= optind; 188 argv += optind; 189 190 if (argc < 1 || argc > 2) 191 usage(); 192 193 fdr = open(argv[0], O_RDONLY); 194 if (fdr < 0) 195 err(1, "Cannot open read descriptor %s", argv[0]); 196 197 error = fstat(fdr, &sb); 198 if (error < 0) 199 err(1, "fstat failed"); 200 if (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode)) { 201 error = ioctl(fdr, DIOCGSECTORSIZE, §orsize); 202 if (error < 0) 203 err(1, "DIOCGSECTORSIZE failed"); 204 205 error = ioctl(fdr, DIOCGSTRIPESIZE, &stripesize); 206 if (error == 0 && stripesize > sectorsize) 207 sectorsize = stripesize; 208 209 minsize = sectorsize; 210 bigsize = rounddown(bigsize, sectorsize); 211 212 error = ioctl(fdr, DIOCGMEDIASIZE, &t); 213 if (error < 0) 214 err(1, "DIOCGMEDIASIZE failed"); 215 } else { 216 t = sb.st_size; 217 } 218 219 if (bigsize < minsize) 220 bigsize = minsize; 221 222 for (ch = 0; (bigsize >> ch) > minsize; ch++) 223 continue; 224 medsize = bigsize >> (ch / 2); 225 medsize = rounddown(medsize, minsize); 226 227 fprintf(stderr, "Bigsize = %zu, medsize = %zu, minsize = %zu\n", 228 bigsize, medsize, minsize); 229 230 buf = malloc(bigsize); 231 if (buf == NULL) 232 err(1, "Cannot allocate %zu bytes buffer", bigsize); 233 234 if (argc > 1) { 235 fdw = open(argv[1], O_WRONLY | O_CREAT, DEFFILEMODE); 236 if (fdw < 0) 237 err(1, "Cannot open write descriptor %s", argv[1]); 238 if (ftruncate(fdw, t) < 0) 239 err(1, "Cannot truncate output %s to %jd bytes", 240 argv[1], (intmax_t)t); 241 } else 242 fdw = -1; 243 244 if (rworklist != NULL) { 245 d = read_worklist(t); 246 } else { 247 new_lump(0, t, 0); 248 d = 0; 249 } 250 if (wworklist != NULL) 251 signal(SIGINT, sighandler); 252 253 t1 = 0; 254 start = len = i = state = 0; 255 PRINT_HEADER; 256 n = 0; 257 for (;;) { 258 lp = TAILQ_FIRST(&lumps); 259 if (lp == NULL) 260 break; 261 while (lp->len > 0 && !aborting) { 262 /* These are only copied for printing stats */ 263 start = lp->start; 264 len = lp->len; 265 state = lp->state; 266 267 i = MIN(lp->len, (off_t)bigsize); 268 if (lp->state == 1) 269 i = MIN(lp->len, (off_t)medsize); 270 if (lp->state > 1) 271 i = MIN(lp->len, (off_t)minsize); 272 time(&t2); 273 if (t1 != t2 || lp->len < (off_t)bigsize) { 274 PRINT_STATUS(start, i, len, state, d, t); 275 t1 = t2; 276 if (++n == snapshot) { 277 save_worklist(); 278 n = 0; 279 } 280 } 281 if (i == 0) { 282 errx(1, "BOGUS i %10jd", (intmax_t)i); 283 } 284 fflush(stdout); 285 j = pread(fdr, buf, i, lp->start); 286 if (j == i) { 287 d += i; 288 if (fdw >= 0) 289 j = pwrite(fdw, buf, i, lp->start); 290 else 291 j = i; 292 if (j != i) 293 printf("\nWrite error at %jd/%zu\n", 294 lp->start, i); 295 lp->start += i; 296 lp->len -= i; 297 continue; 298 } 299 printf("\n%jd %zu failed (%s)\n", 300 lp->start, i, strerror(errno)); 301 if (errno == EINVAL) { 302 printf("read() size too big? Try with -b 131072"); 303 aborting = 1; 304 } 305 if (errno == ENXIO) 306 aborting = 1; 307 new_lump(lp->start, i, lp->state + 1); 308 lp->start += i; 309 lp->len -= i; 310 } 311 if (aborting) { 312 save_worklist(); 313 return (0); 314 } 315 TAILQ_REMOVE(&lumps, lp, list); 316 free(lp); 317 } 318 PRINT_STATUS(start, i, len, state, d, t); 319 save_worklist(); 320 printf("\nCompleted\n"); 321 return (0); 322 } 323