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);
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':
step_max = parse_int(&p, 1, SECLIMIT - 1, "step maximum");
else if (SKIP_PREFIX("retry"))
max_retries = parse_int(&p, 0, INT_MAX, "retries");
+ else if (SKIP_PREFIX("alpha"))
+ alpha = parse_float(&p, 0, 1, "average decay factor");
else if (SKIP_PREFIX("_badwait"))
bad_block_delay = parse_float(&p, 0, DBL_MAX, "bad-block delay");
else if (SKIP_PREFIX("_blkwait"))
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))
}
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);
+ 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, 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);
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) {
if (f&f_write)
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];
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",