aboutsummaryrefslogtreecommitdiff
path: root/sbin/recoverdisk
diff options
context:
space:
mode:
Diffstat (limited to 'sbin/recoverdisk')
-rw-r--r--sbin/recoverdisk/recoverdisk.131
-rw-r--r--sbin/recoverdisk/recoverdisk.c72
2 files changed, 73 insertions, 30 deletions
diff --git a/sbin/recoverdisk/recoverdisk.1 b/sbin/recoverdisk/recoverdisk.1
index 9f1deb4c0c23..76085f3ceb41 100644
--- a/sbin/recoverdisk/recoverdisk.1
+++ b/sbin/recoverdisk/recoverdisk.1
@@ -31,6 +31,7 @@
.Sh SYNOPSIS
.Nm
.Op Fl b Ar bigsize
+.Op Fl i Ar interval
.Op Fl r Ar readlist
.Op Fl s Ar interval
.Op Fl u Ar pattern
@@ -54,7 +55,7 @@ sessions can be resumed, for instance when a marginal
source hard-disk shuts down.
.Pp
The work-list is initialized with a single item which covers the entire
-.Ar source
+.Ar source
and
.Nm
always chips away at the first item on the work-list.
@@ -70,9 +71,9 @@ is specified, the corresponding range is filled with '_UNREAD_'.
.Pp
The first pass attempts to read everything in "big-size" chunks,
the second pass reads in "medium-size" chunks and third and subsequent
-passes read in "small-size" chunks. This three stage process is
-an attempt to optimize the case where only a few bad blocks exist
-on
+passes read in "small-size" chunks.
+This three stage process is an attempt to optimize the case where only
+a few bad blocks exist on
.Ar source .
If too many read-errors are encountered,
.Nm
@@ -102,23 +103,31 @@ every minute.
.It Fl l Ar log-file
Each successful read is logged with timestamp, offset and length.
.It Fl t Ar totalsize
-How many bytes should be recovered. The default is what
+How many bytes should be recovered.
+The default is what
.Dv DIOCGMEDIASIZE
reports for character and block devices or
.Dv st_size
if
.Ar source
is a regular file.
+.It Fl i Ar pause
+.Xr sleep 3
+this long between reads. This reduces the load on the
+.Ar source
+device and the system in general.
.It Fl p Ar pause
.Xr sleep 3
-this long whenever a read fails. This makes the
+this long whenever a read fails.
+This makes the
.Ar source
device look less sick to the operating system.
.It Fl u Ar pattern
By default blocks which cannot be read are filled with the pattern
.Ql _UNREAD_
-in the output file. This option can be used to specify a different
-pattern. If the pattern is the empty string, nothing is written.
+in the output file.
+This option can be used to specify a different pattern.
+If the pattern is the empty string, nothing is written.
.It Fl v
Produce a detailed progress report with ANSI escapes and UTF-8.
.El
@@ -176,7 +185,7 @@ Keep the drive cool with a fan.
.It
If possible, power the drive from a laboratory power supply.
.It
-Do not loose patience: Let
+Do not lose patience: Let
.Nm
run as long as possible.
.It
@@ -186,7 +195,7 @@ is a problem, use a USB-(S)ATA adapter instead.
The
.Nm
source code is deliberately written to be easily portable to
-older versions of
+older versions of
.Fx
and to other operating systems.
.It
@@ -227,7 +236,7 @@ to the size of a track.
The
.Nm
utility first appeared in
-.Fx 7.0
+.Fx 7.0
because Somebody™ forgot to make a backup copy.
.Sh AUTHORS
.An -nosplit
diff --git a/sbin/recoverdisk/recoverdisk.c b/sbin/recoverdisk/recoverdisk.c
index f13a1f211863..5971f78738ac 100644
--- a/sbin/recoverdisk/recoverdisk.c
+++ b/sbin/recoverdisk/recoverdisk.c
@@ -28,6 +28,11 @@
#include <time.h>
#include <unistd.h>
+/*
+ * This is a compromise between speed and wasted effort
+ */
+#define COMPROMISE_SIZE (128<<10)
+
struct lump {
uint64_t start;
uint64_t len;
@@ -51,6 +56,7 @@ static uint64_t medium_read;
static uint64_t small_read;
static uint64_t total_size;
static uint64_t done_size;
+static uint64_t wasted_size;
static char *input;
static char *write_worklist_file = NULL;
static char *read_worklist_file = NULL;
@@ -61,6 +67,7 @@ static FILE *log_file = NULL;
static char *work_buf;
static char *pattern_buf;
static double error_pause;
+static double interval;
static unsigned nlumps;
static double n_reads, n_good_reads;
@@ -418,7 +425,8 @@ fill_buf(char *buf, int64_t len, const char *pattern)
static void
usage(void)
{
- fprintf(stderr, "usage: recoverdisk [-b big_read] [-r readlist] "
+ fprintf(stderr, "usage: recoverdisk "
+ "[-b big_read] [-i interval ] [-r readlist] "
"[-s interval] [-w writelist] source [destination]\n");
/* XXX update */
exit(1);
@@ -486,6 +494,7 @@ attempt_one_lump(time_t t_now)
fflush(log_file);
}
} else {
+ wasted_size += sz;
printf("%14ju %7ju read error %d: (%s)",
(uintmax_t)lp->start,
(uintmax_t)sz, error, strerror(error));
@@ -557,8 +566,6 @@ determine_read_sizes(void)
u_int sectorsize;
off_t stripesize;
- determine_total_size();
-
#ifdef DIOCGSECTORSIZE
if (small_read == 0) {
error = ioctl(read_fd, DIOCGSECTORSIZE, &sectorsize);
@@ -572,8 +579,8 @@ determine_read_sizes(void)
#endif
if (small_read == 0) {
- printf("Assuming 512 for small_read\n");
small_read = 512;
+ printf("# Defaulting small_read to %ju\n", (uintmax_t)small_read);
}
if (medium_read && (medium_read % small_read)) {
@@ -593,13 +600,13 @@ determine_read_sizes(void)
#ifdef DIOCGSTRIPESIZE
if (medium_read == 0) {
error = ioctl(read_fd, DIOCGSTRIPESIZE, &stripesize);
- if (error < 0 || stripesize < 0) {
+ if (error < 0 || stripesize <= 0) {
// nope
} else if ((uint64_t)stripesize < small_read) {
// nope
} else if (stripesize % small_read) {
// nope
- } else if (0 < stripesize && stripesize < (128<<10)) {
+ } else if (stripesize <= COMPROMISE_SIZE) {
medium_read = stripesize;
printf("# Got medium_read from DIOCGSTRIPESIZE: %ju\n",
(uintmax_t)medium_read
@@ -607,6 +614,7 @@ determine_read_sizes(void)
}
}
#endif
+
#if defined(DIOCGFWSECTORS) && defined(DIOCGFWHEADS)
if (medium_read == 0) {
u_int fwsectors = 0, fwheads = 0;
@@ -616,10 +624,16 @@ determine_read_sizes(void)
error = ioctl(read_fd, DIOCGFWHEADS, &fwheads);
if (error)
fwheads = 0;
- if (fwsectors && fwheads) {
+ if (fwsectors * fwheads * small_read <= COMPROMISE_SIZE) {
medium_read = fwsectors * fwheads * small_read;
printf(
- "# Got medium_read from DIOCGFW{SECTORS,HEADS}: %ju\n",
+ "# Got medium_read from DIOCGFW{SECTORS*HEADS}: %ju\n",
+ (uintmax_t)medium_read
+ );
+ } else if (fwsectors * small_read <= COMPROMISE_SIZE) {
+ medium_read = fwsectors * small_read;
+ printf(
+ "# Got medium_read from DIOCGFWSECTORS: %ju\n",
(uintmax_t)medium_read
);
}
@@ -627,10 +641,11 @@ determine_read_sizes(void)
#endif
if (big_read == 0 && medium_read != 0) {
- if (medium_read > (64<<10)) {
+ if (medium_read * 2 > COMPROMISE_SIZE) {
big_read = medium_read;
+ medium_read = 0;
} else {
- big_read = 128 << 10;
+ big_read = COMPROMISE_SIZE;
big_read -= big_read % medium_read;
}
printf("# Got big_read from medium_read: %ju\n",
@@ -639,12 +654,16 @@ determine_read_sizes(void)
}
if (big_read == 0) {
- big_read = 128 << 10;
+ big_read = COMPROMISE_SIZE;
+ big_read -= big_read % small_read;
printf("# Defaulting big_read to %ju\n",
(uintmax_t)big_read
);
}
+ if (medium_read >= big_read)
+ medium_read = 0;
+
if (medium_read == 0) {
/*
* We do not want to go directly to single sectors, but
@@ -662,12 +681,20 @@ determine_read_sizes(void)
(uintmax_t)medium_read
);
}
- fprintf(stderr,
- "# Bigsize = %ju, medium_read = %ju, small_read = %ju\n",
+ printf("# Bigsize = %ju, medium_read = %ju, small_read = %ju\n",
(uintmax_t)big_read, (uintmax_t)medium_read, (uintmax_t)small_read);
-}
+ assert(0 < small_read);
+
+ assert(0 < medium_read);
+ assert(medium_read >= small_read);
+ assert(medium_read <= big_read);
+ assert(medium_read % small_read == 0);
+ assert(0 < big_read);
+ assert(big_read >= medium_read);
+ assert(big_read % small_read == 0);
+}
/**********************************************************************/
@@ -687,15 +714,14 @@ monitor_read_sizes(uint64_t failed_size)
);
big_read = medium_read;
medium_read = small_read;
+ wasted_size = 0;
return;
}
- if (failed_size > small_read) {
- if (n_reads < n_good_reads + 100)
- return;
+ if (big_read > small_read && wasted_size / small_read > 200) {
fprintf(
stderr,
- "Too many failures."
+ "Too much wasted effort."
" (%.0f bad of %.0f)"
" Shifting to small_reads.\n",
n_reads - n_good_reads, n_reads
@@ -719,11 +745,14 @@ main(int argc, char * const argv[])
setbuf(stdout, NULL);
setbuf(stderr, NULL);
- while ((ch = getopt(argc, argv, "b:l:p:m:r:w:s:t:u:v")) != -1) {
+ while ((ch = getopt(argc, argv, "b:i:l:p:m:r:w:s:t:u:v")) != -1) {
switch (ch) {
case 'b':
big_read = strtoul(optarg, NULL, 0);
break;
+ case 'i':
+ interval = strtod(optarg, NULL);
+ break;
case 'l':
log_file = fopen(optarg, "a");
if (log_file == NULL) {
@@ -774,6 +803,8 @@ main(int argc, char * const argv[])
if (read_fd < 0)
err(1, "Cannot open read descriptor %s", argv[0]);
+ determine_total_size();
+
determine_read_sizes();
work_buf = malloc(big_read);
@@ -816,6 +847,9 @@ main(int argc, char * const argv[])
t_save = t_first;
unsaved = 0;
while (!aborting) {
+ if (interval > 0) {
+ usleep((unsigned long)(1e6 * interval));
+ }
t_now = time(NULL);
sz = attempt_one_lump(t_now);
error = errno;