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