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