recoverdisk.c revision 189691
1234219Sadrian/*-
2234219Sadrian * ----------------------------------------------------------------------------
3234219Sadrian * "THE BEER-WARE LICENSE" (Revision 42):
4234219Sadrian * <phk@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
5234219Sadrian * can do whatever you want with this stuff. If we meet some day, and you think
6234219Sadrian * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
7234219Sadrian * ----------------------------------------------------------------------------
8234219Sadrian *
9234219Sadrian * $FreeBSD: head/sbin/recoverdisk/recoverdisk.c 189691 2009-03-11 10:37:02Z phk $
10234219Sadrian */
11234219Sadrian#include <sys/param.h>
12234219Sadrian#include <sys/queue.h>
13234219Sadrian#include <sys/disk.h>
14234219Sadrian#include <sys/stat.h>
15234219Sadrian
16291049Simp#include <err.h>
17291049Simp#include <errno.h>
18234219Sadrian#include <fcntl.h>
19234219Sadrian#include <signal.h>
20234219Sadrian#include <stdint.h>
21234219Sadrian#include <stdio.h>
22263301Simp#include <stdlib.h>
23234219Sadrian#include <string.h>
24263301Simp#include <time.h>
25234220Sadrian#include <unistd.h>
26234235Sadrian
27234235Sadrianvolatile sig_atomic_t aborting = 0;
28234219Sadrianstatic size_t bigsize = 1024 * 1024;
29234235Sadrianstatic size_t medsize;
30345344Skibstatic size_t minsize = 512;
31295945Ssobomax
32295945Ssobomaxstruct lump {
33234235Sadrian	off_t			start;
34234235Sadrian	off_t			len;
35234235Sadrian	int			state;
36234235Sadrian	TAILQ_ENTRY(lump)	list;
37234235Sadrian};
38234232Sadrian
39234232Sadrianstatic TAILQ_HEAD(, lump) lumps = TAILQ_HEAD_INITIALIZER(lumps);
40234232Sadrian
41234863Sadrianstatic void
42234863Sadriannew_lump(off_t start, off_t len, int state)
43234863Sadrian{
44234863Sadrian	struct lump *lp;
45235290Sadrian
46235290Sadrian	lp = malloc(sizeof *lp);
47235290Sadrian	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
55static struct lump *lp;
56static char *wworklist = NULL;
57static char *rworklist = NULL;
58
59
60#define PRINT_HEADER \
61	printf("%13s %7s %13s %5s %13s %13s %9s\n", \
62		"start", "size", "block-len", "state", "done", "remaining", "% done")
63
64#define PRINT_STATUS(start, i, len, state, d, t) \
65	printf("\r%13jd %7zu %13jd %5d %13jd %13jd %9.5f", \
66		(intmax_t)start, \
67		i,  \
68		(intmax_t)len, \
69		state, \
70		(intmax_t)d, \
71		(intmax_t)(t - d), \
72		100*(double)d/(double)t)
73
74/* Save the worklist if -w was given */
75static void
76save_worklist(void)
77{
78	FILE *file;
79	struct lump *llp;
80
81	if (wworklist != NULL) {
82		(void)fprintf(stderr, "\nSaving worklist ...");
83		fflush(stderr);
84
85		file = fopen(wworklist, "w");
86		if (file == NULL)
87			err(1, "Error opening file %s", wworklist);
88
89		TAILQ_FOREACH(llp, &lumps, list)
90			fprintf(file, "%jd %jd %d\n",
91			    (intmax_t)llp->start, (intmax_t)llp->len,
92			    llp->state);
93		fclose(file);
94		(void)fprintf(stderr, " done.\n");
95	}
96}
97
98/* Read the worklist if -r was given */
99static off_t
100read_worklist(off_t t)
101{
102	off_t s, l, d;
103	int state, lines;
104	FILE *file;
105
106	(void)fprintf(stderr, "Reading worklist ...");
107	fflush(stderr);
108	file = fopen(rworklist, "r");
109	if (file == NULL)
110		err(1, "Error opening file %s", rworklist);
111
112	lines = 0;
113	d = t;
114	for (;;) {
115		++lines;
116		if (3 != fscanf(file, "%jd %jd %d\n", &s, &l, &state)) {
117			if (!feof(file))
118				err(1, "Error parsing file %s at line %d",
119				    rworklist, lines);
120			else
121				break;
122		}
123		new_lump(s, l, state);
124		d -= l;
125	}
126	(void)fprintf(stderr, " done.\n");
127	/*
128	 * Return the number of bytes already read
129	 * (at least not in worklist).
130	 */
131	return (d);
132}
133
134static void
135usage(void)
136{
137	(void)fprintf(stderr,
138    "usage: recoverdisk [-r worklist] [-w worklist] source-drive [destination]\n");
139	exit(1);
140}
141
142static void
143sighandler(__unused int sig)
144{
145
146	aborting = 1;
147}
148
149int
150main(int argc, char * const argv[])
151{
152	int ch;
153	int fdr, fdw;
154	off_t t, d, start, len;
155	size_t i, j;
156	int error, flags, state;
157	u_char *buf;
158	u_int sectorsize;
159	time_t t1, t2;
160	struct stat sb;
161	u_int n, snapshot = 60;
162
163	while ((ch = getopt(argc, argv, "b:r:w:s:")) != -1) {
164		switch (ch) {
165		case 'b':
166			bigsize = strtoul(optarg, NULL, 0);
167			break;
168		case 'r':
169			rworklist = strdup(optarg);
170			if (rworklist == NULL)
171				err(1, "Cannot allocate enough memory");
172			break;
173		case 's':
174			snapshot = strtoul(optarg, NULL, 0);
175			break;
176		case 'w':
177			wworklist = strdup(optarg);
178			if (wworklist == NULL)
179				err(1, "Cannot allocate enough memory");
180			break;
181		default:
182			usage();
183			/* NOTREACHED */
184		}
185	}
186	argc -= optind;
187	argv += optind;
188
189	if (argc < 1 || argc > 2)
190		usage();
191
192	fdr = open(argv[0], O_RDONLY);
193	if (fdr < 0)
194		err(1, "Cannot open read descriptor %s", argv[0]);
195
196	error = fstat(fdr, &sb);
197	if (error < 0)
198		err(1, "fstat failed");
199	flags = O_WRONLY;
200	if (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode)) {
201		error = ioctl(fdr, DIOCGSECTORSIZE, &sectorsize);
202		if (error < 0)
203			err(1, "DIOCGSECTORSIZE failed");
204
205		minsize = sectorsize;
206		bigsize = (bigsize / sectorsize) * sectorsize;
207
208		error = ioctl(fdr, DIOCGMEDIASIZE, &t);
209		if (error < 0)
210			err(1, "DIOCGMEDIASIZE failed");
211	} else {
212		t = sb.st_size;
213		flags |= O_CREAT | O_TRUNC;
214	}
215
216	if (bigsize < minsize)
217		bigsize = minsize;
218
219	for (ch = 0; (bigsize >> ch) > minsize; ch++)
220		continue;
221	medsize = bigsize >> (ch / 2);
222	medsize = (medsize / minsize) * minsize;
223
224	fprintf(stderr, "Bigsize = %u, medsize = %u, minsize = %u\n",
225	    bigsize, medsize, minsize);
226
227	buf = malloc(bigsize);
228	if (buf == NULL)
229		err(1, "Cannot allocate %jd bytes buffer", (intmax_t)bigsize);
230
231	if (argc > 1) {
232		fdw = open(argv[1], flags, DEFFILEMODE);
233		if (fdw < 0)
234			err(1, "Cannot open write descriptor %s", argv[1]);
235	} else
236		fdw = -1;
237
238	if (rworklist != NULL) {
239		d = read_worklist(t);
240	} else {
241		new_lump(0, t, 0);
242		d = 0;
243	}
244	if (wworklist != NULL)
245		signal(SIGINT, sighandler);
246
247	t1 = 0;
248	start = len = i = state = 0;
249	PRINT_HEADER;
250	n = 0;
251	for (;;) {
252		lp = TAILQ_FIRST(&lumps);
253		if (lp == NULL)
254			break;
255		while (lp->len > 0 && !aborting) {
256			/* These are only copied for printing stats */
257			start = lp->start;
258			len = lp->len;
259			state = lp->state;
260
261			i = MIN(lp->len, (off_t)bigsize);
262			if (lp->state == 1)
263				i = MIN(lp->len, (off_t)medsize);
264			if (lp->state > 1)
265				i = MIN(lp->len, (off_t)minsize);
266			time(&t2);
267			if (t1 != t2 || lp->len < (off_t)bigsize) {
268				PRINT_STATUS(start, i, len, state, d, t);
269				t1 = t2;
270				if (++n == snapshot) {
271					save_worklist();
272					n = 0;
273				}
274			}
275			if (i == 0) {
276				errx(1, "BOGUS i %10jd", (intmax_t)i);
277			}
278			fflush(stdout);
279			j = pread(fdr, buf, i, lp->start);
280			if (j == i) {
281				d += i;
282				if (fdw >= 0)
283					j = pwrite(fdw, buf, i, lp->start);
284				else
285					j = i;
286				if (j != i)
287					printf("\nWrite error at %jd/%zu\n",
288					    lp->start, i);
289				lp->start += i;
290				lp->len -= i;
291				continue;
292			}
293			printf("\n%jd %zu failed (%s)\n",
294			    lp->start, i, strerror(errno));
295			if (errno == ENXIO)
296				aborting = 1;
297			new_lump(lp->start, i, lp->state + 1);
298			lp->start += i;
299			lp->len -= i;
300		}
301		if (aborting) {
302			save_worklist();
303			return (0);
304		}
305		TAILQ_REMOVE(&lumps, lp, list);
306		free(lp);
307	}
308	PRINT_STATUS(start, i, len, state, d, t);
309	printf("\nCompleted\n");
310	return (0);
311}
312