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, &sectorsize);
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