recoverdisk.c revision 227081
13896Sjjg/*-
24094Sksrini * ----------------------------------------------------------------------------
33896Sjjg * "THE BEER-WARE LICENSE" (Revision 42):
43896Sjjg * <phk@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
53896Sjjg * can do whatever you want with this stuff. If we meet some day, and you think
63896Sjjg * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
73896Sjjg * ----------------------------------------------------------------------------
83896Sjjg *
93896Sjjg * $FreeBSD: head/sbin/recoverdisk/recoverdisk.c 227081 2011-11-04 13:36:02Z ed $
103896Sjjg */
113896Sjjg#include <sys/param.h>
123896Sjjg#include <sys/queue.h>
133896Sjjg#include <sys/disk.h>
143896Sjjg#include <sys/stat.h>
153896Sjjg
163896Sjjg#include <err.h>
173896Sjjg#include <errno.h>
183896Sjjg#include <fcntl.h>
193896Sjjg#include <signal.h>
203896Sjjg#include <stdint.h>
213896Sjjg#include <stdio.h>
223896Sjjg#include <stdlib.h>
233896Sjjg#include <string.h>
243896Sjjg#include <time.h>
253896Sjjg#include <unistd.h>
263896Sjjg
273896Sjjgstatic volatile sig_atomic_t aborting = 0;
283896Sjjgstatic size_t bigsize = 1024 * 1024;
293896Sjjgstatic size_t medsize;
303896Sjjgstatic size_t minsize = 512;
313896Sjjg
323896Sjjgstruct lump {
333896Sjjg	off_t			start;
343896Sjjg	off_t			len;
353896Sjjg	int			state;
363896Sjjg	TAILQ_ENTRY(lump)	list;
373896Sjjg};
383896Sjjg
393896Sjjgstatic TAILQ_HEAD(, lump) lumps = TAILQ_HEAD_INITIALIZER(lumps);
403896Sjjg
413896Sjjgstatic void
423896Sjjgnew_lump(off_t start, off_t len, int state)
433896Sjjg{
443896Sjjg	struct lump *lp;
453896Sjjg
463896Sjjg	lp = malloc(sizeof *lp);
473896Sjjg	if (lp == NULL)
483896Sjjg		err(1, "Malloc failed");
493896Sjjg	lp->start = start;
503896Sjjg	lp->len = len;
513896Sjjg	lp->state = state;
523896Sjjg	TAILQ_INSERT_TAIL(&lumps, lp, list);
533896Sjjg}
543896Sjjg
553896Sjjgstatic struct lump *lp;
563896Sjjgstatic char *wworklist = NULL;
573896Sjjgstatic char *rworklist = NULL;
583896Sjjg
593896Sjjg
603896Sjjg#define PRINT_HEADER \
613896Sjjg	printf("%13s %7s %13s %5s %13s %13s %9s\n", \
623896Sjjg		"start", "size", "block-len", "state", "done", "remaining", "% done")
633896Sjjg
643896Sjjg#define PRINT_STATUS(start, i, len, state, d, t) \
653896Sjjg	printf("\r%13jd %7zu %13jd %5d %13jd %13jd %9.5f", \
663896Sjjg		(intmax_t)start, \
673896Sjjg		i,  \
683896Sjjg		(intmax_t)len, \
693896Sjjg		state, \
703896Sjjg		(intmax_t)d, \
713896Sjjg		(intmax_t)(t - d), \
723896Sjjg		100*(double)d/(double)t)
733896Sjjg
743896Sjjg/* Save the worklist if -w was given */
753896Sjjgstatic void
763896Sjjgsave_worklist(void)
773896Sjjg{
783896Sjjg	FILE *file;
793896Sjjg	struct lump *llp;
803896Sjjg
813896Sjjg	if (wworklist != NULL) {
823896Sjjg		(void)fprintf(stderr, "\nSaving worklist ...");
833896Sjjg		fflush(stderr);
843896Sjjg
853896Sjjg		file = fopen(wworklist, "w");
863896Sjjg		if (file == NULL)
873896Sjjg			err(1, "Error opening file %s", wworklist);
883896Sjjg
893896Sjjg		TAILQ_FOREACH(llp, &lumps, list)
903896Sjjg			fprintf(file, "%jd %jd %d\n",
913896Sjjg			    (intmax_t)llp->start, (intmax_t)llp->len,
923896Sjjg			    llp->state);
933896Sjjg		fclose(file);
943896Sjjg		(void)fprintf(stderr, " done.\n");
953896Sjjg	}
963896Sjjg}
973896Sjjg
984094Sksrini/* Read the worklist if -r was given */
993896Sjjgstatic off_t
1003896Sjjgread_worklist(off_t t)
1013896Sjjg{
1023896Sjjg	off_t s, l, d;
1033896Sjjg	int state, lines;
1043896Sjjg	FILE *file;
1053896Sjjg
1063896Sjjg	(void)fprintf(stderr, "Reading worklist ...");
1073896Sjjg	fflush(stderr);
1083896Sjjg	file = fopen(rworklist, "r");
1093896Sjjg	if (file == NULL)
1103896Sjjg		err(1, "Error opening file %s", rworklist);
1113896Sjjg
1123896Sjjg	lines = 0;
1133896Sjjg	d = t;
1143896Sjjg	for (;;) {
1153896Sjjg		++lines;
1163896Sjjg		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
134static void
135usage(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
142static void
143sighandler(__unused int sig)
144{
145
146	aborting = 1;
147}
148
149int
150main(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	time_t t1, t2;
160	struct stat sb;
161	u_int n, snapshot = 60;
162
163	while ((ch = getopt(argc, argv, "b:r:w:s:")) != -1) {
164		switch (ch) {
165		case 'b':
166			bigsize = strtoul(optarg, NULL, 0);
167			break;
168		case 'r':
169			rworklist = strdup(optarg);
170			if (rworklist == NULL)
171				err(1, "Cannot allocate enough memory");
172			break;
173		case 's':
174			snapshot = strtoul(optarg, NULL, 0);
175			break;
176		case 'w':
177			wworklist = strdup(optarg);
178			if (wworklist == NULL)
179				err(1, "Cannot allocate enough memory");
180			break;
181		default:
182			usage();
183			/* NOTREACHED */
184		}
185	}
186	argc -= optind;
187	argv += optind;
188
189	if (argc < 1 || argc > 2)
190		usage();
191
192	fdr = open(argv[0], O_RDONLY);
193	if (fdr < 0)
194		err(1, "Cannot open read descriptor %s", argv[0]);
195
196	error = fstat(fdr, &sb);
197	if (error < 0)
198		err(1, "fstat failed");
199	if (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode)) {
200		error = ioctl(fdr, DIOCGSECTORSIZE, &sectorsize);
201		if (error < 0)
202			err(1, "DIOCGSECTORSIZE failed");
203
204		minsize = sectorsize;
205		bigsize = (bigsize / sectorsize) * sectorsize;
206
207		error = ioctl(fdr, DIOCGMEDIASIZE, &t);
208		if (error < 0)
209			err(1, "DIOCGMEDIASIZE failed");
210	} else {
211		t = sb.st_size;
212	}
213
214	if (bigsize < minsize)
215		bigsize = minsize;
216
217	for (ch = 0; (bigsize >> ch) > minsize; ch++)
218		continue;
219	medsize = bigsize >> (ch / 2);
220	medsize = (medsize / minsize) * minsize;
221
222	fprintf(stderr, "Bigsize = %zu, medsize = %zu, minsize = %zu\n",
223	    bigsize, medsize, minsize);
224
225	buf = malloc(bigsize);
226	if (buf == NULL)
227		err(1, "Cannot allocate %zu bytes buffer", bigsize);
228
229	if (argc > 1) {
230		fdw = open(argv[1], O_WRONLY | O_CREAT, DEFFILEMODE);
231		if (fdw < 0)
232			err(1, "Cannot open write descriptor %s", argv[1]);
233		if (ftruncate(fdw, t) < 0)
234			err(1, "Cannot truncate output %s to %jd bytes",
235			    argv[1], (intmax_t)t);
236	} else
237		fdw = -1;
238
239	if (rworklist != NULL) {
240		d = read_worklist(t);
241	} else {
242		new_lump(0, t, 0);
243		d = 0;
244	}
245	if (wworklist != NULL)
246		signal(SIGINT, sighandler);
247
248	t1 = 0;
249	start = len = i = state = 0;
250	PRINT_HEADER;
251	n = 0;
252	for (;;) {
253		lp = TAILQ_FIRST(&lumps);
254		if (lp == NULL)
255			break;
256		while (lp->len > 0 && !aborting) {
257			/* These are only copied for printing stats */
258			start = lp->start;
259			len = lp->len;
260			state = lp->state;
261
262			i = MIN(lp->len, (off_t)bigsize);
263			if (lp->state == 1)
264				i = MIN(lp->len, (off_t)medsize);
265			if (lp->state > 1)
266				i = MIN(lp->len, (off_t)minsize);
267			time(&t2);
268			if (t1 != t2 || lp->len < (off_t)bigsize) {
269				PRINT_STATUS(start, i, len, state, d, t);
270				t1 = t2;
271				if (++n == snapshot) {
272					save_worklist();
273					n = 0;
274				}
275			}
276			if (i == 0) {
277				errx(1, "BOGUS i %10jd", (intmax_t)i);
278			}
279			fflush(stdout);
280			j = pread(fdr, buf, i, lp->start);
281			if (j == i) {
282				d += i;
283				if (fdw >= 0)
284					j = pwrite(fdw, buf, i, lp->start);
285				else
286					j = i;
287				if (j != i)
288					printf("\nWrite error at %jd/%zu\n",
289					    lp->start, i);
290				lp->start += i;
291				lp->len -= i;
292				continue;
293			}
294			printf("\n%jd %zu failed (%s)\n",
295			    lp->start, i, strerror(errno));
296			if (errno == EINVAL) {
297				printf("read() size too big? Try with -b 131072");
298				aborting = 1;
299			}
300			if (errno == ENXIO)
301				aborting = 1;
302			new_lump(lp->start, i, lp->state + 1);
303			lp->start += i;
304			lp->len -= i;
305		}
306		if (aborting) {
307			save_worklist();
308			return (0);
309		}
310		TAILQ_REMOVE(&lumps, lp, list);
311		free(lp);
312	}
313	PRINT_STATUS(start, i, len, state, d, t);
314	save_worklist();
315	printf("\nCompleted\n");
316	return (0);
317}
318