summaryrefslogtreecommitdiffstats
path: root/sbin/recoverdisk
diff options
context:
space:
mode:
authormaxim <maxim@FreeBSD.org>2006-05-06 19:52:25 +0000
committermaxim <maxim@FreeBSD.org>2006-05-06 19:52:25 +0000
commita75e2eb7ff3c599a450406736b161660ed1204cb (patch)
treede3f494520426e51b6114d8b2ada96e71ff43217 /sbin/recoverdisk
parent45fe4fa1ab384ba94b6c0d53177dfb9d8f0ae45d (diff)
downloadFreeBSD-src-a75e2eb7ff3c599a450406736b161660ed1204cb.zip
FreeBSD-src-a75e2eb7ff3c599a450406736b161660ed1204cb.tar.gz
o Take an account a media sectorsize for medium and bigsize calculation.
o Introduce -r and -w keys which allow to load and save a worklist. o Replace README by man page. PR: bin/96677 Submitted by: Ulrich Spoerlein Approved by: phk MFC after: 1 month
Diffstat (limited to 'sbin/recoverdisk')
-rw-r--r--sbin/recoverdisk/Makefile3
-rw-r--r--sbin/recoverdisk/recoverdisk.1128
-rw-r--r--sbin/recoverdisk/recoverdisk.c204
3 files changed, 290 insertions, 45 deletions
diff --git a/sbin/recoverdisk/Makefile b/sbin/recoverdisk/Makefile
index e86fbb2..1bfb3a7 100644
--- a/sbin/recoverdisk/Makefile
+++ b/sbin/recoverdisk/Makefile
@@ -1,9 +1,6 @@
# $FreeBSD$
PROG= recoverdisk
-
-NO_MAN=
-
WARNS?= 5
.include <bsd.prog.mk>
diff --git a/sbin/recoverdisk/recoverdisk.1 b/sbin/recoverdisk/recoverdisk.1
new file mode 100644
index 0000000..cf66224
--- /dev/null
+++ b/sbin/recoverdisk/recoverdisk.1
@@ -0,0 +1,128 @@
+.\" Copyright (c) 2006 Ulrich Spoerlein <uspoerlein@gmail.com>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 6, 2006
+.Dt RECOVERDISK 1
+.Os
+.Sh NAME
+.Nm recoverdisk
+.Nd recover data from hard disk or optical media
+.Sh SYNOPSIS
+.Nm
+.Op Fl r Ar rlist
+.Op Fl w Ar wlist
+.Ar special
+.Op file
+.Sh DESCRIPTION
+The
+.Nm
+utility reads data from the
+.Pa special
+file until all blocks could be successfully read.
+It starts reading in multiples of the sector size.
+Whenever a block fails, it is put to the end of the working queue and will be
+read again, possibly with a smaller read size.
+.Pp
+It uses block sizes of roughly 1 MB, 64kB, and the native sector size (usually
+512 bytes).
+These figures are adjusted slightly, for devices whose sectorsize is not a
+power of 2, e.g., audio CDs with a sector size of 2352 bytes.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl r Ar rlist
+Read the list of blocks and block sizes to read from the specified file.
+.It Fl w Ar wlist
+Write the list of remaining blocks to read to the specified file if
+.Nm
+is aborted via SIGINT.
+.El
+.Pp
+The
+.Fl r
+and
+.Fl w
+option can be used in combination.
+Especially, they can point to the same file, which will be updated on abort.
+.Sh OUTPUT
+.Nm
+prints several columns, detailing the progress
+.Bl -tag -width remaining
+.It start
+Starting offset of the current block.
+.It size
+Read size of the current block.
+.It len
+Length of the current block.
+.It state
+Is increased for every failed read.
+.It done
+Number of bytes already read.
+.It remaining
+Number of bytes remaining.
+.It % done
+Percent complete.
+.El
+.Sh EXAMPLES
+# recover data from failing hard drive ad3
+.Dl $ touch /data/lots_of_space
+.Dl $ recoverdisk /dev/ad3 /data/lots_of_space
+.Pp
+# clone a hard disk
+.Dl $ recoverdisk /dev/ad3 /dev/ad4
+.Pp
+# read an ISO image from a CD-ROM
+.Dl $ touch /data/cd.iso; recoverdisk /dev/acd0 /data/cd.iso
+.Pp
+# continue reading from a broken CD and update the existing worklist
+.Dl $ recoverdisk -r worklist -w worklist /dev/acd0 /data/cd.iso
+.Pp
+# recover a single file from the unreadable media
+.Dl $ touch file.avi; recoverdisk /cdrom/file.avi file.avi
+.Sh SEE ALSO
+.Xr dd 1
+.Sh HISTORY
+The
+.Nm
+command first appeared in
+.Fx 7.0 .
+.Sh BUGS
+Reading from media where the sectorsize is not a power of 2 will make all
+1 MB reads fail.
+This is due to the DMA reads being split up into blocks of at most 128kB.
+These reads then fail if the sectorsize is not a divisor of 128kB.
+When reading a full raw audio CD, this leads to roughly 700 error messages
+flying by.
+This is harmless.
+.Sh AUTHORS
+.An -nosplit
+The original implementation was done by
+.An Poul-Henning Kamp Aq phk@freebsd.org
+with minor improvements from
+.An Ulrich Sp\(:orlein Aq uspoerlein@gmail.com .
+.Pp
+This manual page was written by
+.An Ulrich Sp\(:orlein Aq uspoerlein@gmail.com .
diff --git a/sbin/recoverdisk/recoverdisk.c b/sbin/recoverdisk/recoverdisk.c
index d30055a..0951d3c 100644
--- a/sbin/recoverdisk/recoverdisk.c
+++ b/sbin/recoverdisk/recoverdisk.c
@@ -1,4 +1,4 @@
-/*
+/*-
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you
@@ -8,21 +8,26 @@
*
* $FreeBSD$
*/
-#include <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/disk.h>
+#include <sys/stat.h>
+
#include <err.h>
#include <errno.h>
#include <fcntl.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <time.h>
#include <unistd.h>
-#include <sys/queue.h>
-#include <sys/disk.h>
-#include <sys/stat.h>
-#define BIGSIZE (1024 * 1024)
-#define MEDIUMSIZE (64 * 1024)
-#define MINSIZE (512)
+volatile sig_atomic_t aborting = 0;
+static size_t bigsize = 1024 * 1024;
+static size_t medsize = 64 * 1024;
+static size_t minsize = 512;
struct lump {
off_t start;
@@ -33,7 +38,6 @@ struct lump {
static TAILQ_HEAD(, lump) lumps = TAILQ_HEAD_INITIALIZER(lumps);
-
static void
new_lump(off_t start, off_t len, int state)
{
@@ -48,29 +52,126 @@ new_lump(off_t start, off_t len, int state)
TAILQ_INSERT_TAIL(&lumps, lp, list);
}
+static struct lump *lp;
+static char *wworklist = NULL;
+static char *rworklist = NULL;
+
+/* Save the worklist if -w was given */
+static void
+save_worklist(void)
+{
+ FILE *file;
+
+ if (wworklist != NULL) {
+ (void)fprintf(stderr, "\nSaving worklist ...");
+ fflush(stderr);
+
+ file = fopen(wworklist, "w");
+ if (file == NULL)
+ err(1, "Error opening file %s", wworklist);
+
+ for (;;) {
+ lp = TAILQ_FIRST(&lumps);
+ if (lp == NULL)
+ break;
+ fprintf(file, "%jd %jd %d\n",
+ (intmax_t)lp->start, (intmax_t)lp->len, lp->state);
+ TAILQ_REMOVE(&lumps, lp, list);
+ }
+ (void)fprintf(stderr, " done.\n");
+ }
+}
+
+/* Read the worklist if -r was given */
+static off_t
+read_worklist(off_t t)
+{
+ off_t s, l, d;
+ int state, lines;
+ FILE *file;
+
+ (void)fprintf(stderr, "Reading worklist ...");
+ fflush(stderr);
+ file = fopen(rworklist, "r");
+ if (file == NULL)
+ err(1, "Error opening file %s", rworklist);
+
+ lines = 0;
+ d = t;
+ for (;;) {
+ ++lines;
+ if (3 != fscanf(file, "%jd %jd %d\n", &s, &l, &state)) {
+ if (!feof(file))
+ err(1, "Error parsing file %s at line %d",
+ rworklist, lines);
+ else
+ break;
+ }
+ new_lump(s, l, state);
+ d -= l;
+ }
+ (void)fprintf(stderr, " done.\n");
+ /*
+ * Return the number of bytes already read
+ * (at least not in worklist).
+ */
+ return (d);
+}
+
+static void
+usage(void)
+{
+ (void)fprintf(stderr,
+ "usage: recoverdisk [-r worklist] [-w worklist] source-drive [destination]\n");
+ exit(1);
+}
+
+static void
+sighandler(__unused int sig)
+{
+
+ aborting = 1;
+}
+
int
-main(int argc, const char **argv)
+main(int argc, char * const argv[])
{
+ int ch;
int fdr, fdw;
- struct lump *lp;
- off_t t, d;
+ off_t t, d;
size_t i, j;
int error, flags;
u_char *buf;
- u_int sectorsize, minsize;
+ u_int sectorsize;
time_t t1, t2;
struct stat sb;
+ while ((ch = getopt(argc, argv, "r:w:")) != -1) {
+ switch (ch) {
+ case 'r':
+ rworklist = strdup(optarg);
+ if (rworklist == NULL)
+ err(1, "Cannot allocate enough memory");
+ break;
+ case 'w':
+ wworklist = strdup(optarg);
+ if (wworklist == NULL)
+ err(1, "Cannot allocate enough memory");
+ break;
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+ argc -= optind;
+ argv += optind;
- if (argc < 2)
- errx(1, "Usage: %s source-drive [destination]", argv[0]);
+ if (argc < 1 || argc > 2)
+ usage();
- buf = malloc(BIGSIZE);
- if (buf == NULL)
- err(1, "Cannot allocate %d bytes buffer", BIGSIZE);
- fdr = open(argv[1], O_RDONLY);
+ fdr = open(argv[0], O_RDONLY);
if (fdr < 0)
- err(1, "Cannot open read descriptor %s", argv[1]);
+ err(1, "Cannot open read descriptor %s", argv[0]);
error = fstat(fdr, &sb);
if (error < 0)
@@ -80,46 +181,61 @@ main(int argc, const char **argv)
error = ioctl(fdr, DIOCGSECTORSIZE, &sectorsize);
if (error < 0)
err(1, "DIOCGSECTORSIZE failed");
+
+ /*
+ * Make medsize roughly 64kB, depending on native sector
+ * size. bigsize has to be a multiple of medsize.
+ * For media with 2352 sectors, this will
+ * result in 2352, 63504, and 1016064 bytes.
+ */
minsize = sectorsize;
+ medsize = (medsize / sectorsize) * sectorsize;
+ bigsize = medsize * 16;
error = ioctl(fdr, DIOCGMEDIASIZE, &t);
if (error < 0)
err(1, "DIOCGMEDIASIZE failed");
} else {
- sectorsize = 1;
t = sb.st_size;
- minsize = MINSIZE;
flags |= O_CREAT | O_TRUNC;
}
- if (argc > 2) {
- fdw = open(argv[2], flags, DEFFILEMODE);
+ buf = malloc(bigsize);
+ if (buf == NULL)
+ err(1, "Cannot allocate %jd bytes buffer", (intmax_t)bigsize);
+
+ if (argc > 1) {
+ fdw = open(argv[1], flags, DEFFILEMODE);
if (fdw < 0)
- err(1, "Cannot open write descriptor %s", argv[2]);
- } else {
+ err(1, "Cannot open write descriptor %s", argv[1]);
+ } else
fdw = -1;
- }
- new_lump(0, t, 0);
- d = 0;
+ if (rworklist != NULL) {
+ d = read_worklist(t);
+ } else {
+ new_lump(0, t, 0);
+ d = 0;
+ }
+ if (wworklist != NULL)
+ signal(SIGINT, sighandler);
t1 = 0;
+ printf("%13s %7s %13s %5s %13s %13s %9s\n",
+ "start", "size", "len", "state", "done", "remaining", "% done");
for (;;) {
lp = TAILQ_FIRST(&lumps);
if (lp == NULL)
break;
- TAILQ_REMOVE(&lumps, lp, list);
- while (lp->len > 0) {
- i = BIGSIZE;
- if (lp->len < BIGSIZE)
- i = lp->len;
+ while (lp->len > 0 && !aborting) {
+ i = MIN(lp->len, bigsize);
if (lp->state == 1)
- i = MEDIUMSIZE;
+ i = MIN(lp->len, medsize);
if (lp->state > 1)
- i = minsize;
+ i = MIN(lp->len, minsize);
time(&t2);
- if (t1 != t2 || lp->len < BIGSIZE) {
- printf("\r%13jd %7zu %13jd %3d %13jd %13jd %.8f",
+ if (t1 != t2 || lp->len < bigsize) {
+ printf("\r%13jd %7zu %13jd %5d %13jd %13jd %.7f",
(intmax_t)lp->start,
i,
(intmax_t)lp->len,
@@ -152,9 +268,13 @@ main(int argc, const char **argv)
lp->start += i;
lp->len -= i;
}
+ if (aborting) {
+ save_worklist();
+ return (0);
+ }
+ TAILQ_REMOVE(&lumps, lp, list);
free(lp);
}
printf("\nCompleted\n");
- exit (0);
+ return (0);
}
-
OpenPOWER on IntegriCloud