1 /*-
2 * SPDX-License-Identifier: Beerware
3 *
4 * ----------------------------------------------------------------------------
5 * "THE BEER-WARE LICENSE" (Revision 42):
6 * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you
7 * can do whatever you want with this stuff. If we meet some day, and you think
8 * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
9 * ----------------------------------------------------------------------------
10 *
11 * $FreeBSD: stable/12/sbin/recoverdisk/recoverdisk.c 344091 2019-02-13 09:28:48Z avos $
12 */
13 #include <sys/param.h>
14 #include <sys/queue.h>
15 #include <sys/disk.h>
16 #include <sys/stat.h>
17
18 #include <err.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <signal.h>
22 #include <stdint.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <time.h>
27 #include <unistd.h>
28
29 static volatile sig_atomic_t aborting = 0;
30 static size_t bigsize = 1024 * 1024;
31 static size_t medsize;
32 static size_t minsize = 512;
33
34 struct lump {
35 off_t start;
36 off_t len;
37 int state;
38 TAILQ_ENTRY(lump) list;
39 };
40
41 static TAILQ_HEAD(, lump) lumps = TAILQ_HEAD_INITIALIZER(lumps);
42
43 static void
new_lump(off_t start,off_t len,int state)44 new_lump(off_t start, off_t len, int state)
45 {
46 struct lump *lp;
47
48 lp = malloc(sizeof *lp);
49 if (lp == NULL)
50 err(1, "Malloc failed");
51 lp->start = start;
52 lp->len = len;
53 lp->state = state;
54 TAILQ_INSERT_TAIL(&lumps, lp, list);
55 }
56
57 static struct lump *lp;
58 static char *wworklist = NULL;
59 static char *rworklist = NULL;
60
61
62 #define PRINT_HEADER \
63 printf("%13s %7s %13s %5s %13s %13s %9s\n", \
64 "start", "size", "block-len", "state", "done", "remaining", "% done")
65
66 #define PRINT_STATUS(start, i, len, state, d, t) \
67 printf("\r%13jd %7zu %13jd %5d %13jd %13jd %9.5f", \
68 (intmax_t)start, \
69 i, \
70 (intmax_t)len, \
71 state, \
72 (intmax_t)d, \
73 (intmax_t)(t - d), \
74 100*(double)d/(double)t)
75
76 /* Save the worklist if -w was given */
77 static void
save_worklist(void)78 save_worklist(void)
79 {
80 FILE *file;
81 struct lump *llp;
82
83 if (wworklist != NULL) {
84 (void)fprintf(stderr, "\nSaving worklist ...");
85 fflush(stderr);
86
87 file = fopen(wworklist, "w");
88 if (file == NULL)
89 err(1, "Error opening file %s", wworklist);
90
91 TAILQ_FOREACH(llp, &lumps, list)
92 fprintf(file, "%jd %jd %d\n",
93 (intmax_t)llp->start, (intmax_t)llp->len,
94 llp->state);
95 fclose(file);
96 (void)fprintf(stderr, " done.\n");
97 }
98 }
99
100 /* Read the worklist if -r was given */
101 static off_t
read_worklist(off_t t)102 read_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 fclose(file);
129 (void)fprintf(stderr, " done.\n");
130 /*
131 * Return the number of bytes already read
132 * (at least not in worklist).
133 */
134 return (d);
135 }
136
137 static void
usage(void)138 usage(void)
139 {
140 (void)fprintf(stderr, "usage: recoverdisk [-b bigsize] [-r readlist] "
141 "[-s interval] [-w writelist] source [destination]\n");
142 exit(1);
143 }
144
145 static void
sighandler(__unused int sig)146 sighandler(__unused int sig)
147 {
148
149 aborting = 1;
150 }
151
152 int
main(int argc,char * const argv[])153 main(int argc, char * const argv[])
154 {
155 int ch;
156 int fdr, fdw;
157 off_t t, d, start, len;
158 size_t i, j;
159 int error, state;
160 u_char *buf;
161 u_int sectorsize;
162 off_t stripesize;
163 time_t t1, t2;
164 struct stat sb;
165 u_int n, snapshot = 60;
166
167 while ((ch = getopt(argc, argv, "b:r:w:s:")) != -1) {
168 switch (ch) {
169 case 'b':
170 bigsize = strtoul(optarg, NULL, 0);
171 break;
172 case 'r':
173 rworklist = strdup(optarg);
174 if (rworklist == NULL)
175 err(1, "Cannot allocate enough memory");
176 break;
177 case 's':
178 snapshot = strtoul(optarg, NULL, 0);
179 break;
180 case 'w':
181 wworklist = strdup(optarg);
182 if (wworklist == NULL)
183 err(1, "Cannot allocate enough memory");
184 break;
185 default:
186 usage();
187 /* NOTREACHED */
188 }
189 }
190 argc -= optind;
191 argv += optind;
192
193 if (argc < 1 || argc > 2)
194 usage();
195
196 fdr = open(argv[0], O_RDONLY);
197 if (fdr < 0)
198 err(1, "Cannot open read descriptor %s", argv[0]);
199
200 error = fstat(fdr, &sb);
201 if (error < 0)
202 err(1, "fstat failed");
203 if (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode)) {
204 error = ioctl(fdr, DIOCGSECTORSIZE, §orsize);
205 if (error < 0)
206 err(1, "DIOCGSECTORSIZE failed");
207
208 error = ioctl(fdr, DIOCGSTRIPESIZE, &stripesize);
209 if (error == 0 && stripesize > sectorsize)
210 sectorsize = stripesize;
211
212 minsize = sectorsize;
213 bigsize = rounddown(bigsize, sectorsize);
214
215 error = ioctl(fdr, DIOCGMEDIASIZE, &t);
216 if (error < 0)
217 err(1, "DIOCGMEDIASIZE failed");
218 } else {
219 t = sb.st_size;
220 }
221
222 if (bigsize < minsize)
223 bigsize = minsize;
224
225 for (ch = 0; (bigsize >> ch) > minsize; ch++)
226 continue;
227 medsize = bigsize >> (ch / 2);
228 medsize = rounddown(medsize, minsize);
229
230 fprintf(stderr, "Bigsize = %zu, medsize = %zu, minsize = %zu\n",
231 bigsize, medsize, minsize);
232
233 buf = malloc(bigsize);
234 if (buf == NULL)
235 err(1, "Cannot allocate %zu bytes buffer", bigsize);
236
237 if (argc > 1) {
238 fdw = open(argv[1], O_WRONLY | O_CREAT, DEFFILEMODE);
239 if (fdw < 0)
240 err(1, "Cannot open write descriptor %s", argv[1]);
241 if (ftruncate(fdw, t) < 0)
242 err(1, "Cannot truncate output %s to %jd bytes",
243 argv[1], (intmax_t)t);
244 } else
245 fdw = -1;
246
247 if (rworklist != NULL) {
248 d = read_worklist(t);
249 } else {
250 new_lump(0, t, 0);
251 d = 0;
252 }
253 if (wworklist != NULL)
254 signal(SIGINT, sighandler);
255
256 t1 = 0;
257 start = len = i = state = 0;
258 PRINT_HEADER;
259 n = 0;
260 for (;;) {
261 lp = TAILQ_FIRST(&lumps);
262 if (lp == NULL)
263 break;
264 while (lp->len > 0 && !aborting) {
265 /* These are only copied for printing stats */
266 start = lp->start;
267 len = lp->len;
268 state = lp->state;
269
270 i = MIN(lp->len, (off_t)bigsize);
271 if (lp->state == 1)
272 i = MIN(lp->len, (off_t)medsize);
273 if (lp->state > 1)
274 i = MIN(lp->len, (off_t)minsize);
275 time(&t2);
276 if (t1 != t2 || lp->len < (off_t)bigsize) {
277 PRINT_STATUS(start, i, len, state, d, t);
278 t1 = t2;
279 if (++n == snapshot) {
280 save_worklist();
281 n = 0;
282 }
283 }
284 if (i == 0) {
285 errx(1, "BOGUS i %10jd", (intmax_t)i);
286 }
287 fflush(stdout);
288 j = pread(fdr, buf, i, lp->start);
289 if (j == i) {
290 d += i;
291 if (fdw >= 0)
292 j = pwrite(fdw, buf, i, lp->start);
293 else
294 j = i;
295 if (j != i)
296 printf("\nWrite error at %jd/%zu\n",
297 lp->start, i);
298 lp->start += i;
299 lp->len -= i;
300 continue;
301 }
302 printf("\n%jd %zu failed (%s)\n",
303 lp->start, i, strerror(errno));
304 if (errno == EINVAL) {
305 printf("read() size too big? Try with -b 131072");
306 aborting = 1;
307 }
308 if (errno == ENXIO)
309 aborting = 1;
310 new_lump(lp->start, i, lp->state + 1);
311 lp->start += i;
312 lp->len -= i;
313 }
314 if (aborting) {
315 save_worklist();
316 return (0);
317 }
318 TAILQ_REMOVE(&lumps, lp, list);
319 free(lp);
320 }
321 PRINT_STATUS(start, i, len, state, d, t);
322 save_worklist();
323 printf("\nCompleted\n");
324 return (0);
325 }
326