]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tools/tools/recoverdisk/recoverdisk.c
This commit was generated by cvs2svn to compensate for changes in r162621,
[FreeBSD/FreeBSD.git] / tools / tools / 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 volatile sig_atomic_t aborting = 0;
28 static size_t bigsize = 1024 * 1024;
29 static size_t medsize = 64 * 1024;
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 /* Save the worklist if -w was given */
60 static void
61 save_worklist(void)
62 {
63         FILE *file;
64
65         if (wworklist != NULL) {
66                 (void)fprintf(stderr, "\nSaving worklist ...");
67                 fflush(stderr);
68
69                 file = fopen(wworklist, "w");
70                 if (file == NULL)
71                         err(1, "Error opening file %s", wworklist);
72
73                 for (;;) {
74                         lp = TAILQ_FIRST(&lumps);
75                         if (lp == NULL)
76                                 break;
77                         fprintf(file, "%jd %jd %d\n",
78                             (intmax_t)lp->start, (intmax_t)lp->len, lp->state);
79                         TAILQ_REMOVE(&lumps, lp, list);
80                 }
81                 (void)fprintf(stderr, " done.\n");
82         }
83 }
84
85 /* Read the worklist if -r was given */
86 static off_t
87 read_worklist(off_t t)
88 {
89         off_t s, l, d;
90         int state, lines;
91         FILE *file;
92
93         (void)fprintf(stderr, "Reading worklist ...");
94         fflush(stderr);
95         file = fopen(rworklist, "r");
96         if (file == NULL)
97                 err(1, "Error opening file %s", rworklist);
98
99         lines = 0;
100         d = t;
101         for (;;) {
102                 ++lines;
103                 if (3 != fscanf(file, "%jd %jd %d\n", &s, &l, &state)) {
104                         if (!feof(file))
105                                 err(1, "Error parsing file %s at line %d",
106                                     rworklist, lines);
107                         else
108                                 break;
109                 }
110                 new_lump(s, l, state);
111                 d -= l;
112         }
113         (void)fprintf(stderr, " done.\n");
114         /*
115          * Return the number of bytes already read
116          * (at least not in worklist).
117          */
118         return (d);
119 }
120
121 static void
122 usage(void)
123 {
124         (void)fprintf(stderr,
125     "usage: recoverdisk [-r worklist] [-w worklist] source-drive [destination]\n");
126         exit(1);
127 }
128
129 static void
130 sighandler(__unused int sig)
131 {
132
133         aborting = 1;
134 }
135
136 int
137 main(int argc, char * const argv[])
138 {
139         int ch;
140         int fdr, fdw;
141         off_t t, d;
142         size_t i, j;
143         int error, flags;
144         u_char *buf;
145         u_int sectorsize;
146         time_t t1, t2;
147         struct stat sb;
148
149         while ((ch = getopt(argc, argv, "r:w:")) != -1) {
150                 switch (ch) {
151                 case 'r':
152                         rworklist = strdup(optarg);
153                         if (rworklist == NULL)
154                                 err(1, "Cannot allocate enough memory");
155                         break;
156                 case 'w':
157                         wworklist = strdup(optarg);
158                         if (wworklist == NULL)
159                                 err(1, "Cannot allocate enough memory");
160                         break;
161                 default:
162                         usage();
163                         /* NOTREACHED */
164                 }
165         }
166         argc -= optind;
167         argv += optind;
168
169         if (argc < 1 || argc > 2)
170                 usage();
171
172         fdr = open(argv[0], O_RDONLY);
173         if (fdr < 0)
174                 err(1, "Cannot open read descriptor %s", argv[0]);
175
176         error = fstat(fdr, &sb);
177         if (error < 0)
178                 err(1, "fstat failed");
179         flags = O_WRONLY;
180         if (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode)) {
181                 error = ioctl(fdr, DIOCGSECTORSIZE, &sectorsize);
182                 if (error < 0)
183                         err(1, "DIOCGSECTORSIZE failed");
184
185                 /*
186                  * Make medsize roughly 64kB, depending on native sector
187                  * size. bigsize has to be a multiple of medsize.
188                  * For media with 2352 sectors, this will
189                  * result in 2352, 63504, and 1016064 bytes.
190                  */
191                 minsize = sectorsize;
192                 medsize = (medsize / sectorsize) * sectorsize;
193                 bigsize = medsize * 16;
194
195                 error = ioctl(fdr, DIOCGMEDIASIZE, &t);
196                 if (error < 0)
197                         err(1, "DIOCGMEDIASIZE failed");
198         } else {
199                 t = sb.st_size;
200                 flags |= O_CREAT | O_TRUNC;
201         }
202
203         buf = malloc(bigsize);
204         if (buf == NULL)
205                 err(1, "Cannot allocate %jd bytes buffer", (intmax_t)bigsize);
206
207         if (argc > 1) {
208                 fdw = open(argv[1], flags, DEFFILEMODE);
209                 if (fdw < 0)
210                         err(1, "Cannot open write descriptor %s", argv[1]);
211         } else
212                 fdw = -1;
213
214         if (rworklist != NULL) {
215                 d = read_worklist(t);
216         } else {
217                 new_lump(0, t, 0);
218                 d = 0;
219         }
220         if (wworklist != NULL)
221                 signal(SIGINT, sighandler);
222
223         t1 = 0;
224         printf("%13s %7s %13s %5s %13s %13s %9s\n",
225             "start", "size", "len", "state", "done", "remaining", "% done");
226         for (;;) {
227                 lp = TAILQ_FIRST(&lumps);
228                 if (lp == NULL)
229                         break;
230                 while (lp->len > 0 && !aborting) {
231                         i = MIN(lp->len, (off_t)bigsize);
232                         if (lp->state == 1)
233                                 i = MIN(lp->len, (off_t)medsize);
234                         if (lp->state > 1)
235                                 i = MIN(lp->len, (off_t)minsize);
236                         time(&t2);
237                         if (t1 != t2 || lp->len < (off_t)bigsize) {
238                                 printf("\r%13jd %7zu %13jd %5d %13jd %13jd %.7f",
239                                     (intmax_t)lp->start,
240                                     i, 
241                                     (intmax_t)lp->len,
242                                     lp->state,
243                                     (intmax_t)d,
244                                     (intmax_t)(t - d),
245                                     (double)d/(double)t);
246                                 t1 = t2;
247                         }
248                         if (i == 0) {
249                                 errx(1, "BOGUS i %10jd", (intmax_t)i);
250                         }
251                         fflush(stdout);
252                         j = pread(fdr, buf, i, lp->start);
253                         if (j == i) {
254                                 d += i;
255                                 if (fdw >= 0)
256                                         j = pwrite(fdw, buf, i, lp->start);
257                                 else
258                                         j = i;
259                                 if (j != i)
260                                         printf("\nWrite error at %jd/%zu\n",
261                                             lp->start, i);
262                                 lp->start += i;
263                                 lp->len -= i;
264                                 continue;
265                         }
266                         printf("\n%jd %zu failed %d\n", lp->start, i, errno);
267                         new_lump(lp->start, i, lp->state + 1);
268                         lp->start += i;
269                         lp->len -= i;
270                 }
271                 if (aborting) {
272                         save_worklist();
273                         return (0);
274                 }
275                 TAILQ_REMOVE(&lumps, lp, list);
276                 free(lp);
277         }
278         printf("\nCompleted\n");
279         return (0);
280 }