recoverdisk.c revision 149605
1135911Sphk/*
2135911Sphk * ----------------------------------------------------------------------------
3135911Sphk * "THE BEER-WARE LICENSE" (Revision 42):
4135911Sphk * <phk@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
5135911Sphk * can do whatever you want with this stuff. If we meet some day, and you think
6135911Sphk * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
7135911Sphk * ----------------------------------------------------------------------------
8135911Sphk *
9135911Sphk * $FreeBSD: head/sbin/recoverdisk/recoverdisk.c 149605 2005-08-29 23:08:01Z sobomax $
10135911Sphk */
11135911Sphk#include <stdio.h>
12135911Sphk#include <stdint.h>
13135911Sphk#include <stdlib.h>
14136257Sphk#include <err.h>
15135911Sphk#include <errno.h>
16135911Sphk#include <fcntl.h>
17136257Sphk#include <time.h>
18135911Sphk#include <unistd.h>
19135911Sphk#include <sys/queue.h>
20135911Sphk#include <sys/disk.h>
21149605Ssobomax#include <sys/stat.h>
22135911Sphk
23135911Sphk#define BIGSIZE		(1024 * 1024)
24135911Sphk#define MEDIUMSIZE	(64 * 1024)
25149605Ssobomax#define MINSIZE		(512)
26135911Sphk
27135911Sphkstruct lump {
28135911Sphk	off_t			start;
29135911Sphk	off_t			len;
30135911Sphk	int			state;
31135911Sphk	TAILQ_ENTRY(lump)	list;
32135911Sphk};
33135911Sphk
34135911Sphkstatic TAILQ_HEAD(, lump) lumps = TAILQ_HEAD_INITIALIZER(lumps);
35135911Sphk
36135911Sphk
37135911Sphkstatic void
38135911Sphknew_lump(off_t start, off_t len, int state)
39135911Sphk{
40135911Sphk	struct lump *lp;
41135911Sphk
42135911Sphk	lp = malloc(sizeof *lp);
43135911Sphk	if (lp == NULL)
44135911Sphk		err(1, "Malloc failed");
45135911Sphk	lp->start = start;
46135911Sphk	lp->len = len;
47135911Sphk	lp->state = state;
48135911Sphk	TAILQ_INSERT_TAIL(&lumps, lp, list);
49135911Sphk}
50135911Sphk
51135911Sphkint
52135911Sphkmain(int argc, const char **argv)
53135911Sphk{
54135911Sphk	int fdr, fdw;
55135911Sphk	struct lump *lp;
56135911Sphk	off_t 	t, d;
57135911Sphk	size_t i, j;
58149605Ssobomax	int error, flags;
59135911Sphk	u_char *buf;
60149605Ssobomax	u_int sectorsize, minsize;
61136257Sphk	time_t t1, t2;
62149605Ssobomax	struct stat sb;
63135911Sphk
64135911Sphk
65135911Sphk	if (argc < 2)
66135911Sphk		errx(1, "Usage: %s source-drive [destination]", argv[0]);
67135911Sphk
68135911Sphk	buf = malloc(BIGSIZE);
69135911Sphk	if (buf == NULL)
70135911Sphk		err(1, "Cannot allocate %d bytes buffer", BIGSIZE);
71135911Sphk	fdr = open(argv[1], O_RDONLY);
72135911Sphk	if (fdr < 0)
73135911Sphk		err(1, "Cannot open read descriptor %s", argv[1]);
74149605Ssobomax
75149605Ssobomax	error = fstat(fdr, &sb);
76149605Ssobomax	if (error < 0)
77149605Ssobomax		err(1, "fstat failed");
78149605Ssobomax	flags = O_WRONLY;
79149605Ssobomax	if (S_ISBLK(sb.st_mode) || S_ISCHR(sb.st_mode)) {
80149605Ssobomax		error = ioctl(fdr, DIOCGSECTORSIZE, &sectorsize);
81149605Ssobomax		if (error < 0)
82149605Ssobomax			err(1, "DIOCGSECTORSIZE failed");
83149605Ssobomax		minsize = sectorsize;
84149605Ssobomax
85149605Ssobomax		error = ioctl(fdr, DIOCGMEDIASIZE, &t);
86149605Ssobomax		if (error < 0)
87149605Ssobomax			err(1, "DIOCGMEDIASIZE failed");
88149605Ssobomax	} else {
89149605Ssobomax		sectorsize = 1;
90149605Ssobomax		t = sb.st_size;
91149605Ssobomax		minsize = MINSIZE;
92149605Ssobomax		flags |= O_CREAT | O_TRUNC;
93149605Ssobomax	}
94149605Ssobomax
95135911Sphk	if (argc > 2) {
96149605Ssobomax		fdw = open(argv[2], flags, DEFFILEMODE);
97135911Sphk		if (fdw < 0)
98135911Sphk			err(1, "Cannot open write descriptor %s", argv[2]);
99135911Sphk	} else {
100135911Sphk		fdw = -1;
101135911Sphk	}
102135911Sphk
103135911Sphk	new_lump(0, t, 0);
104135911Sphk	d = 0;
105135911Sphk
106136257Sphk	t1 = 0;
107135911Sphk	for (;;) {
108135911Sphk		lp = TAILQ_FIRST(&lumps);
109135911Sphk		if (lp == NULL)
110135911Sphk			break;
111135911Sphk		TAILQ_REMOVE(&lumps, lp, list);
112135911Sphk		while (lp->len > 0) {
113135911Sphk			i = BIGSIZE;
114135911Sphk			if (lp->len < BIGSIZE)
115135911Sphk				i = lp->len;
116135911Sphk			if (lp->state == 1)
117135911Sphk				i = MEDIUMSIZE;
118135911Sphk			if (lp->state > 1)
119149605Ssobomax				i = minsize;
120136257Sphk			time(&t2);
121136257Sphk			if (t1 != t2 || lp->len < BIGSIZE) {
122136815Sdes				printf("\r%13jd %7zu %13jd %3d %13jd %13jd %.8f",
123136257Sphk				    (intmax_t)lp->start,
124136815Sdes				    i,
125136257Sphk				    (intmax_t)lp->len,
126136257Sphk				    lp->state,
127136257Sphk				    (intmax_t)d,
128136257Sphk				    (intmax_t)(t - d),
129136257Sphk				    (double)d/(double)t);
130136257Sphk				t1 = t2;
131136257Sphk			}
132135911Sphk			if (i == 0) {
133135911Sphk				errx(1, "BOGUS i %10jd", (intmax_t)i);
134135911Sphk			}
135135911Sphk			fflush(stdout);
136135911Sphk			j = pread(fdr, buf, i, lp->start);
137135911Sphk			if (j == i) {
138135911Sphk				d += i;
139135911Sphk				if (fdw >= 0)
140135911Sphk					j = pwrite(fdw, buf, i, lp->start);
141135911Sphk				else
142135911Sphk					j = i;
143135911Sphk				if (j != i)
144136815Sdes					printf("\nWrite error at %jd/%zu\n",
145136815Sdes					    lp->start, i);
146135911Sphk				lp->start += i;
147135911Sphk				lp->len -= i;
148135911Sphk				continue;
149135911Sphk			}
150136815Sdes			printf("\n%jd %zu failed %d\n", lp->start, i, errno);
151135911Sphk			new_lump(lp->start, i, lp->state + 1);
152135911Sphk			lp->start += i;
153135911Sphk			lp->len -= i;
154135911Sphk		}
155135911Sphk		free(lp);
156135911Sphk	}
157135911Sphk	printf("\nCompleted\n");
158135911Sphk	exit (0);
159135911Sphk}
160135911Sphk
161