Commit 39b1f04c authored by Mike Hibler's avatar Mike Hibler

A couple of new options inspired by home use:

1. From the "Thank you DOS for all your stupid limits" department
   we have an enhancement to the -I option to ignore a slice.
   The slice number can be followed by a BSD partition letter
   (e.g., "-I1g") to ignore a BSD partition within a slice.
   Ditto for the -R options which forces raw compression.

2. From the "Gee, I shoulda taken that grinding noise more seriously"
   department we have the -X option to force imagezip to try e(X)tremely
   hard (yeah, yeah, its the only letter I could come up with) to read
   the data off the disk, even faking (zero) data if a read fails after
   10 tries.
parent ea12826d
......@@ -12,7 +12,6 @@
.Sh SYNOPSIS
.Nm
.Op Fl dhiorv
.Op Fl F Ar sectors
.Op Fl I Ar slice
.Op Fl R Ar slice
.Op Fl s Ar slice
......@@ -137,10 +136,13 @@ Print a usage message.
.It Fl I Ar slice
In full disk mode, tells
.Nm
to skip a specific slice. This option can be given multiple times to
skip multiple slices.
The slice number should be the DOS partition number which ranges from 1 to 4.
Incompatible with
to skip a specific slice or BSD partition within a slice.
This option can be given multiple times to skip multiple slices or partitions.
The slice number should be the DOS partition number which ranges from 1 to 4,
optionally followed by a BSD partition letter ('a' through 'p').
The latter form only applies to slices with BSD disklabels and allows skipping
subsets of a slice.
This option is incompatible with
.Fl s .
.It Fl i
Prints a variety of diagnostic information about what
......@@ -163,11 +165,14 @@ In full disk mode, tells
.Nm
to force raw (ala
.Fl r )
compression of a specific slice.
compression of a specific slice or BSD partition within a slice.
This option can be given multiple times to force raw compression of
multiple slices.
The slice number should be the DOS partition number which ranges from 1 to 4.
Incompatible with
multiple slices or partitions.
The slice number should be the DOS partition number which ranges from 1 to 4,
optionally followed by a BSD partition letter ('a' through 'p').
The latter form only applies to slices with BSD disklabels and allows skipping
subsets of a slice.
This option is incompatible with
.Fl s .
.It Fl s Ar slice
Create a slice image containing only the indicated slice.
......@@ -204,6 +209,18 @@ how many sectors to compress in full disk mode. Can be used to compress
a subset of a disk.
Incompatible with
.Fl s .
.It Fl D
Allow ``dangerous'' writes.
The default output behavior for
.Nm
is to perform an
.Xr fsync 2
after every chunk write to the image file and to retry such writes that fail
up to 10 times before giving up.
This is a concession to the common case of writing the output
file across NFS, where transient, asynchronously reported errors may occur.
With this option, writes are attempted only once and no fsync is done,
resulting in faster operation.
.It Fl F Ar sectors
The minimum length in sectors that a free range needs to be before it
is recorded as a free range. Ranges shorter than this length are
......@@ -223,6 +240,16 @@ is a Linux filesystem slice. Should be used only when the device does
not contain a DOS partition table and really does contains a Linux filesystem.
Incompatible with
.Fl b .
.It Fl X
Try e(X)tremely hard to read data from the input device,
returning success even if a read fails.
The default input behavior for
.Nm
is to attempt input device reads only once, aborting if a read fails.
With this option, a failing device read will be retried up to 10 times.
If a read still fails, a block of zeros will be returned instead of the data.
This is a desperation measure for those who want to salvage as much data
as possible from a failed disk.
.El
.Sh DIAGNOSTICS
The
......
......@@ -62,10 +62,15 @@
#define DOSPTYP_OPENBSD 0xa6
#endif
#define ISBSD(t) ((t) == DOSPTYP_386BSD || (t) == DOSPTYP_OPENBSD)
#ifndef NDOSPART
#define NDOSPART 4
#endif
/* 0 == false for all, ~0 == true for all, else true for those set */
typedef uint32_t partmap_t[NDOSPART];
char *infilename;
int infd, outfd, outcanseek;
int secsize = 512; /* XXX bytes. */
......@@ -78,11 +83,12 @@ int maxmode = 0;
int slice = 0;
int level = 4;
long dev_bsize = 1;
int ignore[NDOSPART], forceraw[NDOSPART];
int oldstyle = 0;
int frangesize= 64; /* 32k */
int safewrites= 1;
int forcereads= 0;
int retrywrites= 1;
off_t datawritten;
partmap_t ignore, forceraw;
#define sectobytes(s) ((off_t)(s) * secsize)
#define bytestosec(b) (uint32_t)((b) / secsize)
......@@ -143,9 +149,10 @@ int read_raw(void);
int compress_image(void);
void usage(void);
#define IORETRIES 10
#ifdef linux
#define devlseek lseek
#define devread read
#else
static inline off_t devlseek(int fd, off_t off, int whence)
{
......@@ -155,32 +162,86 @@ static inline off_t devlseek(int fd, off_t off, int whence)
assert(noff == (off_t)-1 || (noff & (DEV_BSIZE-1)) == 0);
return noff;
}
#endif
static inline int devread(int fd, void *buf, size_t size)
/*
* Wrap up read in a retry mechanism to persist in the face of IO errors,
* even faking data if requested.
*/
ssize_t
devread(int fd, void *buf, size_t nbytes)
{
assert((size & (DEV_BSIZE-1)) == 0);
return read(fd, buf, size);
}
int cc, i, count;
off_t startoffset;
#ifndef linux
assert((nbytes & (DEV_BSIZE-1)) == 0);
#endif
if (!forcereads)
return read(fd, buf, nbytes);
if ((startoffset = lseek(fd, (off_t) 0, SEEK_CUR)) < 0) {
perror("devread: seeking to get input file ptr");
exit(1);
}
count = 0;
for (i = 0; i < IORETRIES; i++) {
while (nbytes) {
cc = read(fd, buf, nbytes);
if (cc == 0)
break;
if (cc > 0) {
nbytes -= cc;
buf += cc;
count += cc;
continue;
}
if (i == 0)
fprintf(stderr, "read failed: %s, "
"will retry %d more times\n",
strerror(errno), IORETRIES-1);
nbytes += count;
buf -= count;
count = 0;
goto again;
}
return count;
again:
if (lseek(fd, startoffset, SEEK_SET) < 0) {
perror("devread: seeking to set file ptr");
exit(1);
}
}
fprintf(stderr, "devread: read failed in sector range [%u-%u], "
"returning zeros\n",
bytestosec(startoffset), bytestosec(startoffset+nbytes));
memset(buf, 0, nbytes);
return nbytes;
}
/*
* Wrap up write in a retry mechanism to protect against transient NFS
* errors causing a fatal error.
*/
ssize_t
mywrite(int fd, const void *buf, size_t nbytes)
devwrite(int fd, const void *buf, size_t nbytes)
{
int cc, i, count = 0;
off_t startoffset = 0;
int maxretries = 10;
if (outcanseek &&
if (retrywrites && outcanseek &&
((startoffset = lseek(fd, (off_t) 0, SEEK_CUR)) < 0)) {
perror("mywrite: seeking to get output file ptr");
perror("devwrite: seeking to get output file ptr");
exit(1);
}
for (i = 0; i < maxretries; i++) {
for (i = 0; i < IORETRIES; i++) {
while (nbytes) {
cc = write(fd, buf, nbytes);
......@@ -190,13 +251,9 @@ mywrite(int fd, const void *buf, size_t nbytes)
count += cc;
continue;
}
if (!safewrites) {
if (cc < 0)
perror("mywrite: writing");
else
fprintf(stderr, "mywrite: writing\n");
exit(1);
}
if (!retrywrites)
return cc;
if (i == 0)
perror("write error: will retry");
......@@ -207,7 +264,7 @@ mywrite(int fd, const void *buf, size_t nbytes)
count = 0;
goto again;
}
if (safewrites && fsync(fd) < 0) {
if (retrywrites && fsync(fd) < 0) {
perror("fsync error: will retry");
sleep(1);
nbytes += count;
......@@ -219,7 +276,7 @@ mywrite(int fd, const void *buf, size_t nbytes)
return count;
again:
if (lseek(fd, startoffset, SEEK_SET) < 0) {
perror("mywrite: seeking to set file ptr");
perror("devwrite: seeking to set file ptr");
exit(1);
}
}
......@@ -229,7 +286,32 @@ mywrite(int fd, const void *buf, size_t nbytes)
}
/* Map partition number to letter */
#define BSDPARTNAME(i) ("ABCDEFGHIJKLMNOP"[(i)])
#define BSDPARTNAME(i) ("abcdefghijklmnop"[(i)])
static int
setpartition(partmap_t map, char *str)
{
int dospart;
char bsdpart;
bsdpart = str[1];
str[1] = '\0';
dospart = atoi(optarg);
if (dospart < 1 || dospart > 4)
return EINVAL;
/* common case: apply to complete DOS partition */
if (bsdpart == '\0') {
map[dospart-1] = ~0;
return 0;
}
if (bsdpart < 'a' || bsdpart > 'p')
return EINVAL;
map[dospart-1] |= (1 << (bsdpart - 'a'));
return 0;
}
int
main(argc, argv)
......@@ -243,7 +325,7 @@ main(argc, argv)
int rawmode = 0;
extern char build_info[];
while ((ch = getopt(argc, argv, "vlbdihrs:c:z:oI:1F:DR:")) != -1)
while ((ch = getopt(argc, argv, "vlbdihrs:c:z:oI:1F:DR:X")) != -1)
switch(ch) {
case 'v':
version++;
......@@ -252,7 +334,7 @@ main(argc, argv)
info++;
break;
case 'D':
safewrites = 0;
retrywrites = 0;
break;
case 'd':
debug++;
......@@ -283,16 +365,12 @@ main(argc, argv)
inputmaxsec = atoi(optarg);
break;
case 'I':
rval = atoi(optarg);
if (rval < 1 || rval > 4)
if (setpartition(ignore, optarg))
usage();
ignore[rval-1] = 1;
break;
case 'R':
rval = atoi(optarg);
if (rval < 1 || rval > 4)
if (setpartition(forceraw, optarg))
usage();
forceraw[rval-1] = 1;
break;
case '1':
oldstyle = 1;
......@@ -302,6 +380,9 @@ main(argc, argv)
if (frangesize < 0)
usage();
break;
case 'X':
forcereads++;
break;
case 'h':
case '?':
default:
......@@ -358,6 +439,11 @@ main(argc, argv)
else
rval = read_image();
#endif
if (rval) {
fprintf(stderr, "* * * Aborting * * *\n");
exit(1);
}
sortrange(skips, 1);
if (debug)
dumpskips();
......@@ -381,7 +467,7 @@ main(argc, argv)
else {
outfd = fileno(stdout);
outcanseek = 0;
safewrites = 0;
retrywrites = 0;
}
compress_image();
......@@ -484,15 +570,19 @@ read_image()
u_int32_t start = doslabel.parts[i].dp_start;
u_int32_t size = doslabel.parts[i].dp_size;
if (slicemode && i != (slice - 1) /* DOS Numbering */)
if (slicemode && (i + 1) != slice)
continue;
if (ignore[i])
type = DOSPTYP_UNUSED;
else if (forceraw[i]) {
fprintf(stderr,
" Slice %d, Forcing raw compression\n", i+1);
goto skipcheck;
if (ignore[i]) {
if (!ISBSD(type) || ignore[i] == ~0)
type = DOSPTYP_UNUSED;
} else if (forceraw[i]) {
if (!ISBSD(type) || forceraw[i] == ~0) {
fprintf(stderr,
" Slice %d, forcing raw compression\n",
i + 1);
goto skipcheck;
}
}
switch (type) {
......@@ -521,14 +611,16 @@ read_image()
default:
fprintf(stderr,
" Slice %d is an unknown type (%x), "
"performing raw compression.\n",
i + 1 /* DOS Numbering */, type);
"forcing raw compression.\n",
i + 1, type);
break;
}
if (rval) {
warnx("Stopping zip at Slice %d",
i + 1 /* DOS Number */);
return rval;
fprintf(stderr,
" Filesystem specific error in Slice %d, "
"use -R%d to force raw compression.\n",
i + 1, i + 1);
break;
}
skipcheck:
......@@ -627,7 +719,7 @@ read_bsdslice(int slice, u_int32_t start, u_int32_t size, int bsdtype)
*/
if (bsdtype == DOSPTYP_OPENBSD && i >= 8 && i < 16) {
if (debug)
fprintf(stderr, " %c skipping, "
fprintf(stderr, " '%c' skipping, "
"OpenBSD mapping of DOS partition %d\n",
BSDPARTNAME(i), i - 6);
continue;
......@@ -637,7 +729,7 @@ read_bsdslice(int slice, u_int32_t start, u_int32_t size, int bsdtype)
/* XXX silence gcc about dktypenames */
rval = DKMAXTYPES;
fprintf(stderr, " %c ", BSDPARTNAME(i));
fprintf(stderr, " '%c' ", BSDPARTNAME(i));
fprintf(stderr, " start %9d, size %9d\t(%s)\n",
dlabel.label.d_partitions[i].p_offset,
......@@ -645,9 +737,21 @@ read_bsdslice(int slice, u_int32_t start, u_int32_t size, int bsdtype)
fstypenames[dlabel.label.d_partitions[i].p_fstype]);
}
rval = read_bsdpartition(&dlabel.label, i);
if (rval)
return rval;
if (ignore[slice] & (1 << i)) {
fprintf(stderr, " Slice %d BSD partition '%c' ignored,"
" NOT SAVING.\n",
slice + 1, BSDPARTNAME(i));
addskip(dlabel.label.d_partitions[i].p_offset,
dlabel.label.d_partitions[i].p_size);
} else if (forceraw[slice] & (1 << i)) {
fprintf(stderr, " Slice %d BSD partition '%c',"
" forcing raw compression.\n",
slice + 1, BSDPARTNAME(i));
} else {
rval = read_bsdpartition(&dlabel.label, i);
if (rval)
return rval;
}
}
/*
......@@ -710,29 +814,29 @@ read_bsdpartition(struct disklabel *dlabel, int part)
}
if (dlabel->d_partitions[part].p_fstype != FS_BSDFFS) {
warnx("BSD Partition %c: Not a BSD Filesystem",
warnx("BSD Partition '%c': Not a BSD Filesystem",
BSDPARTNAME(part));
return 1;
}
if (devlseek(infd, sectobytes(offset) + SBOFF, SEEK_SET) < 0) {
warnx("BSD Partition %c: Could not seek to superblock",
warnx("BSD Partition '%c': Could not seek to superblock",
BSDPARTNAME(part));
return 1;
}
if ((cc = devread(infd, &fs, SBSIZE)) < 0) {
warn("BSD Partition %c: Could not read superblock",
warn("BSD Partition '%c': Could not read superblock",
BSDPARTNAME(part));
return 1;
}
if (cc != SBSIZE) {
warnx("BSD Partition %c: Truncated superblock",
warnx("BSD Partition '%c': Truncated superblock",
BSDPARTNAME(part));
return 1;
}
if (fs.fs.fs_magic != FS_MAGIC) {
warnx("BSD Partition %c: Bad magic number in superblock",
warnx("BSD Partition '%c': Bad magic number in superblock",
BSDPARTNAME(part));
return 1;
}
......@@ -749,17 +853,17 @@ read_bsdpartition(struct disklabel *dlabel, int part)
dboff = fsbtodb(&fs.fs, cgbase(&fs.fs, i)) + offset;
if (devlseek(infd, sectobytes(cgoff), SEEK_SET) < 0) {
warn("BSD Partition %c: Could not seek to cg %d",
warn("BSD Partition '%c': Could not seek to cg %d",
BSDPARTNAME(part), i);
return 1;
}
if ((cc = devread(infd, &cg, fs.fs.fs_bsize)) < 0) {
warn("BSD Partition %c: Could not read cg %d",
warn("BSD Partition '%c': Could not read cg %d",
BSDPARTNAME(part), i);
return 1;
}
if (cc != fs.fs.fs_bsize) {
warn("BSD Partition %c: Truncated cg %d",
warn("BSD Partition '%c': Truncated cg %d",
BSDPARTNAME(part), i);
return 1;
}
......@@ -2018,7 +2122,16 @@ compress_image(void)
/*
* Write out the finished chunk to disk.
*/
mywrite(outfd, output_buffer, sizeof(output_buffer));
cc = devwrite(outfd, output_buffer, sizeof(output_buffer));
if (cc != sizeof(output_buffer)) {
if (cc < 0)
perror("chunk write");
else
fprintf(stderr,
"chunk write: short write (%d bytes)\n",
cc);
exit(1);
}
/*
* Moving to the next block. Reserve the header area.
......@@ -2099,7 +2212,16 @@ compress_image(void)
* Write out the finished chunk to disk, and
* start over from the beginning of the buffer.
*/
mywrite(outfd, output_buffer, sizeof(output_buffer));
cc = devwrite(outfd, output_buffer, sizeof(output_buffer));
if (cc != sizeof(output_buffer)) {
if (cc < 0)
perror("chunk write");
else
fprintf(stderr,
"chunk write: short write (%d bytes)\n",
cc);
exit(1);
}
buffer_offset = 0;
}
......@@ -2147,7 +2269,7 @@ compress_image(void)
assert(blkhdr->blockindex == i);
blkhdr->blocktotal = count;
if ((cc = mywrite(outfd, buf, sizeof(blockhdr_t))) < 0) {
if ((cc = devwrite(outfd, buf, sizeof(blockhdr_t))) < 0) {
perror("writing new subblock header");
exit(1);
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment