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: stable/11/sbin/recoverdisk/recoverdisk.c 344091 2019-02-13 09:28:48Z avos $ 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 27227081Sedstatic volatile sig_atomic_t aborting = 0; 28158337Smaximstatic size_t bigsize = 1024 * 1024; 29189691Sphkstatic size_t medsize; 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 59168972Sphk 60168972Sphk#define PRINT_HEADER \ 61168972Sphk printf("%13s %7s %13s %5s %13s %13s %9s\n", \ 62168972Sphk "start", "size", "block-len", "state", "done", "remaining", "% done") 63168972Sphk 64168972Sphk#define PRINT_STATUS(start, i, len, state, d, t) \ 65168972Sphk printf("\r%13jd %7zu %13jd %5d %13jd %13jd %9.5f", \ 66168972Sphk (intmax_t)start, \ 67168972Sphk i, \ 68168972Sphk (intmax_t)len, \ 69168972Sphk state, \ 70168972Sphk (intmax_t)d, \ 71168972Sphk (intmax_t)(t - d), \ 72168972Sphk 100*(double)d/(double)t) 73168972Sphk 74158337Smaxim/* Save the worklist if -w was given */ 75158337Smaximstatic void 76158337Smaximsave_worklist(void) 77158337Smaxim{ 78158337Smaxim FILE *file; 79189691Sphk struct lump *llp; 80158337Smaxim 81158337Smaxim if (wworklist != NULL) { 82158337Smaxim (void)fprintf(stderr, "\nSaving worklist ..."); 83158337Smaxim fflush(stderr); 84158337Smaxim 85158337Smaxim file = fopen(wworklist, "w"); 86158337Smaxim if (file == NULL) 87158337Smaxim err(1, "Error opening file %s", wworklist); 88158337Smaxim 89221304Suqs TAILQ_FOREACH(llp, &lumps, list) 90158337Smaxim fprintf(file, "%jd %jd %d\n", 91189691Sphk (intmax_t)llp->start, (intmax_t)llp->len, 92189691Sphk llp->state); 93189691Sphk fclose(file); 94158337Smaxim (void)fprintf(stderr, " done.\n"); 95158337Smaxim } 96158337Smaxim} 97158337Smaxim 98158337Smaxim/* Read the worklist if -r was given */ 99158337Smaximstatic off_t 100158337Smaximread_worklist(off_t t) 101158337Smaxim{ 102158337Smaxim off_t s, l, d; 103158337Smaxim int state, lines; 104158337Smaxim FILE *file; 105158337Smaxim 106158337Smaxim (void)fprintf(stderr, "Reading worklist ..."); 107158337Smaxim fflush(stderr); 108158337Smaxim file = fopen(rworklist, "r"); 109158337Smaxim if (file == NULL) 110158337Smaxim err(1, "Error opening file %s", rworklist); 111158337Smaxim 112158337Smaxim lines = 0; 113158337Smaxim d = t; 114158337Smaxim for (;;) { 115158337Smaxim ++lines; 116158337Smaxim if (3 != fscanf(file, "%jd %jd %d\n", &s, &l, &state)) { 117158337Smaxim if (!feof(file)) 118158337Smaxim err(1, "Error parsing file %s at line %d", 119158337Smaxim rworklist, lines); 120158337Smaxim else 121158337Smaxim break; 122158337Smaxim } 123158337Smaxim new_lump(s, l, state); 124158337Smaxim d -= l; 125158337Smaxim } 126344091Savos fclose(file); 127158337Smaxim (void)fprintf(stderr, " done.\n"); 128158337Smaxim /* 129158337Smaxim * Return the number of bytes already read 130158337Smaxim * (at least not in worklist). 131158337Smaxim */ 132158337Smaxim return (d); 133158337Smaxim} 134158337Smaxim 135158337Smaximstatic void 136158337Smaximusage(void) 137158337Smaxim{ 138221304Suqs (void)fprintf(stderr, "usage: recoverdisk [-b bigsize] [-r readlist] " 139221304Suqs "[-s interval] [-w writelist] source [destination]\n"); 140158337Smaxim exit(1); 141158337Smaxim} 142158337Smaxim 143158337Smaximstatic void 144158337Smaximsighandler(__unused int sig) 145158337Smaxim{ 146158337Smaxim 147158337Smaxim aborting = 1; 148158337Smaxim} 149158337Smaxim 150135911Sphkint 151158337Smaximmain(int argc, char * const argv[]) 152135911Sphk{ 153158337Smaxim int ch; 154135911Sphk int fdr, fdw; 155168972Sphk off_t t, d, start, len; 156135911Sphk size_t i, j; 157221304Suqs int error, state; 158135911Sphk u_char *buf; 159158337Smaxim u_int sectorsize; 160248279Sdelphij off_t stripesize; 161136257Sphk time_t t1, t2; 162149605Ssobomax struct stat sb; 163189691Sphk u_int n, snapshot = 60; 164135911Sphk 165189691Sphk while ((ch = getopt(argc, argv, "b:r:w:s:")) != -1) { 166158337Smaxim switch (ch) { 167189691Sphk case 'b': 168189691Sphk bigsize = strtoul(optarg, NULL, 0); 169189691Sphk break; 170158337Smaxim case 'r': 171158337Smaxim rworklist = strdup(optarg); 172158337Smaxim if (rworklist == NULL) 173158337Smaxim err(1, "Cannot allocate enough memory"); 174158337Smaxim break; 175189691Sphk case 's': 176189691Sphk snapshot = strtoul(optarg, NULL, 0); 177189691Sphk break; 178158337Smaxim case 'w': 179158337Smaxim wworklist = strdup(optarg); 180158337Smaxim if (wworklist == NULL) 181158337Smaxim err(1, "Cannot allocate enough memory"); 182158337Smaxim break; 183158337Smaxim default: 184158337Smaxim usage(); 185158337Smaxim /* NOTREACHED */ 186158337Smaxim } 187158337Smaxim } 188158337Smaxim argc -= optind; 189158337Smaxim argv += optind; 190135911Sphk 191158337Smaxim if (argc < 1 || argc > 2) 192158337Smaxim usage(); 193135911Sphk 194158337Smaxim fdr = open(argv[0], O_RDONLY); 195135911Sphk if (fdr < 0) 196158337Smaxim err(1, "Cannot open read descriptor %s", argv[0]); 197149605Ssobomax 198149605Ssobomax error = fstat(fdr, &sb); 199149605Ssobomax if (error < 0) 200149605Ssobomax err(1, "fstat failed"); 201149605Ssobomax if (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode)) { 202149605Ssobomax error = ioctl(fdr, DIOCGSECTORSIZE, §orsize); 203149605Ssobomax if (error < 0) 204149605Ssobomax err(1, "DIOCGSECTORSIZE failed"); 205158337Smaxim 206246329Sdelphij error = ioctl(fdr, DIOCGSTRIPESIZE, &stripesize); 207246329Sdelphij if (error == 0 && stripesize > sectorsize) 208246329Sdelphij sectorsize = stripesize; 209246329Sdelphij 210149605Ssobomax minsize = sectorsize; 211298872Spfg bigsize = rounddown(bigsize, sectorsize); 212149605Ssobomax 213149605Ssobomax error = ioctl(fdr, DIOCGMEDIASIZE, &t); 214149605Ssobomax if (error < 0) 215149605Ssobomax err(1, "DIOCGMEDIASIZE failed"); 216149605Ssobomax } else { 217149605Ssobomax t = sb.st_size; 218149605Ssobomax } 219149605Ssobomax 220189691Sphk if (bigsize < minsize) 221189691Sphk bigsize = minsize; 222189691Sphk 223189691Sphk for (ch = 0; (bigsize >> ch) > minsize; ch++) 224189691Sphk continue; 225189691Sphk medsize = bigsize >> (ch / 2); 226298872Spfg medsize = rounddown(medsize, minsize); 227189691Sphk 228189700Sphk fprintf(stderr, "Bigsize = %zu, medsize = %zu, minsize = %zu\n", 229189691Sphk bigsize, medsize, minsize); 230189691Sphk 231158337Smaxim buf = malloc(bigsize); 232158337Smaxim if (buf == NULL) 233189703Sed err(1, "Cannot allocate %zu bytes buffer", bigsize); 234158337Smaxim 235158337Smaxim if (argc > 1) { 236221304Suqs fdw = open(argv[1], O_WRONLY | O_CREAT, DEFFILEMODE); 237135911Sphk if (fdw < 0) 238158337Smaxim err(1, "Cannot open write descriptor %s", argv[1]); 239221304Suqs if (ftruncate(fdw, t) < 0) 240221304Suqs err(1, "Cannot truncate output %s to %jd bytes", 241221304Suqs argv[1], (intmax_t)t); 242158337Smaxim } else 243158337Smaxim fdw = -1; 244158337Smaxim 245158337Smaxim if (rworklist != NULL) { 246158337Smaxim d = read_worklist(t); 247135911Sphk } else { 248158337Smaxim new_lump(0, t, 0); 249158337Smaxim d = 0; 250135911Sphk } 251158337Smaxim if (wworklist != NULL) 252158337Smaxim signal(SIGINT, sighandler); 253135911Sphk 254136257Sphk t1 = 0; 255168972Sphk start = len = i = state = 0; 256168972Sphk PRINT_HEADER; 257189691Sphk n = 0; 258135911Sphk for (;;) { 259135911Sphk lp = TAILQ_FIRST(&lumps); 260135911Sphk if (lp == NULL) 261135911Sphk break; 262158337Smaxim while (lp->len > 0 && !aborting) { 263168972Sphk /* These are only copied for printing stats */ 264168972Sphk start = lp->start; 265168972Sphk len = lp->len; 266168972Sphk state = lp->state; 267168972Sphk 268159076Smatteo i = MIN(lp->len, (off_t)bigsize); 269135911Sphk if (lp->state == 1) 270159076Smatteo i = MIN(lp->len, (off_t)medsize); 271135911Sphk if (lp->state > 1) 272159076Smatteo i = MIN(lp->len, (off_t)minsize); 273136257Sphk time(&t2); 274159076Smatteo if (t1 != t2 || lp->len < (off_t)bigsize) { 275168972Sphk PRINT_STATUS(start, i, len, state, d, t); 276136257Sphk t1 = t2; 277189691Sphk if (++n == snapshot) { 278189691Sphk save_worklist(); 279189691Sphk n = 0; 280189691Sphk } 281136257Sphk } 282135911Sphk if (i == 0) { 283135911Sphk errx(1, "BOGUS i %10jd", (intmax_t)i); 284135911Sphk } 285135911Sphk fflush(stdout); 286135911Sphk j = pread(fdr, buf, i, lp->start); 287135911Sphk if (j == i) { 288135911Sphk d += i; 289135911Sphk if (fdw >= 0) 290135911Sphk j = pwrite(fdw, buf, i, lp->start); 291135911Sphk else 292135911Sphk j = i; 293135911Sphk if (j != i) 294136815Sdes printf("\nWrite error at %jd/%zu\n", 295136815Sdes lp->start, i); 296135911Sphk lp->start += i; 297135911Sphk lp->len -= i; 298135911Sphk continue; 299135911Sphk } 300187360Sphk printf("\n%jd %zu failed (%s)\n", 301187360Sphk lp->start, i, strerror(errno)); 302221304Suqs if (errno == EINVAL) { 303221304Suqs printf("read() size too big? Try with -b 131072"); 304221304Suqs aborting = 1; 305221304Suqs } 306187360Sphk if (errno == ENXIO) 307187360Sphk aborting = 1; 308135911Sphk new_lump(lp->start, i, lp->state + 1); 309135911Sphk lp->start += i; 310135911Sphk lp->len -= i; 311135911Sphk } 312158337Smaxim if (aborting) { 313158337Smaxim save_worklist(); 314158337Smaxim return (0); 315158337Smaxim } 316158337Smaxim TAILQ_REMOVE(&lumps, lp, list); 317135911Sphk free(lp); 318135911Sphk } 319168972Sphk PRINT_STATUS(start, i, len, state, d, t); 320190317Sphk save_worklist(); 321135911Sphk printf("\nCompleted\n"); 322158337Smaxim return (0); 323135911Sphk} 324