xref: /freebsd/sbin/recoverdisk/recoverdisk.c (revision d830cac9fed1882a192b0ec5c96fb3ac9bfbc2ee)
1fd64dc9aSMaxim Konovalov /*-
264de3fddSPedro F. Giffuni  * SPDX-License-Identifier: Beerware
364de3fddSPedro F. Giffuni  *
439d969aeSPoul-Henning Kamp  * ----------------------------------------------------------------------------
539d969aeSPoul-Henning Kamp  * "THE BEER-WARE LICENSE" (Revision 42):
639d969aeSPoul-Henning Kamp  * <phk@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
739d969aeSPoul-Henning Kamp  * can do whatever you want with this stuff. If we meet some day, and you think
839d969aeSPoul-Henning Kamp  * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
939d969aeSPoul-Henning Kamp  * ----------------------------------------------------------------------------
1039d969aeSPoul-Henning Kamp  */
11fd64dc9aSMaxim Konovalov #include <sys/param.h>
1239d969aeSPoul-Henning Kamp #include <sys/queue.h>
1339d969aeSPoul-Henning Kamp #include <sys/disk.h>
14db45c56dSMaxim Sobolev #include <sys/stat.h>
1539d969aeSPoul-Henning Kamp 
169546e08eSPoul-Henning Kamp #include <assert.h>
17fd64dc9aSMaxim Konovalov #include <err.h>
18fd64dc9aSMaxim Konovalov #include <errno.h>
199546e08eSPoul-Henning Kamp #include <math.h>
20fd64dc9aSMaxim Konovalov #include <fcntl.h>
21fd64dc9aSMaxim Konovalov #include <signal.h>
22fd64dc9aSMaxim Konovalov #include <stdint.h>
23fd64dc9aSMaxim Konovalov #include <stdio.h>
24fd64dc9aSMaxim Konovalov #include <stdlib.h>
25fd64dc9aSMaxim Konovalov #include <string.h>
269546e08eSPoul-Henning Kamp #include <termios.h>
27fd64dc9aSMaxim Konovalov #include <time.h>
28fd64dc9aSMaxim Konovalov #include <unistd.h>
29fd64dc9aSMaxim Konovalov 
309546e08eSPoul-Henning Kamp /* Safe printf into a fixed-size buffer */
319546e08eSPoul-Henning Kamp #define bprintf(buf, fmt, ...)                                          \
329546e08eSPoul-Henning Kamp 	do {                                                            \
339546e08eSPoul-Henning Kamp 		int ibprintf;                                           \
349546e08eSPoul-Henning Kamp 		ibprintf = snprintf(buf, sizeof buf, fmt, __VA_ARGS__); \
359546e08eSPoul-Henning Kamp 		assert(ibprintf >= 0 && ibprintf < (int)sizeof buf);    \
369546e08eSPoul-Henning Kamp 	} while (0)
3739d969aeSPoul-Henning Kamp 
3839d969aeSPoul-Henning Kamp struct lump {
3939d969aeSPoul-Henning Kamp 	off_t			start;
4039d969aeSPoul-Henning Kamp 	off_t			len;
4139d969aeSPoul-Henning Kamp 	int			state;
4239d969aeSPoul-Henning Kamp 	TAILQ_ENTRY(lump)	list;
4339d969aeSPoul-Henning Kamp };
4439d969aeSPoul-Henning Kamp 
459546e08eSPoul-Henning Kamp struct period {
469546e08eSPoul-Henning Kamp 	time_t			t0;
479546e08eSPoul-Henning Kamp 	time_t			t1;
489546e08eSPoul-Henning Kamp 	char			str[20];
499546e08eSPoul-Henning Kamp 	off_t			bytes_read;
509546e08eSPoul-Henning Kamp 	TAILQ_ENTRY(period)	list;
519546e08eSPoul-Henning Kamp };
529546e08eSPoul-Henning Kamp TAILQ_HEAD(period_head, period);
539546e08eSPoul-Henning Kamp 
549546e08eSPoul-Henning Kamp static volatile sig_atomic_t aborting = 0;
559546e08eSPoul-Henning Kamp static int verbose = 0;
569546e08eSPoul-Henning Kamp static size_t bigsize = 1024 * 1024;
579546e08eSPoul-Henning Kamp static size_t medsize;
589546e08eSPoul-Henning Kamp static size_t minsize = 512;
599546e08eSPoul-Henning Kamp static off_t tot_size;
609546e08eSPoul-Henning Kamp static off_t done_size;
619546e08eSPoul-Henning Kamp static char *input;
629546e08eSPoul-Henning Kamp static char *wworklist = NULL;
639546e08eSPoul-Henning Kamp static char *rworklist = NULL;
649546e08eSPoul-Henning Kamp static const char *unreadable_pattern = "_UNREAD_";
659546e08eSPoul-Henning Kamp static const int write_errors_are_fatal = 1;
669546e08eSPoul-Henning Kamp static int fdr, fdw;
679546e08eSPoul-Henning Kamp 
6839d969aeSPoul-Henning Kamp static TAILQ_HEAD(, lump) lumps = TAILQ_HEAD_INITIALIZER(lumps);
699546e08eSPoul-Henning Kamp static struct period_head minute = TAILQ_HEAD_INITIALIZER(minute);
709546e08eSPoul-Henning Kamp static struct period_head quarter = TAILQ_HEAD_INITIALIZER(quarter);
719546e08eSPoul-Henning Kamp static struct period_head hour = TAILQ_HEAD_INITIALIZER(quarter);
729546e08eSPoul-Henning Kamp static struct period_head day = TAILQ_HEAD_INITIALIZER(quarter);
739546e08eSPoul-Henning Kamp 
749546e08eSPoul-Henning Kamp /**********************************************************************/
759546e08eSPoul-Henning Kamp 
769546e08eSPoul-Henning Kamp static void
report_good_read2(time_t now,size_t bytes,struct period_head * ph,time_t dt)779546e08eSPoul-Henning Kamp report_good_read2(time_t now, size_t bytes, struct period_head *ph, time_t dt)
789546e08eSPoul-Henning Kamp {
799546e08eSPoul-Henning Kamp 	struct period *pp;
809546e08eSPoul-Henning Kamp 	const char *fmt;
819546e08eSPoul-Henning Kamp 	struct tm tm1;
829546e08eSPoul-Henning Kamp 
839546e08eSPoul-Henning Kamp 	pp = TAILQ_FIRST(ph);
849546e08eSPoul-Henning Kamp 	if (pp == NULL || pp->t1 < now) {
856991cb36SJohn Baldwin 		pp = calloc(1, sizeof(*pp));
869546e08eSPoul-Henning Kamp 		assert(pp != NULL);
879546e08eSPoul-Henning Kamp 		pp->t0 = (now / dt) * dt;
889546e08eSPoul-Henning Kamp 		pp->t1 = (now / dt + 1) * dt;
899546e08eSPoul-Henning Kamp 		assert(localtime_r(&pp->t0, &tm1) != NULL);
909546e08eSPoul-Henning Kamp 		if (dt < 86400)
919546e08eSPoul-Henning Kamp 			fmt = "%H:%M";
929546e08eSPoul-Henning Kamp 		else
939546e08eSPoul-Henning Kamp 			fmt = "%d%b";
949546e08eSPoul-Henning Kamp 		assert(strftime(pp->str, sizeof pp->str, fmt, &tm1) != 0);
959546e08eSPoul-Henning Kamp 		TAILQ_INSERT_HEAD(ph, pp, list);
969546e08eSPoul-Henning Kamp 	}
979546e08eSPoul-Henning Kamp 	pp->bytes_read += bytes;
989546e08eSPoul-Henning Kamp }
999546e08eSPoul-Henning Kamp 
1009546e08eSPoul-Henning Kamp static void
report_good_read(time_t now,size_t bytes)1019546e08eSPoul-Henning Kamp report_good_read(time_t now, size_t bytes)
1029546e08eSPoul-Henning Kamp {
1039546e08eSPoul-Henning Kamp 
1049546e08eSPoul-Henning Kamp 	report_good_read2(now, bytes, &minute, 60L);
1059546e08eSPoul-Henning Kamp 	report_good_read2(now, bytes, &quarter, 900L);
1069546e08eSPoul-Henning Kamp 	report_good_read2(now, bytes, &hour, 3600L);
1079546e08eSPoul-Henning Kamp 	report_good_read2(now, bytes, &day, 86400L);
1089546e08eSPoul-Henning Kamp }
1099546e08eSPoul-Henning Kamp 
1109546e08eSPoul-Henning Kamp static void
report_one_period(const char * period,struct period_head * ph)1119546e08eSPoul-Henning Kamp report_one_period(const char *period, struct period_head *ph)
1129546e08eSPoul-Henning Kamp {
1139546e08eSPoul-Henning Kamp 	struct period *pp;
1149546e08eSPoul-Henning Kamp 	int n;
1159546e08eSPoul-Henning Kamp 
1169546e08eSPoul-Henning Kamp 	n = 0;
1179546e08eSPoul-Henning Kamp 	printf("%s \xe2\x94\x82", period);
1189546e08eSPoul-Henning Kamp 	TAILQ_FOREACH(pp, ph, list) {
1199546e08eSPoul-Henning Kamp 		if (n == 3) {
1209546e08eSPoul-Henning Kamp 			TAILQ_REMOVE(ph, pp, list);
1219546e08eSPoul-Henning Kamp 			free(pp);
1229546e08eSPoul-Henning Kamp 			break;
1239546e08eSPoul-Henning Kamp 		}
1249546e08eSPoul-Henning Kamp 		if (n++)
1259546e08eSPoul-Henning Kamp 			printf("  \xe2\x94\x82");
1269546e08eSPoul-Henning Kamp 		printf("  %s %14jd", pp->str, pp->bytes_read);
1279546e08eSPoul-Henning Kamp 	}
1289546e08eSPoul-Henning Kamp 	for (; n < 3; n++) {
1299546e08eSPoul-Henning Kamp 		printf("  \xe2\x94\x82");
1309546e08eSPoul-Henning Kamp 		printf("  %5s %14s", "", "");
1319546e08eSPoul-Henning Kamp 	}
1329546e08eSPoul-Henning Kamp 	printf("\x1b[K\n");
1339546e08eSPoul-Henning Kamp }
1349546e08eSPoul-Henning Kamp 
1359546e08eSPoul-Henning Kamp static void
report_periods(void)1369546e08eSPoul-Henning Kamp report_periods(void)
1379546e08eSPoul-Henning Kamp {
1389546e08eSPoul-Henning Kamp 	report_one_period("1m ", &minute);
1399546e08eSPoul-Henning Kamp 	report_one_period("15m", &quarter);
1409546e08eSPoul-Henning Kamp 	report_one_period("1h ", &hour);
1419546e08eSPoul-Henning Kamp 	report_one_period("1d ", &day);
1429546e08eSPoul-Henning Kamp }
1439546e08eSPoul-Henning Kamp 
1449546e08eSPoul-Henning Kamp /**********************************************************************/
1459546e08eSPoul-Henning Kamp 
1469546e08eSPoul-Henning Kamp static void
set_verbose(void)1479546e08eSPoul-Henning Kamp set_verbose(void)
1489546e08eSPoul-Henning Kamp {
1499546e08eSPoul-Henning Kamp 	struct winsize wsz;
1509546e08eSPoul-Henning Kamp 
1519546e08eSPoul-Henning Kamp 	if (!isatty(STDIN_FILENO) || ioctl(STDIN_FILENO, TIOCGWINSZ, &wsz))
1529546e08eSPoul-Henning Kamp 		return;
1539546e08eSPoul-Henning Kamp 	verbose = 1;
1549546e08eSPoul-Henning Kamp }
1559546e08eSPoul-Henning Kamp 
1569546e08eSPoul-Henning Kamp static void
report_header(int eol)1579546e08eSPoul-Henning Kamp report_header(int eol)
1589546e08eSPoul-Henning Kamp {
1599546e08eSPoul-Henning Kamp 	printf("%13s %7s %13s %5s %13s %13s %9s",
1609546e08eSPoul-Henning Kamp 	    "start",
1619546e08eSPoul-Henning Kamp 	    "size",
1629546e08eSPoul-Henning Kamp 	    "block-len",
1639546e08eSPoul-Henning Kamp 	    "pass",
1649546e08eSPoul-Henning Kamp 	    "done",
1659546e08eSPoul-Henning Kamp 	    "remaining",
1669546e08eSPoul-Henning Kamp 	    "% done");
1679546e08eSPoul-Henning Kamp 	if (eol)
1689546e08eSPoul-Henning Kamp 		printf("\x1b[K");
1699546e08eSPoul-Henning Kamp 	putchar('\n');
1709546e08eSPoul-Henning Kamp }
1719546e08eSPoul-Henning Kamp 
1729546e08eSPoul-Henning Kamp #define REPORTWID 79
1739546e08eSPoul-Henning Kamp 
1749546e08eSPoul-Henning Kamp static void
report_hline(const char * how)1759546e08eSPoul-Henning Kamp report_hline(const char *how)
1769546e08eSPoul-Henning Kamp {
1779546e08eSPoul-Henning Kamp 	int j;
1789546e08eSPoul-Henning Kamp 
1799546e08eSPoul-Henning Kamp 	for (j = 0; j < REPORTWID; j++) {
1809546e08eSPoul-Henning Kamp 		if (how && (j == 4 || j == 29 || j == 54)) {
1819546e08eSPoul-Henning Kamp 			printf("%s", how);
1829546e08eSPoul-Henning Kamp 		} else {
1839546e08eSPoul-Henning Kamp 			printf("\xe2\x94\x80");
1849546e08eSPoul-Henning Kamp 		}
1859546e08eSPoul-Henning Kamp 	}
1869546e08eSPoul-Henning Kamp 	printf("\x1b[K\n");
1879546e08eSPoul-Henning Kamp }
1889546e08eSPoul-Henning Kamp 
1899546e08eSPoul-Henning Kamp static off_t hist[REPORTWID];
1909546e08eSPoul-Henning Kamp static off_t last_done = -1;
1919546e08eSPoul-Henning Kamp 
1929546e08eSPoul-Henning Kamp static void
report_histogram(const struct lump * lp)1939546e08eSPoul-Henning Kamp report_histogram(const struct lump *lp)
1949546e08eSPoul-Henning Kamp {
1959546e08eSPoul-Henning Kamp 	off_t j, bucket, fp, fe, k, now;
1969546e08eSPoul-Henning Kamp 	double a;
1979546e08eSPoul-Henning Kamp 	struct lump *lp2;
1989546e08eSPoul-Henning Kamp 
1999546e08eSPoul-Henning Kamp 	bucket = tot_size / REPORTWID;
2009546e08eSPoul-Henning Kamp 	if (tot_size > bucket * REPORTWID)
2019546e08eSPoul-Henning Kamp 		bucket += 1;
2029546e08eSPoul-Henning Kamp 	if (done_size != last_done) {
2039546e08eSPoul-Henning Kamp 		memset(hist, 0, sizeof hist);
2049546e08eSPoul-Henning Kamp 		TAILQ_FOREACH(lp2, &lumps, list) {
2059546e08eSPoul-Henning Kamp 			fp = lp2->start;
2069546e08eSPoul-Henning Kamp 			fe = lp2->start + lp2->len;
2079546e08eSPoul-Henning Kamp 			for (j = fp / bucket; fp < fe; j++) {
2089546e08eSPoul-Henning Kamp 				k = (j + 1) * bucket;
2099546e08eSPoul-Henning Kamp 				if (k > fe)
2109546e08eSPoul-Henning Kamp 					k = fe;
2119546e08eSPoul-Henning Kamp 				k -= fp;
2129546e08eSPoul-Henning Kamp 				hist[j] += k;
2139546e08eSPoul-Henning Kamp 				fp += k;
2149546e08eSPoul-Henning Kamp 			}
2159546e08eSPoul-Henning Kamp 		}
2169546e08eSPoul-Henning Kamp 		last_done = done_size;
2179546e08eSPoul-Henning Kamp 	}
2189546e08eSPoul-Henning Kamp 	now = lp->start / bucket;
2199546e08eSPoul-Henning Kamp 	for (j = 0; j < REPORTWID; j++) {
2209546e08eSPoul-Henning Kamp 		a = round(8 * (double)hist[j] / bucket);
2219546e08eSPoul-Henning Kamp 		assert (a >= 0 && a < 9);
2229546e08eSPoul-Henning Kamp 		if (a == 0 && hist[j])
2239546e08eSPoul-Henning Kamp 			a = 1;
2249546e08eSPoul-Henning Kamp 		if (j == now)
2259546e08eSPoul-Henning Kamp 			printf("\x1b[31m");
2269546e08eSPoul-Henning Kamp 		if (a == 0) {
2279546e08eSPoul-Henning Kamp 			putchar(' ');
2289546e08eSPoul-Henning Kamp 		} else {
2299546e08eSPoul-Henning Kamp 			putchar(0xe2);
2309546e08eSPoul-Henning Kamp 			putchar(0x96);
2319546e08eSPoul-Henning Kamp 			putchar(0x80 + (int)a);
2329546e08eSPoul-Henning Kamp 		}
2339546e08eSPoul-Henning Kamp 		if (j == now)
2349546e08eSPoul-Henning Kamp 			printf("\x1b[0m");
2359546e08eSPoul-Henning Kamp 	}
2369546e08eSPoul-Henning Kamp 	putchar('\n');
2379546e08eSPoul-Henning Kamp }
2389546e08eSPoul-Henning Kamp 
2399546e08eSPoul-Henning Kamp static void
report(const struct lump * lp,size_t sz)2409546e08eSPoul-Henning Kamp report(const struct lump *lp, size_t sz)
2419546e08eSPoul-Henning Kamp {
2429546e08eSPoul-Henning Kamp 	struct winsize wsz;
2439546e08eSPoul-Henning Kamp 	int j;
2449546e08eSPoul-Henning Kamp 
2459546e08eSPoul-Henning Kamp 	assert(lp != NULL);
2469546e08eSPoul-Henning Kamp 
2479546e08eSPoul-Henning Kamp 	if (verbose) {
2489546e08eSPoul-Henning Kamp 		printf("\x1b[H%s\x1b[K\n", input);
2499546e08eSPoul-Henning Kamp 		report_header(1);
2509546e08eSPoul-Henning Kamp 	} else {
2519546e08eSPoul-Henning Kamp 		putchar('\r');
2529546e08eSPoul-Henning Kamp 	}
2539546e08eSPoul-Henning Kamp 
2549546e08eSPoul-Henning Kamp 	printf("%13jd %7zu %13jd %5d %13jd %13jd %9.4f",
2559546e08eSPoul-Henning Kamp 	    (intmax_t)lp->start,
2569546e08eSPoul-Henning Kamp 	    sz,
2579546e08eSPoul-Henning Kamp 	    (intmax_t)lp->len,
2589546e08eSPoul-Henning Kamp 	    lp->state,
2599546e08eSPoul-Henning Kamp 	    (intmax_t)done_size,
2609546e08eSPoul-Henning Kamp 	    (intmax_t)(tot_size - done_size),
2619546e08eSPoul-Henning Kamp 	    100*(double)done_size/(double)tot_size
2629546e08eSPoul-Henning Kamp 	);
2639546e08eSPoul-Henning Kamp 
2649546e08eSPoul-Henning Kamp 	if (verbose) {
2659546e08eSPoul-Henning Kamp 		printf("\x1b[K\n");
2669546e08eSPoul-Henning Kamp 		report_hline(NULL);
2679546e08eSPoul-Henning Kamp 		report_histogram(lp);
2689546e08eSPoul-Henning Kamp 		if (TAILQ_EMPTY(&minute)) {
2699546e08eSPoul-Henning Kamp 			report_hline(NULL);
2709546e08eSPoul-Henning Kamp 		} else {
2719546e08eSPoul-Henning Kamp 			report_hline("\xe2\x94\xac");
2729546e08eSPoul-Henning Kamp 			report_periods();
2739546e08eSPoul-Henning Kamp 			report_hline("\xe2\x94\xb4");
2749546e08eSPoul-Henning Kamp 		}
2759546e08eSPoul-Henning Kamp 		j = ioctl(STDIN_FILENO, TIOCGWINSZ, &wsz);
2769546e08eSPoul-Henning Kamp 		if (!j)
2779546e08eSPoul-Henning Kamp 			printf("\x1b[%d;1H", wsz.ws_row);
2789546e08eSPoul-Henning Kamp 	}
2799546e08eSPoul-Henning Kamp 	fflush(stdout);
2809546e08eSPoul-Henning Kamp }
2819546e08eSPoul-Henning Kamp 
2829546e08eSPoul-Henning Kamp /**********************************************************************/
28339d969aeSPoul-Henning Kamp 
28439d969aeSPoul-Henning Kamp static void
new_lump(off_t start,off_t len,int state)28539d969aeSPoul-Henning Kamp new_lump(off_t start, off_t len, int state)
28639d969aeSPoul-Henning Kamp {
28739d969aeSPoul-Henning Kamp 	struct lump *lp;
28839d969aeSPoul-Henning Kamp 
28939d969aeSPoul-Henning Kamp 	lp = malloc(sizeof *lp);
29039d969aeSPoul-Henning Kamp 	if (lp == NULL)
29139d969aeSPoul-Henning Kamp 		err(1, "Malloc failed");
29239d969aeSPoul-Henning Kamp 	lp->start = start;
29339d969aeSPoul-Henning Kamp 	lp->len = len;
29439d969aeSPoul-Henning Kamp 	lp->state = state;
29539d969aeSPoul-Henning Kamp 	TAILQ_INSERT_TAIL(&lumps, lp, list);
29639d969aeSPoul-Henning Kamp }
29739d969aeSPoul-Henning Kamp 
2989546e08eSPoul-Henning Kamp /**********************************************************************
2999546e08eSPoul-Henning Kamp  * Save the worklist if -w was given
3009546e08eSPoul-Henning Kamp  */
301fd64dc9aSMaxim Konovalov 
302fd64dc9aSMaxim Konovalov static void
save_worklist(void)303fd64dc9aSMaxim Konovalov save_worklist(void)
30439d969aeSPoul-Henning Kamp {
305fd64dc9aSMaxim Konovalov 	FILE *file;
306584f5546SPoul-Henning Kamp 	struct lump *llp;
3079546e08eSPoul-Henning Kamp 	char buf[PATH_MAX];
3089546e08eSPoul-Henning Kamp 
3099546e08eSPoul-Henning Kamp 	if (fdw >= 0 && fdatasync(fdw))
3109546e08eSPoul-Henning Kamp 		err(1, "Write error, probably disk full");
311fd64dc9aSMaxim Konovalov 
312fd64dc9aSMaxim Konovalov 	if (wworklist != NULL) {
3139546e08eSPoul-Henning Kamp 		bprintf(buf, "%s.tmp", wworklist);
314fd64dc9aSMaxim Konovalov 		(void)fprintf(stderr, "\nSaving worklist ...");
3159546e08eSPoul-Henning Kamp 		(void)fflush(stderr);
316fd64dc9aSMaxim Konovalov 
3179546e08eSPoul-Henning Kamp 		file = fopen(buf, "w");
318fd64dc9aSMaxim Konovalov 		if (file == NULL)
3199546e08eSPoul-Henning Kamp 			err(1, "Error opening file %s", buf);
320fd64dc9aSMaxim Konovalov 
321584f5546SPoul-Henning Kamp 		TAILQ_FOREACH(llp, &lumps, list)
322fd64dc9aSMaxim Konovalov 			fprintf(file, "%jd %jd %d\n",
323584f5546SPoul-Henning Kamp 			    (intmax_t)llp->start, (intmax_t)llp->len,
324584f5546SPoul-Henning Kamp 			    llp->state);
3259546e08eSPoul-Henning Kamp 		(void)fflush(file);
3269546e08eSPoul-Henning Kamp 		if (ferror(file) || fdatasync(fileno(file)) || fclose(file))
3279546e08eSPoul-Henning Kamp 			err(1, "Error writing file %s", buf);
3289546e08eSPoul-Henning Kamp 		if (rename(buf, wworklist))
3299546e08eSPoul-Henning Kamp 			err(1, "Error renaming %s to %s", buf, wworklist);
330fd64dc9aSMaxim Konovalov 		(void)fprintf(stderr, " done.\n");
331fd64dc9aSMaxim Konovalov 	}
332fd64dc9aSMaxim Konovalov }
333fd64dc9aSMaxim Konovalov 
334fd64dc9aSMaxim Konovalov /* Read the worklist if -r was given */
335fd64dc9aSMaxim Konovalov static off_t
read_worklist(off_t t)336fd64dc9aSMaxim Konovalov read_worklist(off_t t)
337fd64dc9aSMaxim Konovalov {
338fd64dc9aSMaxim Konovalov 	off_t s, l, d;
339fd64dc9aSMaxim Konovalov 	int state, lines;
340fd64dc9aSMaxim Konovalov 	FILE *file;
341fd64dc9aSMaxim Konovalov 
342fd64dc9aSMaxim Konovalov 	(void)fprintf(stderr, "Reading worklist ...");
3439546e08eSPoul-Henning Kamp 	(void)fflush(stderr);
344fd64dc9aSMaxim Konovalov 	file = fopen(rworklist, "r");
345fd64dc9aSMaxim Konovalov 	if (file == NULL)
346fd64dc9aSMaxim Konovalov 		err(1, "Error opening file %s", rworklist);
347fd64dc9aSMaxim Konovalov 
348fd64dc9aSMaxim Konovalov 	lines = 0;
349fd64dc9aSMaxim Konovalov 	d = t;
350fd64dc9aSMaxim Konovalov 	for (;;) {
351fd64dc9aSMaxim Konovalov 		++lines;
352fd64dc9aSMaxim Konovalov 		if (3 != fscanf(file, "%jd %jd %d\n", &s, &l, &state)) {
353fd64dc9aSMaxim Konovalov 			if (!feof(file))
354fd64dc9aSMaxim Konovalov 				err(1, "Error parsing file %s at line %d",
355fd64dc9aSMaxim Konovalov 				    rworklist, lines);
356fd64dc9aSMaxim Konovalov 			else
357fd64dc9aSMaxim Konovalov 				break;
358fd64dc9aSMaxim Konovalov 		}
359fd64dc9aSMaxim Konovalov 		new_lump(s, l, state);
360fd64dc9aSMaxim Konovalov 		d -= l;
361fd64dc9aSMaxim Konovalov 	}
3629546e08eSPoul-Henning Kamp 	if (fclose(file))
3639546e08eSPoul-Henning Kamp 		err(1, "Error closing file %s", rworklist);
364fd64dc9aSMaxim Konovalov 	(void)fprintf(stderr, " done.\n");
365fd64dc9aSMaxim Konovalov 	/*
366fd64dc9aSMaxim Konovalov 	 * Return the number of bytes already read
367fd64dc9aSMaxim Konovalov 	 * (at least not in worklist).
368fd64dc9aSMaxim Konovalov 	 */
369fd64dc9aSMaxim Konovalov 	return (d);
370fd64dc9aSMaxim Konovalov }
371fd64dc9aSMaxim Konovalov 
3729546e08eSPoul-Henning Kamp /**********************************************************************/
3739546e08eSPoul-Henning Kamp 
3749546e08eSPoul-Henning Kamp static void
write_buf(int fd,const void * buf,ssize_t len,off_t where)3759546e08eSPoul-Henning Kamp write_buf(int fd, const void *buf, ssize_t len, off_t where)
3769546e08eSPoul-Henning Kamp {
3779546e08eSPoul-Henning Kamp 	ssize_t i;
3789546e08eSPoul-Henning Kamp 
3799546e08eSPoul-Henning Kamp 	i = pwrite(fd, buf, len, where);
3809546e08eSPoul-Henning Kamp 	if (i == len)
3819546e08eSPoul-Henning Kamp 		return;
3829546e08eSPoul-Henning Kamp 
3839546e08eSPoul-Henning Kamp 	printf("\nWrite error at %jd/%zu\n\t%s\n",
3849546e08eSPoul-Henning Kamp 	    where, i, strerror(errno));
3859546e08eSPoul-Henning Kamp 	save_worklist();
3869546e08eSPoul-Henning Kamp 	if (write_errors_are_fatal)
3879546e08eSPoul-Henning Kamp 		exit(3);
3889546e08eSPoul-Henning Kamp }
3899546e08eSPoul-Henning Kamp 
3909546e08eSPoul-Henning Kamp static void
fill_buf(char * buf,ssize_t len,const char * pattern)3919546e08eSPoul-Henning Kamp fill_buf(char *buf, ssize_t len, const char *pattern)
3929546e08eSPoul-Henning Kamp {
3939546e08eSPoul-Henning Kamp 	ssize_t sz = strlen(pattern);
3949546e08eSPoul-Henning Kamp 	ssize_t i, j;
3959546e08eSPoul-Henning Kamp 
3969546e08eSPoul-Henning Kamp 	for (i = 0; i < len; i += sz) {
3979546e08eSPoul-Henning Kamp 		j = len - i;
3989546e08eSPoul-Henning Kamp 		if (j > sz)
3999546e08eSPoul-Henning Kamp 			j = sz;
4009546e08eSPoul-Henning Kamp 		memcpy(buf + i, pattern, j);
4019546e08eSPoul-Henning Kamp 	}
4029546e08eSPoul-Henning Kamp }
4039546e08eSPoul-Henning Kamp 
4049546e08eSPoul-Henning Kamp /**********************************************************************/
4059546e08eSPoul-Henning Kamp 
406fd64dc9aSMaxim Konovalov static void
usage(void)407fd64dc9aSMaxim Konovalov usage(void)
408fd64dc9aSMaxim Konovalov {
4095df69e92SUlrich Spörlein 	(void)fprintf(stderr, "usage: recoverdisk [-b bigsize] [-r readlist] "
4105df69e92SUlrich Spörlein 	    "[-s interval] [-w writelist] source [destination]\n");
4119546e08eSPoul-Henning Kamp 	/* XXX update */
412fd64dc9aSMaxim Konovalov 	exit(1);
413fd64dc9aSMaxim Konovalov }
414fd64dc9aSMaxim Konovalov 
415fd64dc9aSMaxim Konovalov static void
sighandler(__unused int sig)416fd64dc9aSMaxim Konovalov sighandler(__unused int sig)
417fd64dc9aSMaxim Konovalov {
418fd64dc9aSMaxim Konovalov 
419fd64dc9aSMaxim Konovalov 	aborting = 1;
420fd64dc9aSMaxim Konovalov }
421fd64dc9aSMaxim Konovalov 
422fd64dc9aSMaxim Konovalov int
main(int argc,char * const argv[])423fd64dc9aSMaxim Konovalov main(int argc, char * const argv[])
424fd64dc9aSMaxim Konovalov {
425fd64dc9aSMaxim Konovalov 	int ch;
4269546e08eSPoul-Henning Kamp 	size_t sz, j;
4279546e08eSPoul-Henning Kamp 	int error;
4289546e08eSPoul-Henning Kamp 	char *buf;
429fd64dc9aSMaxim Konovalov 	u_int sectorsize;
430d44ae92aSXin LI 	off_t stripesize;
4311f6ac570SPoul-Henning Kamp 	time_t t1, t2;
432db45c56dSMaxim Sobolev 	struct stat sb;
433584f5546SPoul-Henning Kamp 	u_int n, snapshot = 60;
4349546e08eSPoul-Henning Kamp 	static struct lump *lp;
43539d969aeSPoul-Henning Kamp 
4369546e08eSPoul-Henning Kamp 	while ((ch = getopt(argc, argv, "b:r:w:s:u:v")) != -1) {
437fd64dc9aSMaxim Konovalov 		switch (ch) {
438584f5546SPoul-Henning Kamp 		case 'b':
439584f5546SPoul-Henning Kamp 			bigsize = strtoul(optarg, NULL, 0);
440584f5546SPoul-Henning Kamp 			break;
441fd64dc9aSMaxim Konovalov 		case 'r':
442fd64dc9aSMaxim Konovalov 			rworklist = strdup(optarg);
443fd64dc9aSMaxim Konovalov 			if (rworklist == NULL)
444fd64dc9aSMaxim Konovalov 				err(1, "Cannot allocate enough memory");
445fd64dc9aSMaxim Konovalov 			break;
446584f5546SPoul-Henning Kamp 		case 's':
447584f5546SPoul-Henning Kamp 			snapshot = strtoul(optarg, NULL, 0);
448584f5546SPoul-Henning Kamp 			break;
4499546e08eSPoul-Henning Kamp 		case 'u':
4509546e08eSPoul-Henning Kamp 			unreadable_pattern = optarg;
4519546e08eSPoul-Henning Kamp 			break;
4529546e08eSPoul-Henning Kamp 		case 'v':
4539546e08eSPoul-Henning Kamp 			set_verbose();
4549546e08eSPoul-Henning Kamp 			break;
455fd64dc9aSMaxim Konovalov 		case 'w':
456fd64dc9aSMaxim Konovalov 			wworklist = strdup(optarg);
457fd64dc9aSMaxim Konovalov 			if (wworklist == NULL)
458fd64dc9aSMaxim Konovalov 				err(1, "Cannot allocate enough memory");
459fd64dc9aSMaxim Konovalov 			break;
460fd64dc9aSMaxim Konovalov 		default:
461fd64dc9aSMaxim Konovalov 			usage();
462fd64dc9aSMaxim Konovalov 			/* NOTREACHED */
463fd64dc9aSMaxim Konovalov 		}
464fd64dc9aSMaxim Konovalov 	}
465fd64dc9aSMaxim Konovalov 	argc -= optind;
466fd64dc9aSMaxim Konovalov 	argv += optind;
46739d969aeSPoul-Henning Kamp 
468fd64dc9aSMaxim Konovalov 	if (argc < 1 || argc > 2)
469fd64dc9aSMaxim Konovalov 		usage();
47039d969aeSPoul-Henning Kamp 
4719546e08eSPoul-Henning Kamp 	input = argv[0];
472fd64dc9aSMaxim Konovalov 	fdr = open(argv[0], O_RDONLY);
47339d969aeSPoul-Henning Kamp 	if (fdr < 0)
474fd64dc9aSMaxim Konovalov 		err(1, "Cannot open read descriptor %s", argv[0]);
475db45c56dSMaxim Sobolev 
476db45c56dSMaxim Sobolev 	error = fstat(fdr, &sb);
477db45c56dSMaxim Sobolev 	if (error < 0)
478db45c56dSMaxim Sobolev 		err(1, "fstat failed");
479db45c56dSMaxim Sobolev 	if (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode)) {
480db45c56dSMaxim Sobolev 		error = ioctl(fdr, DIOCGSECTORSIZE, &sectorsize);
481db45c56dSMaxim Sobolev 		if (error < 0)
482db45c56dSMaxim Sobolev 			err(1, "DIOCGSECTORSIZE failed");
483fd64dc9aSMaxim Konovalov 
484399fe479SXin LI 		error = ioctl(fdr, DIOCGSTRIPESIZE, &stripesize);
485*d830cac9SPoul-Henning Kamp 		if (error == 0 && stripesize < sectorsize)
486399fe479SXin LI 			sectorsize = stripesize;
487399fe479SXin LI 
488db45c56dSMaxim Sobolev 		minsize = sectorsize;
489f32d2926SPedro F. Giffuni 		bigsize = rounddown(bigsize, sectorsize);
490db45c56dSMaxim Sobolev 
4919546e08eSPoul-Henning Kamp 		error = ioctl(fdr, DIOCGMEDIASIZE, &tot_size);
492db45c56dSMaxim Sobolev 		if (error < 0)
493db45c56dSMaxim Sobolev 			err(1, "DIOCGMEDIASIZE failed");
494db45c56dSMaxim Sobolev 	} else {
4959546e08eSPoul-Henning Kamp 		tot_size = sb.st_size;
496db45c56dSMaxim Sobolev 	}
497db45c56dSMaxim Sobolev 
498584f5546SPoul-Henning Kamp 	if (bigsize < minsize)
499584f5546SPoul-Henning Kamp 		bigsize = minsize;
500584f5546SPoul-Henning Kamp 
501584f5546SPoul-Henning Kamp 	for (ch = 0; (bigsize >> ch) > minsize; ch++)
502584f5546SPoul-Henning Kamp 		continue;
503584f5546SPoul-Henning Kamp 	medsize = bigsize >> (ch / 2);
504f32d2926SPedro F. Giffuni 	medsize = rounddown(medsize, minsize);
505584f5546SPoul-Henning Kamp 
506ab5792a9SPoul-Henning Kamp 	fprintf(stderr, "Bigsize = %zu, medsize = %zu, minsize = %zu\n",
507584f5546SPoul-Henning Kamp 	    bigsize, medsize, minsize);
508584f5546SPoul-Henning Kamp 
509fd64dc9aSMaxim Konovalov 	buf = malloc(bigsize);
510fd64dc9aSMaxim Konovalov 	if (buf == NULL)
511d81c1141SEd Schouten 		err(1, "Cannot allocate %zu bytes buffer", bigsize);
51239d969aeSPoul-Henning Kamp 
513fd64dc9aSMaxim Konovalov 	if (argc > 1) {
5145df69e92SUlrich Spörlein 		fdw = open(argv[1], O_WRONLY | O_CREAT, DEFFILEMODE);
515fd64dc9aSMaxim Konovalov 		if (fdw < 0)
516fd64dc9aSMaxim Konovalov 			err(1, "Cannot open write descriptor %s", argv[1]);
5179546e08eSPoul-Henning Kamp 		if (ftruncate(fdw, tot_size) < 0)
5185df69e92SUlrich Spörlein 			err(1, "Cannot truncate output %s to %jd bytes",
5199546e08eSPoul-Henning Kamp 			    argv[1], (intmax_t)tot_size);
520fd64dc9aSMaxim Konovalov 	} else
521fd64dc9aSMaxim Konovalov 		fdw = -1;
522fd64dc9aSMaxim Konovalov 
523fd64dc9aSMaxim Konovalov 	if (rworklist != NULL) {
5249546e08eSPoul-Henning Kamp 		done_size = read_worklist(tot_size);
525fd64dc9aSMaxim Konovalov 	} else {
5269546e08eSPoul-Henning Kamp 		new_lump(0, tot_size, 0);
5279546e08eSPoul-Henning Kamp 		done_size = 0;
528fd64dc9aSMaxim Konovalov 	}
529fd64dc9aSMaxim Konovalov 	if (wworklist != NULL)
530fd64dc9aSMaxim Konovalov 		signal(SIGINT, sighandler);
53139d969aeSPoul-Henning Kamp 
5329546e08eSPoul-Henning Kamp 	t1 = time(NULL);
5339546e08eSPoul-Henning Kamp 	sz = 0;
5349546e08eSPoul-Henning Kamp 	if (!verbose)
5359546e08eSPoul-Henning Kamp 		report_header(0);
536ca9acde2SPoul-Henning Kamp 	else
537ca9acde2SPoul-Henning Kamp 		printf("\x1b[2J");
538584f5546SPoul-Henning Kamp 	n = 0;
53939d969aeSPoul-Henning Kamp 	for (;;) {
54039d969aeSPoul-Henning Kamp 		lp = TAILQ_FIRST(&lumps);
54139d969aeSPoul-Henning Kamp 		if (lp == NULL)
54239d969aeSPoul-Henning Kamp 			break;
5439546e08eSPoul-Henning Kamp 		while (lp->len > 0) {
544601c3cc0SPoul-Henning Kamp 
5459546e08eSPoul-Henning Kamp 			if (lp->state == 0)
5469546e08eSPoul-Henning Kamp 				sz = MIN(lp->len, (off_t)bigsize);
5479546e08eSPoul-Henning Kamp 			else if (lp->state == 1)
5489546e08eSPoul-Henning Kamp 				sz = MIN(lp->len, (off_t)medsize);
5499546e08eSPoul-Henning Kamp 			else
5509546e08eSPoul-Henning Kamp 				sz = MIN(lp->len, (off_t)minsize);
5519546e08eSPoul-Henning Kamp 			assert(sz != 0);
5529546e08eSPoul-Henning Kamp 
5539546e08eSPoul-Henning Kamp 			t2 = time(NULL);
55489d500c6SMatteo Riondato 			if (t1 != t2 || lp->len < (off_t)bigsize) {
5551f6ac570SPoul-Henning Kamp 				t1 = t2;
556584f5546SPoul-Henning Kamp 				if (++n == snapshot) {
557584f5546SPoul-Henning Kamp 					save_worklist();
558584f5546SPoul-Henning Kamp 					n = 0;
559584f5546SPoul-Henning Kamp 				}
5609546e08eSPoul-Henning Kamp 				report(lp, sz);
5611f6ac570SPoul-Henning Kamp 			}
5629546e08eSPoul-Henning Kamp 
5639546e08eSPoul-Henning Kamp 			j = pread(fdr, buf, sz, lp->start);
5649546e08eSPoul-Henning Kamp #if 0
5659546e08eSPoul-Henning Kamp if (!(random() & 0xf)) {
5669546e08eSPoul-Henning Kamp 	j = -1;
5679546e08eSPoul-Henning Kamp 	errno = EIO;
56839d969aeSPoul-Henning Kamp }
5699546e08eSPoul-Henning Kamp #endif
5709546e08eSPoul-Henning Kamp 			if (j == sz) {
5719546e08eSPoul-Henning Kamp 				done_size += sz;
57239d969aeSPoul-Henning Kamp 				if (fdw >= 0)
5739546e08eSPoul-Henning Kamp 					write_buf(fdw, buf, sz, lp->start);
5749546e08eSPoul-Henning Kamp 				lp->start += sz;
5759546e08eSPoul-Henning Kamp 				lp->len -= sz;
5769546e08eSPoul-Henning Kamp 				if (verbose && lp->state > 2)
5779546e08eSPoul-Henning Kamp 					report_good_read(t2, sz);
57839d969aeSPoul-Henning Kamp 				continue;
57939d969aeSPoul-Henning Kamp 			}
5809546e08eSPoul-Henning Kamp 			error = errno;
5819546e08eSPoul-Henning Kamp 
5829546e08eSPoul-Henning Kamp 			printf("%jd %zu %d read error (%s)\n",
5839546e08eSPoul-Henning Kamp 			    lp->start, sz, lp->state, strerror(error));
5849546e08eSPoul-Henning Kamp 			if (verbose)
5859546e08eSPoul-Henning Kamp 				report(lp, sz);
586d21ce74dSPoul-Henning Kamp 			if (fdw >= 0 && strlen(unreadable_pattern)) {
587d21ce74dSPoul-Henning Kamp 				fill_buf(buf, sz, unreadable_pattern);
588d21ce74dSPoul-Henning Kamp 				write_buf(fdw, buf, sz, lp->start);
589d21ce74dSPoul-Henning Kamp 			}
590d21ce74dSPoul-Henning Kamp 			new_lump(lp->start, sz, lp->state + 1);
591d21ce74dSPoul-Henning Kamp 			lp->start += sz;
592d21ce74dSPoul-Henning Kamp 			lp->len -= sz;
5939546e08eSPoul-Henning Kamp 			if (error == EINVAL) {
5949546e08eSPoul-Henning Kamp 				printf("Try with -b 131072 or lower ?\n");
5955df69e92SUlrich Spörlein 				aborting = 1;
5969546e08eSPoul-Henning Kamp 				break;
5975df69e92SUlrich Spörlein 			}
5989546e08eSPoul-Henning Kamp 			if (error == ENXIO) {
5999546e08eSPoul-Henning Kamp 				printf("Input device probably detached...\n");
600972b3f30SPoul-Henning Kamp 				aborting = 1;
6019546e08eSPoul-Henning Kamp 				break;
60239d969aeSPoul-Henning Kamp 			}
6039546e08eSPoul-Henning Kamp 		}
6049546e08eSPoul-Henning Kamp 		if (aborting)
605fd64dc9aSMaxim Konovalov 			save_worklist();
6069546e08eSPoul-Henning Kamp 		if (aborting || !TAILQ_NEXT(lp, list))
6079546e08eSPoul-Henning Kamp 			report(lp, sz);
6089546e08eSPoul-Henning Kamp 		if (aborting)
6099546e08eSPoul-Henning Kamp 			break;
6109546e08eSPoul-Henning Kamp 		assert(lp->len == 0);
611fd64dc9aSMaxim Konovalov 		TAILQ_REMOVE(&lumps, lp, list);
61239d969aeSPoul-Henning Kamp 		free(lp);
61339d969aeSPoul-Henning Kamp 	}
6149546e08eSPoul-Henning Kamp 	printf("%s", aborting ? "Aborted\n" : "Completed\n");
6159546e08eSPoul-Henning Kamp 	free(buf);
616fd64dc9aSMaxim Konovalov 	return (0);
61739d969aeSPoul-Henning Kamp }
618