static void usage(FILE *fp)
{
fprintf(fp,
- "usage: %s [-c] [-B PARAM=VALUE,...] [-R MAP]\n"
+ "usage: %s [-ci] [-B PARAM=VALUE,...] [-R MAP]\n"
"\t[-b OUTMAP] [-r [START]-[END]] DEVICE OUTFILE\n",
prog);
}
(tv_hi->tv_usec - tv_lo->tv_usec)/1.0e6);
}
-static void carefully_write(int fd, const void *buf, size_t sz)
-{
- const unsigned char *p = buf;
- ssize_t n;
-
- if (fd < 0) return;
- while (sz) {
- n = write(fd, p, sz);
- if (n < 0) {
- if (errno == EINTR) continue;
- bail_syserr(errno, "failed to write to output file");
- }
- if (!n) bail("unexpected short write to output file");
- p += n; sz -= n;
- }
-}
-
-static void open_file_on_demand(const char *file, FILE **fp_inout,
- const char *what)
-{
- FILE *fp;
-
- if (!*fp_inout) {
- fp = fopen(file, "w");
- if (!fp)
- bail_syserr(errno, "failed to open %s file `%s'", what, file);
- fprintf(fp, "## %s\n\n", what);
- *fp_inout = fp;
- }
-}
-
-static void check_write(FILE *fp, const char *what)
-{
- fflush(fp);
- if (ferror(fp)) bail_syserr(errno, "error writing %s file", what);
-}
-
-static void carefully_fclose(FILE *fp, const char *what)
-{
- if (fp && (ferror(fp) || fclose(fp)))
- bail_syserr(errno, "error writing %s file", what);
-}
-
#define DEFVEC(vtype, etype) \
typedef struct { etype *v; size_t n, sz; } vtype
#define VEC_INIT { 0, 0, 0 }
progress_shownotice(render, bg, 7);
}
+static double alpha = 0.1;
+
static void update_progress(secaddr pos)
{
struct timeval now;
gettimeofday(&now, 0);
t = tvdiff(&last_time, &now);
-#define ALPHA 0.1
-#define BETA (1 - ALPHA)
+#define BETA (1 - alpha)
if (t) {
g = wcount ? pow(BETA, t) : 0.0; f = (1 - g)/(1 - BETA);
last_time = now; last_pos = pos;
}
-#undef ALPHA
#undef BETA
throbix++; if (!throbber[throbix]) throbix = 0;
if (badblocks.n) {
best = 0; lo = 0; hi = badblocks.n;
#ifdef DEBUG
- progress_clear();
+ progress_clear(&progress);
printf(";; searching badblocks for %"PRIuSEC" .. %"PRIuSEC"\n",
pos, pos + want);
#endif
va_list ap;
va_start(ap, what);
- progress_clear();
+ progress_clear(&progress);
printf(";; recovery buffer (");
vprintf(what, ap);
printf("): "
ssize_t n;
#ifdef DEBUG
- progress_clear();
+ progress_clear(&progress);
show_recovery_buffer_map(r, "begin(%"PRIuSEC", %"PRIuSEC")", pos, want);
#endif
} else if (pos > r->pos + r->end) {
r->pos = pos; r->start = r->end = 0;
#ifdef DEBUG
-p show_recovery_buffer_map(r, "cleared; beyond previous region");
+ show_recovery_buffer_map(r, "cleared; beyond previous region");
#endif
} else if (pos + want > r->pos + r->sz) {
diff = (pos + want) - (r->pos + r->sz);
recovered(bad_lo, end); *pos_inout = end;
return (0);
}
- step = step_factor*(bad_hi - bad_lo);
+ step = (step_factor - 1)*(bad_hi - bad_lo);
if (step < step_min) step = step_min;
if (step_max && step > step_max) step = step_max;
- if (step > end - bad_lo) step = end - bad_lo;
- pos = bad_lo + step - 1;
+ if (step > end - bad_hi) step = end - bad_hi;
+ pos = bad_hi + step - 1;
want = run_length_wanted(pos, step, end);
n = recovery_read(&r, pos, want);
#ifdef DEBUG
if (n == want) good = pos;
else bad_hi = pos + n + 1;
}
- recovered(bad_lo, bad_hi); *pos_inout = good;
- if (good < r.pos + r.start || r.pos + r.end <= good)
+ recovered(bad_lo, bad_hi); *pos_inout = bad_hi;
+ if (bad_hi < r.pos + r.start || r.pos + r.end <= bad_hi)
n = 0;
else {
- n = r.pos + r.end - good;
- rearrange_sectors(&r, 0, good - r.pos, n);
+ n = r.pos + r.end - bad_hi;
+ rearrange_sectors(&r, 0, bad_hi - r.pos, n);
}
#ifdef DEBUG
show_recovery_buffer_map(&r, "returning %zd good sectors at %"PRIuSEC"",
- n, good);
+ n, bad_hi);
#endif
return (n);
}
return (0);
}
-static double parse_float(const char **p_inout, double min, double max,
- const char *what)
-{
- const char *p;
- char *q;
- double x;
- int err;
-
- err = errno; errno = 0;
- p = *p_inout;
- x = strtod(p, &q);
- if (errno || x < min || x > max) bail("bad %s `%s'", what, p);
- *p_inout = q; errno = err;
- return (x);
-}
-
-static long parse_int(const char **p_inout, long min, long max,
- const char *what)
-{
- const char *p;
- char *q;
- long x;
- int err;
-
- err = errno; errno = 0;
- p = *p_inout;
- x = strtoul(p, &q, 0);
- if (errno || x < min || x > max) bail("bad %s `%s'", what, p);
- *p_inout = q; errno = err;
- return (x);
-}
-
#define PRF_HYPHEN 1u
static int parse_range(const char *p, unsigned f,
secaddr *start_out, secaddr *end_out)
return (rc);
}
+#ifdef DEBUG
+static void dump_eventq(const char *what)
+{
+ unsigned i;
+ const struct event *ev;
+ char fn[MAXFNSZ];
+
+ printf("\n;; event dump (%s):\n", what);
+ for (i = 0; i < eventq.n; i++) {
+ ev = &eventq.v[i];
+ switch (ev->ev) {
+ case EV_BEGIN:
+ store_filename(fn, filetab.v[ev->file].id);
+ printf(";; %8"PRIuSEC": begin %s\n", ev->pos, fn);
+ break;
+ case EV_END:
+ store_filename(fn, filetab.v[ev->file].id);
+ printf(";; %8"PRIuSEC": end %s\n", ev->pos, fn);
+ break;
+ case EV_WRITE:
+ printf(";; %8"PRIuSEC": write\n", ev->pos);
+ break;
+ case EV_STOP:
+ printf(";; %8"PRIuSEC": stop\n", ev->pos);
+ break;
+ default:
+ printf(";; %8"PRIuSEC": ?%u\n", ev->pos, ev->ev);
+ break;
+ }
+ }
+}
+#endif
+
int main(int argc, char *argv[])
{
unsigned f = 0;
const char *p;
- uint64_t volsz;
+ off_t volsz;
secaddr pos;
off_t off;
secaddr start, end, last;
const char *device, *outfile;
struct badblock *bad;
int opt, blksz;
- unsigned n;
size_t i;
FILE *fp;
struct buf buf = BUF_INIT;
struct timeval tv0, tv1;
double t, rate, tot;
const char *rateunit, *totunit;
- char timebuf[TIMESTRMAX];
- struct stat st;
+ char timebuf[TIMESTRMAX], id_in[MAXIDSZ], id_out[MAXIDSZ];
+ dvd_reader_t *dvd_out;
#ifdef DEBUG
const struct file *file;
char fn[MAXFNSZ];
#define f_continue 2u
#define f_fixup 4u
#define f_stats 8u
+#define f_checkid 16u
#define f_write 256u
set_prog(argv[0]);
for (;;) {
- opt = getopt(argc, argv, "hB:E:FR:X:b:cr:s"); if (opt < 0) break;
+ opt = getopt(argc, argv, "hB:E:FR:X:b:cir:s"); if (opt < 0) break;
switch (opt) {
case 'h': usage(stderr); exit(0);
case 'B':
(STRNCMP(p, ==, s "=", sizeof(s)) && (p += sizeof(s), 1))
for (;;) {
if (SKIP_PREFIX("cf"))
- clear_factor = parse_float(&p, 0, DBL_MAX, "clear factor");
+ clear_factor = parse_float(&p, PNF_JUNK, 0, DBL_MAX,
+ "clear factor");
else if (SKIP_PREFIX("cmin"))
- clear_min = parse_int(&p, 1, SECLIMIT, "clear minimum");
+ clear_min = parse_int(&p, PNF_JUNK, 1, SECLIMIT,
+ "clear minimum");
else if (SKIP_PREFIX("cmax"))
- clear_max = parse_int(&p, 1, SECLIMIT, "clear maximum");
+ clear_max = parse_int(&p, PNF_JUNK, 1, SECLIMIT,
+ "clear maximum");
else if (SKIP_PREFIX("sf"))
- step_factor = parse_float(&p, 0, DBL_MAX, "step factor");
+ step_factor = parse_float(&p, PNF_JUNK, 0, DBL_MAX,
+ "step factor");
else if (SKIP_PREFIX("smin"))
- step_min = parse_int(&p, 1, SECLIMIT - 1, "step minimum");
+ step_min = parse_int(&p, PNF_JUNK, 1, SECLIMIT - 1,
+ "step minimum");
else if (SKIP_PREFIX("smax"))
- step_max = parse_int(&p, 1, SECLIMIT - 1, "step maximum");
+ step_max = parse_int(&p, PNF_JUNK, 1, SECLIMIT - 1,
+ "step maximum");
else if (SKIP_PREFIX("retry"))
- max_retries = parse_int(&p, 0, INT_MAX, "retries");
+ max_retries = parse_int(&p, PNF_JUNK, 0, INT_MAX, "retries");
+ else if (SKIP_PREFIX("alpha"))
+ alpha = parse_float(&p, PNF_JUNK, 0, 1, "average decay factor");
else if (SKIP_PREFIX("_badwait"))
- bad_block_delay = parse_float(&p, 0, DBL_MAX, "bad-block delay");
+ bad_block_delay = parse_float(&p, PNF_JUNK, 0, DBL_MAX,
+ "bad-block delay");
else if (SKIP_PREFIX("_blkwait"))
- good_block_delay = parse_float(&p, 0, DBL_MAX, "good block delay");
+ good_block_delay = parse_float(&p, PNF_JUNK, 0, DBL_MAX,
+ "good block delay");
else
bail("unknown bad blocks parameter `%s'", p);
if (!*p) break;
mapfile = optarg;
break;
case 'c': f |= f_continue; break;
+ case 'i': f |= f_checkid; break;
case 'r':
start = 0; end = -1;
if (parse_range(optarg, PRF_HYPHEN, &start, &end))
#endif
}
- open_dvd(device, &dvdfd, &dvd);
- if (fstat(dvdfd, &st))
- bail_syserr(errno, "failed to stat device `%s'", device);
- if (S_ISREG(st.st_mode)) {
- blksz = SECTORSZ;
- volsz = st.st_size;
- } else if (S_ISBLK(st.st_mode)) {
- if (ioctl(dvdfd, BLKSSZGET, &blksz))
- bail_syserr(errno, "failed to get block size for `%s'", device);
- if (ioctl(dvdfd, BLKGETSIZE64, &volsz))
- bail_syserr(errno, "failed to get volume size for `%s'", device);
- } else
- bail("can't use `%s' as source: expected file or block device", device);
+ open_dvd(device, O_RDONLY, &dvdfd, &dvd);
+ blksz = SECTORSZ; volsz = device_size(dvdfd, device, &blksz);
if (blksz != SECTORSZ)
bail("device `%s' block size %d /= %d", device, blksz, SECTORSZ);
if (volsz%SECTORSZ)
bail("device `%s' volume size %"PRIu64" not a multiple of %d",
device, volsz, SECTORSZ);
+ if (f&f_checkid) {
+ open_dvd(outfile, O_RDONLY, 0, &dvd_out);
+ if (dvd_id(id_in, dvd, DIF_MUSTIFOHASH, device) ||
+ dvd_id(id_out, dvd_out, DIF_MUSTIFOHASH, device))
+ exit(2);
+ if (STRCMP(id_in, !=, id_out))
+ bail("DVD id mismatch: input `%s' is `%s'; output `%s' is `%s'",
+ device, id_in, outfile, id_out);
+ }
+
outfd = open(outfile, O_WRONLY | O_CREAT, 0666);
if (outfd < 0)
bail_syserr(errno, "failed to create output file `%s'", outfile);
for (i = 0; i < filetab.n; i++) {
file = &filetab.v[i];
store_filename(fn, file->id);
- printf(";;\t%8"PRIuSEC" %s\n", file->start, fn);
+ printf(";;\t%8"PRIuSEC" .. %-8"PRIuSEC" %s\n",
+ file->start, file->end, fn);
}
#endif
qsort(eventq.v, eventq.n, sizeof(struct event), compare_event);
- f &= ~f_write; start = 0; n = 0;
+ f &= ~f_write; start = 0;
for (i = 0; i < eventq.n; i++) {
ev = &eventq.v[i];
switch (ev->ev) {
case EV_WRITE:
if (f&f_write)
- bail("overlapping ranges: range from %"PRIuSEC" still open at %"PRIuSEC"",
+ bail("overlapping ranges: range from %"PRIuSEC" "
+ "still open at %"PRIuSEC"",
start, ev->pos);
- n++; f |= f_write; start = ev->pos;
+ f |= f_write; start = ev->pos;
break;
case EV_STOP:
f &= ~f_write;
}
}
+#ifdef DEBUG
+ dump_eventq("initial");
+#endif
f &= ~f_write; start = 0;
for (i = 0; i < eventq.n; i++) {
ev = &eventq.v[i];
- switch (ev->ev) {
- case EV_WRITE: start = ev->pos; f |= f_write; break;
- case EV_STOP: nsectors += ev->pos - start; f &= ~f_write; break;
- }
+ if (ev->ev == EV_WRITE) { start = ev->pos; f |= f_write; }
if (ev->pos >= limit) break;
+ if (ev->ev == EV_STOP) { nsectors += ev->pos - start; f &= ~f_write; }
if (f&f_fixup) start = ev->pos;
}
eventq.n = i;
+#ifdef DEBUG
+ dump_eventq("trimmed");
+#endif
if (f&f_fixup) {
put_event(EV_WRITE, 0, start);
- n++; f |= f_write;
+ f |= f_write;
}
if (f&f_write) {
nsectors += limit - start;
put_event(EV_STOP, 0, limit);
}
+#ifdef DEBUG
+ dump_eventq("final");
+#endif
copy_progress.render = render_copy_progress;
progress_additem(&progress, ©_progress);
if (f&f_stats) {
gettimeofday(&tv1, 0); t = tvdiff(&tv0, &tv1);
- if (nsectors == limit - start) { ndone -= start; nsectors -= start; }
+ if (nsectors == limit) { ndone -= start; nsectors -= start; }
tot = scale_bytes((double)nsectors*SECTORSZ, &totunit);
rate = scale_bytes((double)nsectors*SECTORSZ/t, &rateunit);
moan("all done: %.1f %sB in %s -- %.1f %sB/s",