xref: /freebsd/sbin/recoverdisk/recoverdisk.c (revision 6af83ee0d2941d18880b6aaa2b4facd1d30c6106)
1 /*
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$
10  */
11 #include <stdio.h>
12 #include <stdint.h>
13 #include <stdlib.h>
14 #include <err.h>
15 #include <errno.h>
16 #include <fcntl.h>
17 #include <time.h>
18 #include <unistd.h>
19 #include <sys/queue.h>
20 #include <sys/disk.h>
21 
22 #define BIGSIZE		(1024 * 1024)
23 #define MEDIUMSIZE	(64 * 1024)
24 
25 struct lump {
26 	off_t			start;
27 	off_t			len;
28 	int			state;
29 	TAILQ_ENTRY(lump)	list;
30 };
31 
32 static TAILQ_HEAD(, lump) lumps = TAILQ_HEAD_INITIALIZER(lumps);
33 
34 
35 static void
36 new_lump(off_t start, off_t len, int state)
37 {
38 	struct lump *lp;
39 
40 	lp = malloc(sizeof *lp);
41 	if (lp == NULL)
42 		err(1, "Malloc failed");
43 	lp->start = start;
44 	lp->len = len;
45 	lp->state = state;
46 	TAILQ_INSERT_TAIL(&lumps, lp, list);
47 }
48 
49 int
50 main(int argc, const char **argv)
51 {
52 	int fdr, fdw;
53 	struct lump *lp;
54 	off_t 	t, d;
55 	size_t i, j;
56 	int error;
57 	u_char *buf;
58 	u_int sectorsize;
59 	time_t t1, t2;
60 
61 
62 	if (argc < 2)
63 		errx(1, "Usage: %s source-drive [destination]", argv[0]);
64 
65 	buf = malloc(BIGSIZE);
66 	if (buf == NULL)
67 		err(1, "Cannot allocate %d bytes buffer", BIGSIZE);
68 	fdr = open(argv[1], O_RDONLY);
69 	if (fdr < 0)
70 		err(1, "Cannot open read descriptor %s", argv[1]);
71 	if (argc > 2) {
72 		fdw = open(argv[2], O_WRONLY);
73 		if (fdw < 0)
74 			err(1, "Cannot open write descriptor %s", argv[2]);
75 	} else {
76 		fdw = -1;
77 	}
78 
79 	error = ioctl(fdr, DIOCGSECTORSIZE, &sectorsize);
80 	if (error < 0)
81 		err(1, "DIOCGSECTORSIZE failed");
82 
83 	error = ioctl(fdr, DIOCGMEDIASIZE, &t);
84 	if (error < 0)
85 		err(1, "DIOCGMEDIASIZE failed");
86 
87 	new_lump(0, t, 0);
88 	d = 0;
89 
90 	t1 = 0;
91 	for (;;) {
92 		lp = TAILQ_FIRST(&lumps);
93 		if (lp == NULL)
94 			break;
95 		TAILQ_REMOVE(&lumps, lp, list);
96 		while (lp->len > 0) {
97 			i = BIGSIZE;
98 			if (lp->len < BIGSIZE)
99 				i = lp->len;
100 			if (lp->state == 1)
101 				i = MEDIUMSIZE;
102 			if (lp->state > 1)
103 				i = sectorsize;
104 			time(&t2);
105 			if (t1 != t2 || lp->len < BIGSIZE) {
106 				printf("\r%13jd %7zu %13jd %3d %13jd %13jd %.8f",
107 				    (intmax_t)lp->start,
108 				    i,
109 				    (intmax_t)lp->len,
110 				    lp->state,
111 				    (intmax_t)d,
112 				    (intmax_t)(t - d),
113 				    (double)d/(double)t);
114 				t1 = t2;
115 			}
116 			if (i == 0) {
117 				errx(1, "BOGUS i %10jd", (intmax_t)i);
118 			}
119 			fflush(stdout);
120 			j = pread(fdr, buf, i, lp->start);
121 			if (j == i) {
122 				d += i;
123 				if (fdw >= 0)
124 					j = pwrite(fdw, buf, i, lp->start);
125 				else
126 					j = i;
127 				if (j != i)
128 					printf("\nWrite error at %jd/%zu\n",
129 					    lp->start, i);
130 				lp->start += i;
131 				lp->len -= i;
132 				continue;
133 			}
134 			printf("\n%jd %zu failed %d\n", lp->start, i, errno);
135 			new_lump(lp->start, i, lp->state + 1);
136 			lp->start += i;
137 			lp->len -= i;
138 		}
139 		free(lp);
140 	}
141 	printf("\nCompleted\n");
142 	exit (0);
143 }
144 
145