recoverdisk.c revision 170888
1220150Smm/*-
2 * ----------------------------------------------------------------------------
3 * "THE BEER-WARE LICENSE" (Revision 42):
4 * <phk@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
5 * can do whatever you want with this stuff. If we meet some day, and you think
6 * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
7 * ----------------------------------------------------------------------------
8 *
9 * $FreeBSD: head/sbin/recoverdisk/recoverdisk.c 168972 2007-04-23 12:17:27Z phk $
10 */
11#include <sys/param.h>
12#include <sys/queue.h>
13#include <sys/disk.h>
14#include <sys/stat.h>
15
16#include <err.h>
17#include <errno.h>
18#include <fcntl.h>
19#include <signal.h>
20#include <stdint.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <time.h>
25#include <unistd.h>
26
27volatile sig_atomic_t aborting = 0;
28static size_t bigsize = 1024 * 1024;
29static size_t medsize = 64 * 1024;
30static size_t minsize = 512;
31
32struct lump {
33	off_t			start;
34	off_t			len;
35	int			state;
36	TAILQ_ENTRY(lump)	list;
37};
38
39static TAILQ_HEAD(, lump) lumps = TAILQ_HEAD_INITIALIZER(lumps);
40
41static void
42new_lump(off_t start, off_t len, int state)
43{
44	struct lump *lp;
45
46	lp = malloc(sizeof *lp);
47	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
80	if (wworklist != NULL) {
81		(void)fprintf(stderr, "\nSaving worklist ...");
82		fflush(stderr);
83
84		file = fopen(wworklist, "w");
85		if (file == NULL)
86			err(1, "Error opening file %s", wworklist);
87
88		for (;;) {
89			lp = TAILQ_FIRST(&lumps);
90			if (lp == NULL)
91				break;
92			fprintf(file, "%jd %jd %d\n",
93			    (intmax_t)lp->start, (intmax_t)lp->len, lp->state);
94			TAILQ_REMOVE(&lumps, lp, list);
95		}
96		(void)fprintf(stderr, " done.\n");
97	}
98}
99
100/* Read the worklist if -r was given */
101static off_t
102read_worklist(off_t t)
103{
104	off_t s, l, d;
105	int state, lines;
106	FILE *file;
107
108	(void)fprintf(stderr, "Reading worklist ...");
109	fflush(stderr);
110	file = fopen(rworklist, "r");
111	if (file == NULL)
112		err(1, "Error opening file %s", rworklist);
113
114	lines = 0;
115	d = t;
116	for (;;) {
117		++lines;
118		if (3 != fscanf(file, "%jd %jd %d\n", &s, &l, &state)) {
119			if (!feof(file))
120				err(1, "Error parsing file %s at line %d",
121				    rworklist, lines);
122			else
123				break;
124		}
125		new_lump(s, l, state);
126		d -= l;
127	}
128	(void)fprintf(stderr, " done.\n");
129	/*
130	 * Return the number of bytes already read
131	 * (at least not in worklist).
132	 */
133	return (d);
134}
135
136static void
137usage(void)
138{
139	(void)fprintf(stderr,
140    "usage: recoverdisk [-r worklist] [-w worklist] source-drive [destination]\n");
141	exit(1);
142}
143
144static void
145sighandler(__unused int sig)
146{
147
148	aborting = 1;
149}
150
151int
152main(int argc, char * const argv[])
153{
154	int ch;
155	int fdr, fdw;
156	off_t t, d, start, len;
157	size_t i, j;
158	int error, flags, state;
159	u_char *buf;
160	u_int sectorsize;
161	time_t t1, t2;
162	struct stat sb;
163
164	while ((ch = getopt(argc, argv, "r:w:")) != -1) {
165		switch (ch) {
166		case 'r':
167			rworklist = strdup(optarg);
168			if (rworklist == NULL)
169				err(1, "Cannot allocate enough memory");
170			break;
171		case 'w':
172			wworklist = strdup(optarg);
173			if (wworklist == NULL)
174				err(1, "Cannot allocate enough memory");
175			break;
176		default:
177			usage();
178			/* NOTREACHED */
179		}
180	}
181	argc -= optind;
182	argv += optind;
183
184	if (argc < 1 || argc > 2)
185		usage();
186
187	fdr = open(argv[0], O_RDONLY);
188	if (fdr < 0)
189		err(1, "Cannot open read descriptor %s", argv[0]);
190
191	error = fstat(fdr, &sb);
192	if (error < 0)
193		err(1, "fstat failed");
194	flags = O_WRONLY;
195	if (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode)) {
196		error = ioctl(fdr, DIOCGSECTORSIZE, &sectorsize);
197		if (error < 0)
198			err(1, "DIOCGSECTORSIZE failed");
199
200		/*
201		 * Make medsize roughly 64kB, depending on native sector
202		 * size. bigsize has to be a multiple of medsize.
203		 * For media with 2352 sectors, this will
204		 * result in 2352, 63504, and 1016064 bytes.
205		 */
206		minsize = sectorsize;
207		medsize = (medsize / sectorsize) * sectorsize;
208		bigsize = medsize * 16;
209
210		error = ioctl(fdr, DIOCGMEDIASIZE, &t);
211		if (error < 0)
212			err(1, "DIOCGMEDIASIZE failed");
213	} else {
214		t = sb.st_size;
215		flags |= O_CREAT | O_TRUNC;
216	}
217
218	buf = malloc(bigsize);
219	if (buf == NULL)
220		err(1, "Cannot allocate %jd bytes buffer", (intmax_t)bigsize);
221
222	if (argc > 1) {
223		fdw = open(argv[1], flags, DEFFILEMODE);
224		if (fdw < 0)
225			err(1, "Cannot open write descriptor %s", argv[1]);
226	} else
227		fdw = -1;
228
229	if (rworklist != NULL) {
230		d = read_worklist(t);
231	} else {
232		new_lump(0, t, 0);
233		d = 0;
234	}
235	if (wworklist != NULL)
236		signal(SIGINT, sighandler);
237
238	t1 = 0;
239	start = len = i = state = 0;
240	PRINT_HEADER;
241	for (;;) {
242		lp = TAILQ_FIRST(&lumps);
243		if (lp == NULL)
244			break;
245		while (lp->len > 0 && !aborting) {
246			/* These are only copied for printing stats */
247			start = lp->start;
248			len = lp->len;
249			state = lp->state;
250
251			i = MIN(lp->len, (off_t)bigsize);
252			if (lp->state == 1)
253				i = MIN(lp->len, (off_t)medsize);
254			if (lp->state > 1)
255				i = MIN(lp->len, (off_t)minsize);
256			time(&t2);
257			if (t1 != t2 || lp->len < (off_t)bigsize) {
258				PRINT_STATUS(start, i, len, state, d, t);
259				t1 = t2;
260			}
261			if (i == 0) {
262				errx(1, "BOGUS i %10jd", (intmax_t)i);
263			}
264			fflush(stdout);
265			j = pread(fdr, buf, i, lp->start);
266			if (j == i) {
267				d += i;
268				if (fdw >= 0)
269					j = pwrite(fdw, buf, i, lp->start);
270				else
271					j = i;
272				if (j != i)
273					printf("\nWrite error at %jd/%zu\n",
274					    lp->start, i);
275				lp->start += i;
276				lp->len -= i;
277				continue;
278			}
279			printf("\n%jd %zu failed %d\n", lp->start, i, errno);
280			new_lump(lp->start, i, lp->state + 1);
281			lp->start += i;
282			lp->len -= i;
283		}
284		if (aborting) {
285			save_worklist();
286			return (0);
287		}
288		TAILQ_REMOVE(&lumps, lp, list);
289		free(lp);
290	}
291	PRINT_STATUS(start, i, len, state, d, t);
292	printf("\nCompleted\n");
293	return (0);
294}
295