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 volatile sig_atomic_t aborting = 0; 28 static size_t bigsize = 1024 * 1024; 29 static size_t medsize = 64 * 1024; 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 /* Save the worklist if -w was given */ 60 static void 61 save_worklist(void) 62 { 63 FILE *file; 64 65 if (wworklist != NULL) { 66 (void)fprintf(stderr, "\nSaving worklist ..."); 67 fflush(stderr); 68 69 file = fopen(wworklist, "w"); 70 if (file == NULL) 71 err(1, "Error opening file %s", wworklist); 72 73 for (;;) { 74 lp = TAILQ_FIRST(&lumps); 75 if (lp == NULL) 76 break; 77 fprintf(file, "%jd %jd %d\n", 78 (intmax_t)lp->start, (intmax_t)lp->len, lp->state); 79 TAILQ_REMOVE(&lumps, lp, list); 80 } 81 (void)fprintf(stderr, " done.\n"); 82 } 83 } 84 85 /* Read the worklist if -r was given */ 86 static off_t 87 read_worklist(off_t t) 88 { 89 off_t s, l, d; 90 int state, lines; 91 FILE *file; 92 93 (void)fprintf(stderr, "Reading worklist ..."); 94 fflush(stderr); 95 file = fopen(rworklist, "r"); 96 if (file == NULL) 97 err(1, "Error opening file %s", rworklist); 98 99 lines = 0; 100 d = t; 101 for (;;) { 102 ++lines; 103 if (3 != fscanf(file, "%jd %jd %d\n", &s, &l, &state)) { 104 if (!feof(file)) 105 err(1, "Error parsing file %s at line %d", 106 rworklist, lines); 107 else 108 break; 109 } 110 new_lump(s, l, state); 111 d -= l; 112 } 113 (void)fprintf(stderr, " done.\n"); 114 /* 115 * Return the number of bytes already read 116 * (at least not in worklist). 117 */ 118 return (d); 119 } 120 121 static void 122 usage(void) 123 { 124 (void)fprintf(stderr, 125 "usage: recoverdisk [-r worklist] [-w worklist] source-drive [destination]\n"); 126 exit(1); 127 } 128 129 static void 130 sighandler(__unused int sig) 131 { 132 133 aborting = 1; 134 } 135 136 int 137 main(int argc, char * const argv[]) 138 { 139 int ch; 140 int fdr, fdw; 141 off_t t, d; 142 size_t i, j; 143 int error, flags; 144 u_char *buf; 145 u_int sectorsize; 146 time_t t1, t2; 147 struct stat sb; 148 149 while ((ch = getopt(argc, argv, "r:w:")) != -1) { 150 switch (ch) { 151 case 'r': 152 rworklist = strdup(optarg); 153 if (rworklist == NULL) 154 err(1, "Cannot allocate enough memory"); 155 break; 156 case 'w': 157 wworklist = strdup(optarg); 158 if (wworklist == NULL) 159 err(1, "Cannot allocate enough memory"); 160 break; 161 default: 162 usage(); 163 /* NOTREACHED */ 164 } 165 } 166 argc -= optind; 167 argv += optind; 168 169 if (argc < 1 || argc > 2) 170 usage(); 171 172 fdr = open(argv[0], O_RDONLY); 173 if (fdr < 0) 174 err(1, "Cannot open read descriptor %s", argv[0]); 175 176 error = fstat(fdr, &sb); 177 if (error < 0) 178 err(1, "fstat failed"); 179 flags = O_WRONLY; 180 if (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode)) { 181 error = ioctl(fdr, DIOCGSECTORSIZE, §orsize); 182 if (error < 0) 183 err(1, "DIOCGSECTORSIZE failed"); 184 185 /* 186 * Make medsize roughly 64kB, depending on native sector 187 * size. bigsize has to be a multiple of medsize. 188 * For media with 2352 sectors, this will 189 * result in 2352, 63504, and 1016064 bytes. 190 */ 191 minsize = sectorsize; 192 medsize = (medsize / sectorsize) * sectorsize; 193 bigsize = medsize * 16; 194 195 error = ioctl(fdr, DIOCGMEDIASIZE, &t); 196 if (error < 0) 197 err(1, "DIOCGMEDIASIZE failed"); 198 } else { 199 t = sb.st_size; 200 flags |= O_CREAT | O_TRUNC; 201 } 202 203 buf = malloc(bigsize); 204 if (buf == NULL) 205 err(1, "Cannot allocate %jd bytes buffer", (intmax_t)bigsize); 206 207 if (argc > 1) { 208 fdw = open(argv[1], flags, DEFFILEMODE); 209 if (fdw < 0) 210 err(1, "Cannot open write descriptor %s", argv[1]); 211 } else 212 fdw = -1; 213 214 if (rworklist != NULL) { 215 d = read_worklist(t); 216 } else { 217 new_lump(0, t, 0); 218 d = 0; 219 } 220 if (wworklist != NULL) 221 signal(SIGINT, sighandler); 222 223 t1 = 0; 224 printf("%13s %7s %13s %5s %13s %13s %9s\n", 225 "start", "size", "len", "state", "done", "remaining", "% done"); 226 for (;;) { 227 lp = TAILQ_FIRST(&lumps); 228 if (lp == NULL) 229 break; 230 while (lp->len > 0 && !aborting) { 231 i = MIN(lp->len, (off_t)bigsize); 232 if (lp->state == 1) 233 i = MIN(lp->len, (off_t)medsize); 234 if (lp->state > 1) 235 i = MIN(lp->len, (off_t)minsize); 236 time(&t2); 237 if (t1 != t2 || lp->len < (off_t)bigsize) { 238 printf("\r%13jd %7zu %13jd %5d %13jd %13jd %.7f", 239 (intmax_t)lp->start, 240 i, 241 (intmax_t)lp->len, 242 lp->state, 243 (intmax_t)d, 244 (intmax_t)(t - d), 245 (double)d/(double)t); 246 t1 = t2; 247 } 248 if (i == 0) { 249 errx(1, "BOGUS i %10jd", (intmax_t)i); 250 } 251 fflush(stdout); 252 j = pread(fdr, buf, i, lp->start); 253 if (j == i) { 254 d += i; 255 if (fdw >= 0) 256 j = pwrite(fdw, buf, i, lp->start); 257 else 258 j = i; 259 if (j != i) 260 printf("\nWrite error at %jd/%zu\n", 261 lp->start, i); 262 lp->start += i; 263 lp->len -= i; 264 continue; 265 } 266 printf("\n%jd %zu failed %d\n", lp->start, i, errno); 267 new_lump(lp->start, i, lp->state + 1); 268 lp->start += i; 269 lp->len -= i; 270 } 271 if (aborting) { 272 save_worklist(); 273 return (0); 274 } 275 TAILQ_REMOVE(&lumps, lp, list); 276 free(lp); 277 } 278 printf("\nCompleted\n"); 279 return (0); 280 } 281