recoverdisk.c revision 159076
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: head/sbin/recoverdisk/recoverdisk.c 159076 2006-05-30 19:10:18Z matteo $
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
27158337Smaximvolatile sig_atomic_t aborting = 0;
28158337Smaximstatic size_t bigsize = 1024 * 1024;
29158337Smaximstatic size_t medsize = 64 * 1024;
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
59158337Smaxim/* Save the worklist if -w was given */
60158337Smaximstatic void
61158337Smaximsave_worklist(void)
62158337Smaxim{
63158337Smaxim	FILE *file;
64158337Smaxim
65158337Smaxim	if (wworklist != NULL) {
66158337Smaxim		(void)fprintf(stderr, "\nSaving worklist ...");
67158337Smaxim		fflush(stderr);
68158337Smaxim
69158337Smaxim		file = fopen(wworklist, "w");
70158337Smaxim		if (file == NULL)
71158337Smaxim			err(1, "Error opening file %s", wworklist);
72158337Smaxim
73158337Smaxim		for (;;) {
74158337Smaxim			lp = TAILQ_FIRST(&lumps);
75158337Smaxim			if (lp == NULL)
76158337Smaxim				break;
77158337Smaxim			fprintf(file, "%jd %jd %d\n",
78158337Smaxim			    (intmax_t)lp->start, (intmax_t)lp->len, lp->state);
79158337Smaxim			TAILQ_REMOVE(&lumps, lp, list);
80158337Smaxim		}
81158337Smaxim		(void)fprintf(stderr, " done.\n");
82158337Smaxim	}
83158337Smaxim}
84158337Smaxim
85158337Smaxim/* Read the worklist if -r was given */
86158337Smaximstatic off_t
87158337Smaximread_worklist(off_t t)
88158337Smaxim{
89158337Smaxim	off_t s, l, d;
90158337Smaxim	int state, lines;
91158337Smaxim	FILE *file;
92158337Smaxim
93158337Smaxim	(void)fprintf(stderr, "Reading worklist ...");
94158337Smaxim	fflush(stderr);
95158337Smaxim	file = fopen(rworklist, "r");
96158337Smaxim	if (file == NULL)
97158337Smaxim		err(1, "Error opening file %s", rworklist);
98158337Smaxim
99158337Smaxim	lines = 0;
100158337Smaxim	d = t;
101158337Smaxim	for (;;) {
102158337Smaxim		++lines;
103158337Smaxim		if (3 != fscanf(file, "%jd %jd %d\n", &s, &l, &state)) {
104158337Smaxim			if (!feof(file))
105158337Smaxim				err(1, "Error parsing file %s at line %d",
106158337Smaxim				    rworklist, lines);
107158337Smaxim			else
108158337Smaxim				break;
109158337Smaxim		}
110158337Smaxim		new_lump(s, l, state);
111158337Smaxim		d -= l;
112158337Smaxim	}
113158337Smaxim	(void)fprintf(stderr, " done.\n");
114158337Smaxim	/*
115158337Smaxim	 * Return the number of bytes already read
116158337Smaxim	 * (at least not in worklist).
117158337Smaxim	 */
118158337Smaxim	return (d);
119158337Smaxim}
120158337Smaxim
121158337Smaximstatic void
122158337Smaximusage(void)
123158337Smaxim{
124158337Smaxim	(void)fprintf(stderr,
125158337Smaxim    "usage: recoverdisk [-r worklist] [-w worklist] source-drive [destination]\n");
126158337Smaxim	exit(1);
127158337Smaxim}
128158337Smaxim
129158337Smaximstatic void
130158337Smaximsighandler(__unused int sig)
131158337Smaxim{
132158337Smaxim
133158337Smaxim	aborting = 1;
134158337Smaxim}
135158337Smaxim
136135911Sphkint
137158337Smaximmain(int argc, char * const argv[])
138135911Sphk{
139158337Smaxim	int ch;
140135911Sphk	int fdr, fdw;
141158337Smaxim	off_t t, d;
142135911Sphk	size_t i, j;
143149605Ssobomax	int error, flags;
144135911Sphk	u_char *buf;
145158337Smaxim	u_int sectorsize;
146136257Sphk	time_t t1, t2;
147149605Ssobomax	struct stat sb;
148135911Sphk
149158337Smaxim	while ((ch = getopt(argc, argv, "r:w:")) != -1) {
150158337Smaxim		switch (ch) {
151158337Smaxim		case 'r':
152158337Smaxim			rworklist = strdup(optarg);
153158337Smaxim			if (rworklist == NULL)
154158337Smaxim				err(1, "Cannot allocate enough memory");
155158337Smaxim			break;
156158337Smaxim		case 'w':
157158337Smaxim			wworklist = strdup(optarg);
158158337Smaxim			if (wworklist == NULL)
159158337Smaxim				err(1, "Cannot allocate enough memory");
160158337Smaxim			break;
161158337Smaxim		default:
162158337Smaxim			usage();
163158337Smaxim			/* NOTREACHED */
164158337Smaxim		}
165158337Smaxim	}
166158337Smaxim	argc -= optind;
167158337Smaxim	argv += optind;
168135911Sphk
169158337Smaxim	if (argc < 1 || argc > 2)
170158337Smaxim		usage();
171135911Sphk
172158337Smaxim	fdr = open(argv[0], O_RDONLY);
173135911Sphk	if (fdr < 0)
174158337Smaxim		err(1, "Cannot open read descriptor %s", argv[0]);
175149605Ssobomax
176149605Ssobomax	error = fstat(fdr, &sb);
177149605Ssobomax	if (error < 0)
178149605Ssobomax		err(1, "fstat failed");
179149605Ssobomax	flags = O_WRONLY;
180149605Ssobomax	if (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode)) {
181149605Ssobomax		error = ioctl(fdr, DIOCGSECTORSIZE, &sectorsize);
182149605Ssobomax		if (error < 0)
183149605Ssobomax			err(1, "DIOCGSECTORSIZE failed");
184158337Smaxim
185158337Smaxim		/*
186158337Smaxim		 * Make medsize roughly 64kB, depending on native sector
187158337Smaxim		 * size. bigsize has to be a multiple of medsize.
188158337Smaxim		 * For media with 2352 sectors, this will
189158337Smaxim		 * result in 2352, 63504, and 1016064 bytes.
190158337Smaxim		 */
191149605Ssobomax		minsize = sectorsize;
192158337Smaxim		medsize = (medsize / sectorsize) * sectorsize;
193158337Smaxim		bigsize = medsize * 16;
194149605Ssobomax
195149605Ssobomax		error = ioctl(fdr, DIOCGMEDIASIZE, &t);
196149605Ssobomax		if (error < 0)
197149605Ssobomax			err(1, "DIOCGMEDIASIZE failed");
198149605Ssobomax	} else {
199149605Ssobomax		t = sb.st_size;
200149605Ssobomax		flags |= O_CREAT | O_TRUNC;
201149605Ssobomax	}
202149605Ssobomax
203158337Smaxim	buf = malloc(bigsize);
204158337Smaxim	if (buf == NULL)
205158337Smaxim		err(1, "Cannot allocate %jd bytes buffer", (intmax_t)bigsize);
206158337Smaxim
207158337Smaxim	if (argc > 1) {
208158337Smaxim		fdw = open(argv[1], flags, DEFFILEMODE);
209135911Sphk		if (fdw < 0)
210158337Smaxim			err(1, "Cannot open write descriptor %s", argv[1]);
211158337Smaxim	} else
212158337Smaxim		fdw = -1;
213158337Smaxim
214158337Smaxim	if (rworklist != NULL) {
215158337Smaxim		d = read_worklist(t);
216135911Sphk	} else {
217158337Smaxim		new_lump(0, t, 0);
218158337Smaxim		d = 0;
219135911Sphk	}
220158337Smaxim	if (wworklist != NULL)
221158337Smaxim		signal(SIGINT, sighandler);
222135911Sphk
223136257Sphk	t1 = 0;
224158337Smaxim	printf("%13s %7s %13s %5s %13s %13s %9s\n",
225158337Smaxim	    "start", "size", "len", "state", "done", "remaining", "% done");
226135911Sphk	for (;;) {
227135911Sphk		lp = TAILQ_FIRST(&lumps);
228135911Sphk		if (lp == NULL)
229135911Sphk			break;
230158337Smaxim		while (lp->len > 0 && !aborting) {
231159076Smatteo			i = MIN(lp->len, (off_t)bigsize);
232135911Sphk			if (lp->state == 1)
233159076Smatteo				i = MIN(lp->len, (off_t)medsize);
234135911Sphk			if (lp->state > 1)
235159076Smatteo				i = MIN(lp->len, (off_t)minsize);
236136257Sphk			time(&t2);
237159076Smatteo			if (t1 != t2 || lp->len < (off_t)bigsize) {
238158337Smaxim				printf("\r%13jd %7zu %13jd %5d %13jd %13jd %.7f",
239136257Sphk				    (intmax_t)lp->start,
240136815Sdes				    i,
241136257Sphk				    (intmax_t)lp->len,
242136257Sphk				    lp->state,
243136257Sphk				    (intmax_t)d,
244136257Sphk				    (intmax_t)(t - d),
245136257Sphk				    (double)d/(double)t);
246136257Sphk				t1 = t2;
247136257Sphk			}
248135911Sphk			if (i == 0) {
249135911Sphk				errx(1, "BOGUS i %10jd", (intmax_t)i);
250135911Sphk			}
251135911Sphk			fflush(stdout);
252135911Sphk			j = pread(fdr, buf, i, lp->start);
253135911Sphk			if (j == i) {
254135911Sphk				d += i;
255135911Sphk				if (fdw >= 0)
256135911Sphk					j = pwrite(fdw, buf, i, lp->start);
257135911Sphk				else
258135911Sphk					j = i;
259135911Sphk				if (j != i)
260136815Sdes					printf("\nWrite error at %jd/%zu\n",
261136815Sdes					    lp->start, i);
262135911Sphk				lp->start += i;
263135911Sphk				lp->len -= i;
264135911Sphk				continue;
265135911Sphk			}
266136815Sdes			printf("\n%jd %zu failed %d\n", lp->start, i, errno);
267135911Sphk			new_lump(lp->start, i, lp->state + 1);
268135911Sphk			lp->start += i;
269135911Sphk			lp->len -= i;
270135911Sphk		}
271158337Smaxim		if (aborting) {
272158337Smaxim			save_worklist();
273158337Smaxim			return (0);
274158337Smaxim		}
275158337Smaxim		TAILQ_REMOVE(&lumps, lp, list);
276135911Sphk		free(lp);
277135911Sphk	}
278135911Sphk	printf("\nCompleted\n");
279158337Smaxim	return (0);
280135911Sphk}
281