recoverdisk.c revision 159076
1158337Smaxim/*- 2135911Sphk * ---------------------------------------------------------------------------- 3135911Sphk * "THE BEER-WARE LICENSE" (Revision 42): 4135911Sphk * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you 5135911Sphk * can do whatever you want with this stuff. If we meet some day, and you think 6135911Sphk * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 7135911Sphk * ---------------------------------------------------------------------------- 8135911Sphk * 9135911Sphk * $FreeBSD: head/sbin/recoverdisk/recoverdisk.c 159076 2006-05-30 19:10:18Z matteo $ 10135911Sphk */ 11158337Smaxim#include <sys/param.h> 12158337Smaxim#include <sys/queue.h> 13158337Smaxim#include <sys/disk.h> 14158337Smaxim#include <sys/stat.h> 15158337Smaxim 16136257Sphk#include <err.h> 17135911Sphk#include <errno.h> 18135911Sphk#include <fcntl.h> 19158337Smaxim#include <signal.h> 20158337Smaxim#include <stdint.h> 21158337Smaxim#include <stdio.h> 22158337Smaxim#include <stdlib.h> 23158337Smaxim#include <string.h> 24136257Sphk#include <time.h> 25135911Sphk#include <unistd.h> 26135911Sphk 27158337Smaximvolatile sig_atomic_t aborting = 0; 28158337Smaximstatic size_t bigsize = 1024 * 1024; 29158337Smaximstatic size_t medsize = 64 * 1024; 30158337Smaximstatic size_t minsize = 512; 31135911Sphk 32135911Sphkstruct lump { 33135911Sphk off_t start; 34135911Sphk off_t len; 35135911Sphk int state; 36135911Sphk TAILQ_ENTRY(lump) list; 37135911Sphk}; 38135911Sphk 39135911Sphkstatic TAILQ_HEAD(, lump) lumps = TAILQ_HEAD_INITIALIZER(lumps); 40135911Sphk 41135911Sphkstatic void 42135911Sphknew_lump(off_t start, off_t len, int state) 43135911Sphk{ 44135911Sphk struct lump *lp; 45135911Sphk 46135911Sphk lp = malloc(sizeof *lp); 47135911Sphk if (lp == NULL) 48135911Sphk err(1, "Malloc failed"); 49135911Sphk lp->start = start; 50135911Sphk lp->len = len; 51135911Sphk lp->state = state; 52135911Sphk TAILQ_INSERT_TAIL(&lumps, lp, list); 53135911Sphk} 54135911Sphk 55158337Smaximstatic struct lump *lp; 56158337Smaximstatic char *wworklist = NULL; 57158337Smaximstatic char *rworklist = NULL; 58158337Smaxim 59158337Smaxim/* Save the worklist if -w was given */ 60158337Smaximstatic void 61158337Smaximsave_worklist(void) 62158337Smaxim{ 63158337Smaxim FILE *file; 64158337Smaxim 65158337Smaxim if (wworklist != NULL) { 66158337Smaxim (void)fprintf(stderr, "\nSaving worklist ..."); 67158337Smaxim fflush(stderr); 68158337Smaxim 69158337Smaxim file = fopen(wworklist, "w"); 70158337Smaxim if (file == NULL) 71158337Smaxim err(1, "Error opening file %s", wworklist); 72158337Smaxim 73158337Smaxim for (;;) { 74158337Smaxim lp = TAILQ_FIRST(&lumps); 75158337Smaxim if (lp == NULL) 76158337Smaxim break; 77158337Smaxim fprintf(file, "%jd %jd %d\n", 78158337Smaxim (intmax_t)lp->start, (intmax_t)lp->len, lp->state); 79158337Smaxim TAILQ_REMOVE(&lumps, lp, list); 80158337Smaxim } 81158337Smaxim (void)fprintf(stderr, " done.\n"); 82158337Smaxim } 83158337Smaxim} 84158337Smaxim 85158337Smaxim/* Read the worklist if -r was given */ 86158337Smaximstatic off_t 87158337Smaximread_worklist(off_t t) 88158337Smaxim{ 89158337Smaxim off_t s, l, d; 90158337Smaxim int state, lines; 91158337Smaxim FILE *file; 92158337Smaxim 93158337Smaxim (void)fprintf(stderr, "Reading worklist ..."); 94158337Smaxim fflush(stderr); 95158337Smaxim file = fopen(rworklist, "r"); 96158337Smaxim if (file == NULL) 97158337Smaxim err(1, "Error opening file %s", rworklist); 98158337Smaxim 99158337Smaxim lines = 0; 100158337Smaxim d = t; 101158337Smaxim for (;;) { 102158337Smaxim ++lines; 103158337Smaxim if (3 != fscanf(file, "%jd %jd %d\n", &s, &l, &state)) { 104158337Smaxim if (!feof(file)) 105158337Smaxim err(1, "Error parsing file %s at line %d", 106158337Smaxim rworklist, lines); 107158337Smaxim else 108158337Smaxim break; 109158337Smaxim } 110158337Smaxim new_lump(s, l, state); 111158337Smaxim d -= l; 112158337Smaxim } 113158337Smaxim (void)fprintf(stderr, " done.\n"); 114158337Smaxim /* 115158337Smaxim * Return the number of bytes already read 116158337Smaxim * (at least not in worklist). 117158337Smaxim */ 118158337Smaxim return (d); 119158337Smaxim} 120158337Smaxim 121158337Smaximstatic void 122158337Smaximusage(void) 123158337Smaxim{ 124158337Smaxim (void)fprintf(stderr, 125158337Smaxim "usage: recoverdisk [-r worklist] [-w worklist] source-drive [destination]\n"); 126158337Smaxim exit(1); 127158337Smaxim} 128158337Smaxim 129158337Smaximstatic void 130158337Smaximsighandler(__unused int sig) 131158337Smaxim{ 132158337Smaxim 133158337Smaxim aborting = 1; 134158337Smaxim} 135158337Smaxim 136135911Sphkint 137158337Smaximmain(int argc, char * const argv[]) 138135911Sphk{ 139158337Smaxim int ch; 140135911Sphk int fdr, fdw; 141158337Smaxim off_t t, d; 142135911Sphk size_t i, j; 143149605Ssobomax int error, flags; 144135911Sphk u_char *buf; 145158337Smaxim u_int sectorsize; 146136257Sphk time_t t1, t2; 147149605Ssobomax struct stat sb; 148135911Sphk 149158337Smaxim while ((ch = getopt(argc, argv, "r:w:")) != -1) { 150158337Smaxim switch (ch) { 151158337Smaxim case 'r': 152158337Smaxim rworklist = strdup(optarg); 153158337Smaxim if (rworklist == NULL) 154158337Smaxim err(1, "Cannot allocate enough memory"); 155158337Smaxim break; 156158337Smaxim case 'w': 157158337Smaxim wworklist = strdup(optarg); 158158337Smaxim if (wworklist == NULL) 159158337Smaxim err(1, "Cannot allocate enough memory"); 160158337Smaxim break; 161158337Smaxim default: 162158337Smaxim usage(); 163158337Smaxim /* NOTREACHED */ 164158337Smaxim } 165158337Smaxim } 166158337Smaxim argc -= optind; 167158337Smaxim argv += optind; 168135911Sphk 169158337Smaxim if (argc < 1 || argc > 2) 170158337Smaxim usage(); 171135911Sphk 172158337Smaxim fdr = open(argv[0], O_RDONLY); 173135911Sphk if (fdr < 0) 174158337Smaxim err(1, "Cannot open read descriptor %s", argv[0]); 175149605Ssobomax 176149605Ssobomax error = fstat(fdr, &sb); 177149605Ssobomax if (error < 0) 178149605Ssobomax err(1, "fstat failed"); 179149605Ssobomax flags = O_WRONLY; 180149605Ssobomax if (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode)) { 181149605Ssobomax error = ioctl(fdr, DIOCGSECTORSIZE, §orsize); 182149605Ssobomax if (error < 0) 183149605Ssobomax err(1, "DIOCGSECTORSIZE failed"); 184158337Smaxim 185158337Smaxim /* 186158337Smaxim * Make medsize roughly 64kB, depending on native sector 187158337Smaxim * size. bigsize has to be a multiple of medsize. 188158337Smaxim * For media with 2352 sectors, this will 189158337Smaxim * result in 2352, 63504, and 1016064 bytes. 190158337Smaxim */ 191149605Ssobomax minsize = sectorsize; 192158337Smaxim medsize = (medsize / sectorsize) * sectorsize; 193158337Smaxim bigsize = medsize * 16; 194149605Ssobomax 195149605Ssobomax error = ioctl(fdr, DIOCGMEDIASIZE, &t); 196149605Ssobomax if (error < 0) 197149605Ssobomax err(1, "DIOCGMEDIASIZE failed"); 198149605Ssobomax } else { 199149605Ssobomax t = sb.st_size; 200149605Ssobomax flags |= O_CREAT | O_TRUNC; 201149605Ssobomax } 202149605Ssobomax 203158337Smaxim buf = malloc(bigsize); 204158337Smaxim if (buf == NULL) 205158337Smaxim err(1, "Cannot allocate %jd bytes buffer", (intmax_t)bigsize); 206158337Smaxim 207158337Smaxim if (argc > 1) { 208158337Smaxim fdw = open(argv[1], flags, DEFFILEMODE); 209135911Sphk if (fdw < 0) 210158337Smaxim err(1, "Cannot open write descriptor %s", argv[1]); 211158337Smaxim } else 212158337Smaxim fdw = -1; 213158337Smaxim 214158337Smaxim if (rworklist != NULL) { 215158337Smaxim d = read_worklist(t); 216135911Sphk } else { 217158337Smaxim new_lump(0, t, 0); 218158337Smaxim d = 0; 219135911Sphk } 220158337Smaxim if (wworklist != NULL) 221158337Smaxim signal(SIGINT, sighandler); 222135911Sphk 223136257Sphk t1 = 0; 224158337Smaxim printf("%13s %7s %13s %5s %13s %13s %9s\n", 225158337Smaxim "start", "size", "len", "state", "done", "remaining", "% done"); 226135911Sphk for (;;) { 227135911Sphk lp = TAILQ_FIRST(&lumps); 228135911Sphk if (lp == NULL) 229135911Sphk break; 230158337Smaxim while (lp->len > 0 && !aborting) { 231159076Smatteo i = MIN(lp->len, (off_t)bigsize); 232135911Sphk if (lp->state == 1) 233159076Smatteo i = MIN(lp->len, (off_t)medsize); 234135911Sphk if (lp->state > 1) 235159076Smatteo i = MIN(lp->len, (off_t)minsize); 236136257Sphk time(&t2); 237159076Smatteo if (t1 != t2 || lp->len < (off_t)bigsize) { 238158337Smaxim printf("\r%13jd %7zu %13jd %5d %13jd %13jd %.7f", 239136257Sphk (intmax_t)lp->start, 240136815Sdes i, 241136257Sphk (intmax_t)lp->len, 242136257Sphk lp->state, 243136257Sphk (intmax_t)d, 244136257Sphk (intmax_t)(t - d), 245136257Sphk (double)d/(double)t); 246136257Sphk t1 = t2; 247136257Sphk } 248135911Sphk if (i == 0) { 249135911Sphk errx(1, "BOGUS i %10jd", (intmax_t)i); 250135911Sphk } 251135911Sphk fflush(stdout); 252135911Sphk j = pread(fdr, buf, i, lp->start); 253135911Sphk if (j == i) { 254135911Sphk d += i; 255135911Sphk if (fdw >= 0) 256135911Sphk j = pwrite(fdw, buf, i, lp->start); 257135911Sphk else 258135911Sphk j = i; 259135911Sphk if (j != i) 260136815Sdes printf("\nWrite error at %jd/%zu\n", 261136815Sdes lp->start, i); 262135911Sphk lp->start += i; 263135911Sphk lp->len -= i; 264135911Sphk continue; 265135911Sphk } 266136815Sdes printf("\n%jd %zu failed %d\n", lp->start, i, errno); 267135911Sphk new_lump(lp->start, i, lp->state + 1); 268135911Sphk lp->start += i; 269135911Sphk lp->len -= i; 270135911Sphk } 271158337Smaxim if (aborting) { 272158337Smaxim save_worklist(); 273158337Smaxim return (0); 274158337Smaxim } 275158337Smaxim TAILQ_REMOVE(&lumps, lp, list); 276135911Sphk free(lp); 277135911Sphk } 278135911Sphk printf("\nCompleted\n"); 279158337Smaxim return (0); 280135911Sphk} 281