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
This commit is contained in:
parent
387196bf56
commit
fd64dc9a18
@ -1,9 +1,6 @@
|
||||
# $FreeBSD$
|
||||
|
||||
PROG= recoverdisk
|
||||
|
||||
NO_MAN=
|
||||
|
||||
WARNS?= 5
|
||||
|
||||
.include <bsd.prog.mk>
|
||||
|
128
sbin/recoverdisk/recoverdisk.1
Normal file
128
sbin/recoverdisk/recoverdisk.1
Normal file
@ -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 .
|
@ -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 <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/disk.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#define BIGSIZE (1024 * 1024)
|
||||
#define MEDIUMSIZE (64 * 1024)
|
||||
#define MINSIZE (512)
|
||||
#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>
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, const char **argv)
|
||||
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, 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, §orsize);
|
||||
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);
|
||||
if (fdw < 0)
|
||||
err(1, "Cannot open write descriptor %s", argv[2]);
|
||||
} else {
|
||||
fdw = -1;
|
||||
}
|
||||
buf = malloc(bigsize);
|
||||
if (buf == NULL)
|
||||
err(1, "Cannot allocate %jd bytes buffer", (intmax_t)bigsize);
|
||||
|
||||
new_lump(0, t, 0);
|
||||
d = 0;
|
||||
if (argc > 1) {
|
||||
fdw = open(argv[1], flags, DEFFILEMODE);
|
||||
if (fdw < 0)
|
||||
err(1, "Cannot open write descriptor %s", argv[1]);
|
||||
} else
|
||||
fdw = -1;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,6 @@
|
||||
# $FreeBSD$
|
||||
|
||||
PROG= recoverdisk
|
||||
|
||||
NO_MAN=
|
||||
|
||||
WARNS?= 5
|
||||
|
||||
.include <bsd.prog.mk>
|
||||
|
@ -1,25 +0,0 @@
|
||||
$FreeBSD$
|
||||
|
||||
This is a small tool which will read an entire disk(partition) or file
|
||||
using 1M blocks and optionally write the read data to a file or disk.
|
||||
|
||||
If a read error happens, the 1M block gets put on the end of the worklist
|
||||
and will be retried with 64k blocksize.
|
||||
|
||||
If a read error happens again, the 64k block gets put at the end of the
|
||||
worklist and will be retried with single sector reads.
|
||||
|
||||
The program keeps trying until you stop it.
|
||||
|
||||
You can refresh a disk:
|
||||
|
||||
recoverdisk /dev/ad1 /dev/ad1
|
||||
|
||||
or salvage a floppy:
|
||||
|
||||
touch myfloppy.flp
|
||||
recoverdisk /dev/fd0 myfloppy.flp
|
||||
|
||||
or recover a single file from the unreadable media:
|
||||
|
||||
recoverdisk /cdrom/file.avi file.avi
|
128
tools/tools/recoverdisk/recoverdisk.1
Normal file
128
tools/tools/recoverdisk/recoverdisk.1
Normal file
@ -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 .
|
@ -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 <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/disk.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#define BIGSIZE (1024 * 1024)
|
||||
#define MEDIUMSIZE (64 * 1024)
|
||||
#define MINSIZE (512)
|
||||
#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>
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, const char **argv)
|
||||
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, 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, §orsize);
|
||||
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);
|
||||
if (fdw < 0)
|
||||
err(1, "Cannot open write descriptor %s", argv[2]);
|
||||
} else {
|
||||
fdw = -1;
|
||||
}
|
||||
buf = malloc(bigsize);
|
||||
if (buf == NULL)
|
||||
err(1, "Cannot allocate %jd bytes buffer", (intmax_t)bigsize);
|
||||
|
||||
new_lump(0, t, 0);
|
||||
d = 0;
|
||||
if (argc > 1) {
|
||||
fdw = open(argv[1], flags, DEFFILEMODE);
|
||||
if (fdw < 0)
|
||||
err(1, "Cannot open write descriptor %s", argv[1]);
|
||||
} else
|
||||
fdw = -1;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user