]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/recoverdisk/recoverdisk.c
MFV r339226 (peter): Record merge of serf-1.3.9.
[FreeBSD/FreeBSD.git] / sbin / recoverdisk / recoverdisk.c
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$
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
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
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
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         (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
136 static void
137 usage(void)
138 {
139         (void)fprintf(stderr, "usage: recoverdisk [-b bigsize] [-r readlist] "
140             "[-s interval] [-w writelist] source [destination]\n");
141         exit(1);
142 }
143
144 static void
145 sighandler(__unused int sig)
146 {
147
148         aborting = 1;
149 }
150
151 int
152 main(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, state;
159         u_char *buf;
160         u_int sectorsize;
161         off_t stripesize;
162         time_t t1, t2;
163         struct stat sb;
164         u_int n, snapshot = 60;
165
166         while ((ch = getopt(argc, argv, "b:r:w:s:")) != -1) {
167                 switch (ch) {
168                 case 'b':
169                         bigsize = strtoul(optarg, NULL, 0);
170                         break;
171                 case 'r':
172                         rworklist = strdup(optarg);
173                         if (rworklist == NULL)
174                                 err(1, "Cannot allocate enough memory");
175                         break;
176                 case 's':
177                         snapshot = strtoul(optarg, NULL, 0);
178                         break;
179                 case 'w':
180                         wworklist = strdup(optarg);
181                         if (wworklist == NULL)
182                                 err(1, "Cannot allocate enough memory");
183                         break;
184                 default:
185                         usage();
186                         /* NOTREACHED */
187                 }
188         }
189         argc -= optind;
190         argv += optind;
191
192         if (argc < 1 || argc > 2)
193                 usage();
194
195         fdr = open(argv[0], O_RDONLY);
196         if (fdr < 0)
197                 err(1, "Cannot open read descriptor %s", argv[0]);
198
199         error = fstat(fdr, &sb);
200         if (error < 0)
201                 err(1, "fstat failed");
202         if (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode)) {
203                 error = ioctl(fdr, DIOCGSECTORSIZE, &sectorsize);
204                 if (error < 0)
205                         err(1, "DIOCGSECTORSIZE failed");
206
207                 error = ioctl(fdr, DIOCGSTRIPESIZE, &stripesize);
208                 if (error == 0 && stripesize > sectorsize)
209                         sectorsize = stripesize;
210
211                 minsize = sectorsize;
212                 bigsize = rounddown(bigsize, sectorsize);
213
214                 error = ioctl(fdr, DIOCGMEDIASIZE, &t);
215                 if (error < 0)
216                         err(1, "DIOCGMEDIASIZE failed");
217         } else {
218                 t = sb.st_size;
219         }
220
221         if (bigsize < minsize)
222                 bigsize = minsize;
223
224         for (ch = 0; (bigsize >> ch) > minsize; ch++)
225                 continue;
226         medsize = bigsize >> (ch / 2);
227         medsize = rounddown(medsize, minsize);
228
229         fprintf(stderr, "Bigsize = %zu, medsize = %zu, minsize = %zu\n",
230             bigsize, medsize, minsize);
231
232         buf = malloc(bigsize);
233         if (buf == NULL)
234                 err(1, "Cannot allocate %zu bytes buffer", bigsize);
235
236         if (argc > 1) {
237                 fdw = open(argv[1], O_WRONLY | O_CREAT, DEFFILEMODE);
238                 if (fdw < 0)
239                         err(1, "Cannot open write descriptor %s", argv[1]);
240                 if (ftruncate(fdw, t) < 0)
241                         err(1, "Cannot truncate output %s to %jd bytes",
242                             argv[1], (intmax_t)t);
243         } else
244                 fdw = -1;
245
246         if (rworklist != NULL) {
247                 d = read_worklist(t);
248         } else {
249                 new_lump(0, t, 0);
250                 d = 0;
251         }
252         if (wworklist != NULL)
253                 signal(SIGINT, sighandler);
254
255         t1 = 0;
256         start = len = i = state = 0;
257         PRINT_HEADER;
258         n = 0;
259         for (;;) {
260                 lp = TAILQ_FIRST(&lumps);
261                 if (lp == NULL)
262                         break;
263                 while (lp->len > 0 && !aborting) {
264                         /* These are only copied for printing stats */
265                         start = lp->start;
266                         len = lp->len;
267                         state = lp->state;
268
269                         i = MIN(lp->len, (off_t)bigsize);
270                         if (lp->state == 1)
271                                 i = MIN(lp->len, (off_t)medsize);
272                         if (lp->state > 1)
273                                 i = MIN(lp->len, (off_t)minsize);
274                         time(&t2);
275                         if (t1 != t2 || lp->len < (off_t)bigsize) {
276                                 PRINT_STATUS(start, i, len, state, d, t);
277                                 t1 = t2;
278                                 if (++n == snapshot) {
279                                         save_worklist();
280                                         n = 0;
281                                 }
282                         }
283                         if (i == 0) {
284                                 errx(1, "BOGUS i %10jd", (intmax_t)i);
285                         }
286                         fflush(stdout);
287                         j = pread(fdr, buf, i, lp->start);
288                         if (j == i) {
289                                 d += i;
290                                 if (fdw >= 0)
291                                         j = pwrite(fdw, buf, i, lp->start);
292                                 else
293                                         j = i;
294                                 if (j != i)
295                                         printf("\nWrite error at %jd/%zu\n",
296                                             lp->start, i);
297                                 lp->start += i;
298                                 lp->len -= i;
299                                 continue;
300                         }
301                         printf("\n%jd %zu failed (%s)\n",
302                             lp->start, i, strerror(errno));
303                         if (errno == EINVAL) {
304                                 printf("read() size too big? Try with -b 131072");
305                                 aborting = 1;
306                         }
307                         if (errno == ENXIO)
308                                 aborting = 1;
309                         new_lump(lp->start, i, lp->state + 1);
310                         lp->start += i;
311                         lp->len -= i;
312                 }
313                 if (aborting) {
314                         save_worklist();
315                         return (0);
316                 }
317                 TAILQ_REMOVE(&lumps, lp, list);
318                 free(lp);
319         }
320         PRINT_STATUS(start, i, len, state, d, t);
321         save_worklist();
322         printf("\nCompleted\n");
323         return (0);
324 }