X-Git-Url: https://git.distorted.org.uk/~mdw/dvdrip/blobdiff_plain/dc53ebfaa3fb887f962b574c6bafa45b160fc765..5d42ee1b6a34ff5d30c46ba51f7bb8cfdaadc4c8:/dvd-sector-copy.c diff --git a/dvd-sector-copy.c b/dvd-sector-copy.c index 7b7e674..8c4ba1f 100644 --- a/dvd-sector-copy.c +++ b/dvd-sector-copy.c @@ -1,82 +1,13 @@ #include "lib.h" -static int status = 0; - 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); } -static double tvdiff(const struct timeval *tv_lo, - const struct timeval *tv_hi) -{ - return ((tv_hi->tv_sec - tv_lo->tv_sec) + - (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 } -#define VEC_FREE(vv) do { \ - free((vv)->v); (vv)->v 0; (vv)->n = (vv)->sz = 0; \ -} while (0) -#define VEC_PUSH(p, vv) do { \ - size_t _want; \ - if ((vv)->n >= (vv)->sz) { \ - (vv)->sz = (vv)->sz ? 2*(vv)->sz : 32; \ - _want = (vv)->sz*sizeof(*(vv)->v); \ - (vv)->v = realloc((vv)->v, _want); \ - if (!(vv)->v) bail("out of memory allocating %zu bytes", _want); \ - } \ - (p) = &(vv)->v[(vv)->n++]; \ -} while (0) - #define MAXFILES (1 + 2*99 + 1) struct file { ident id; @@ -164,8 +95,8 @@ static void put_menu(dvd_reader_t *dvd, unsigned title) store_filename(fn, id); start = UDFFindFile(dvd, fn, &len); if (!start) return; #ifdef DEBUG - printf(";; %8"PRIuSEC" .. %-8"PRIuSEC": %s\n", - start, start + SECTORS(len), fn); + printf(";; %8"PRIuSEC" .. %-8"PRIuSEC": %s\n", + start, start + SECTORS(len), fn); #endif put_file(id, start, start + SECTORS(len)); } @@ -315,6 +246,8 @@ static void render_badblock_progress(struct progress_item *item, progress_shownotice(render, bg, 7); } +static double alpha = 0.1; + static void update_progress(secaddr pos) { struct timeval now; @@ -323,8 +256,7 @@ static void update_progress(secaddr pos) 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); @@ -334,7 +266,6 @@ static void update_progress(secaddr pos) last_time = now; last_pos = pos; } -#undef ALPHA #undef BETA throbix++; if (!throbber[throbix]) throbix = 0; @@ -380,7 +311,7 @@ static ssize_t read_sectors(secaddr pos, void *buf, secaddr want) 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 @@ -392,7 +323,10 @@ static ssize_t read_sectors(secaddr pos, void *buf, secaddr want) #endif if (pos < bad->start) { D( printf("high\n"); ) best = bad; hi = mid; } else if (pos >= bad->end) { D( printf("low\n"); ) lo = mid + 1; } - else { D( printf("match!\n"); ) errno = EIO; return (-1); } + else { + D( printf("match!\n"); ) + errno = EIO; sit(bad_block_delay); return (-1); + } } #ifdef DEBUG if (best) @@ -402,7 +336,8 @@ static ssize_t read_sectors(secaddr pos, void *buf, secaddr want) if (best && pos + want > best->start) { want = best->start - pos; fakeerr = EIO; sit(bad_block_delay); } } - done = 0; + + done = 0; errno = 0; while (want) { if (vob) { errno = 0; n = DVDReadBlocks(vob, pos - file->start, want, p); } @@ -430,26 +365,6 @@ static ssize_t read_sectors(secaddr pos, void *buf, secaddr want) return (!done && errno ? -1 : done); } -static void record_bad_sectors(secaddr bad_lo, secaddr bad_hi) -{ - char fn[MAXFNSZ]; - - if (!mapfile) return; - - open_file_on_demand(mapfile, &mapfp, "bad-sector region map"); - fprintf(mapfp, "%"PRIuSEC" %"PRIuSEC"", bad_lo, bad_hi); - - if (file && id_kind(file->id) != RAW) { - store_filename(fn, file->id); - fprintf(mapfp, " # `%s' %"PRIuSEC" .. %"PRIuSEC" of %"PRIuSEC"", - fn, bad_lo - file->start, bad_hi - file->start, - file->end - file->start); - } - - fputc('\n', mapfp); - check_write(mapfp, "bad-sector region map"); -} - static void recovered(secaddr bad_lo, secaddr bad_hi) { char fn[MAXFNSZ]; @@ -468,7 +383,19 @@ static void recovered(secaddr bad_lo, secaddr bad_hi) file->end - file->start); } - record_bad_sectors(bad_lo, bad_hi); + if (mapfile) { + open_file_on_demand(mapfile, &mapfp, "bad-sector region map"); + fprintf(mapfp, "%"PRIuSEC" %"PRIuSEC" # %"PRIuSEC" sectors", + bad_lo, bad_hi, bad_hi - bad_lo); + + if (file && id_kind(file->id) != RAW) + fprintf(mapfp, "; `%s' %"PRIuSEC" .. %"PRIuSEC" of %"PRIuSEC"", + fn, bad_lo - file->start, bad_hi - file->start, + file->end - file->start); + + fputc('\n', mapfp); + check_write(mapfp, "bad-sector region map"); + } if (lseek(outfd, (off_t)(bad_hi - bad_lo)*SECTORSZ, SEEK_CUR) < 0) bail_syserr(errno, "failed to seek past bad sectors"); @@ -499,7 +426,7 @@ static PRINTF_LIKE(2, 3) va_list ap; va_start(ap, what); - progress_clear(); + progress_clear(&progress); printf(";; recovery buffer ("); vprintf(what, ap); printf("): " @@ -521,6 +448,7 @@ static ssize_t recovery_read_sectors(struct recoverybuf *r, ssize_t n; assert(off <= r->sz); assert(want <= r->sz - off); + assert(pos == r->pos + off); n = read_sectors(pos, r->buf + off*SECTORSZ, want); return (n); } @@ -532,7 +460,7 @@ static ssize_t recovery_read_buffer(struct recoverybuf *r, ssize_t n; #ifdef DEBUG - progress_clear(); + progress_clear(&progress); show_recovery_buffer_map(r, "begin(%"PRIuSEC", %"PRIuSEC")", pos, want); #endif @@ -552,9 +480,9 @@ static ssize_t recovery_read_buffer(struct recoverybuf *r, #endif } } else if (pos > r->pos + r->end) { - r->pos = pos; r->start = r->end = 0; + 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); @@ -680,7 +608,7 @@ static secaddr run_length_wanted(secaddr pos, secaddr badlen, secaddr end) { secaddr want; - want = clear_factor*badlen; + want = ceil(clear_factor*badlen); if (want < clear_min) want = clear_min; if (want > end - pos) want = end - pos; if (clear_max && want > clear_max) want = clear_max; @@ -701,13 +629,10 @@ static ssize_t find_good_sector(secaddr *pos_inout, secaddr end, badblock_progress.render = render_badblock_progress; progress_additem(&progress, &badblock_progress); - r.buf = buf; r.sz = sz; r.pos = r.start = r.end = 0; - r.good_lo = r.good_hi = 0; - want = sz; if (want > end - pos) want = end - pos; for (retry = 0; retry < max_retries; retry++) { report_bad_blocks_progress(pos, errno); - n = recovery_read(&r, pos, want); + n = read_sectors(pos, buf, want); #ifdef DEBUG progress_clear(&progress); printf(";; [retry] try reading %"PRIuSEC" .. %"PRIuSEC" -> %zd\n", @@ -722,6 +647,9 @@ static ssize_t find_good_sector(secaddr *pos_inout, secaddr end, } } + r.buf = buf; r.sz = sz; r.pos = r.start = r.end = 0; + r.good_lo = r.good_hi = 0; + bad_lo = pos; bad_hi = pos + 1; for (;;) { report_bad_blocks_progress(bad_hi, errno); @@ -737,11 +665,11 @@ static ssize_t find_good_sector(secaddr *pos_inout, secaddr end, 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 @@ -774,16 +702,16 @@ static ssize_t find_good_sector(secaddr *pos_inout, secaddr end, 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); } @@ -870,73 +798,6 @@ static void emit(secaddr start, secaddr end) #undef BUFSECTORS } -struct buf { - char *p; - size_t n, sz; -}; -#define BUF_INIT { 0, 0, 0 } -#define BUF_REWIND(b) do { (b)->n = 0; } while (0) -#define BUF_FREE(b) do { \ - buf *_b = (b); \ - free(_b->p); _b->p = 0; _b->n = _b->sz = 0; \ -} while (0) -#define BUF_PUTC(b, ch) do { \ - struct buf *_b = (b); \ - if (_b->n >= _b->sz) { \ - _b->sz = _b->sz ? 2*_b->sz : 32; \ - _b->p = realloc(_b->p, _b->sz); \ - if (!_b->p) bail("out of memory allocating %zu bytes", _b->sz); \ - } \ - _b->p[_b->n] = (ch); \ -} while (0) - -static int read_line(FILE *fp, struct buf *b) -{ - int ch; - - ch = getc(fp); - if (ch == EOF) - return (-1); - else if (ch != '\n') do { - BUF_PUTC(b, ch); b->n++; - ch = getc(fp); - } while (ch != EOF && ch != '\n'); - BUF_PUTC(b, 0); - 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) @@ -980,11 +841,44 @@ end: 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; @@ -992,15 +886,14 @@ int main(int argc, char *argv[]) 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]; @@ -1010,11 +903,12 @@ int main(int argc, char *argv[]) #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': @@ -1023,23 +917,33 @@ int main(int argc, char *argv[]) (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; @@ -1056,7 +960,7 @@ int main(int argc, char *argv[]) bail_syserr(errno, "failed to open ranges file `%s'", optarg); i = 0; last = -1; for (;;) { - BUF_REWIND(&buf); if (read_line(fp, &buf)) break; + buf_rewind(&buf); if (read_line(fp, &buf)) break; p = buf.p; i++; while (ISSPACE(*p)) p++; if (!*p || *p == '#') continue; @@ -1082,7 +986,7 @@ int main(int argc, char *argv[]) bail_syserr(errno, "failed to open bad-blocks file `%s'", optarg); i = 0; last = -1; for (;;) { - BUF_REWIND(&buf); if (read_line(fp, &buf)) break; + buf_rewind(&buf); if (read_line(fp, &buf)) break; p = buf.p; i++; while (ISSPACE(*p)) p++; if (!*p || *p == '#') continue; @@ -1100,6 +1004,7 @@ int main(int argc, char *argv[]) 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)) @@ -1131,26 +1036,25 @@ int main(int argc, char *argv[]) #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); + if (open_dvd(device, O_RDONLY, &dvdfd, &dvd)) exit(2); + 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) { + if (open_dvd(outfile, O_RDONLY, 0, &dvd_out)) exit(2); + 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); @@ -1183,21 +1087,23 @@ int main(int argc, char *argv[]) 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; @@ -1205,30 +1111,38 @@ int main(int argc, char *argv[]) } } +#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 (nsectors != limit) { + if (nsectors == limit - start) + { ndone = start; nsectors = limit; } + else { disc_progress.render = render_disc_progress; progress_additem(&progress, &disc_progress); } @@ -1297,6 +1211,7 @@ int main(int argc, char *argv[]) if (f&f_stats) { gettimeofday(&tv1, 0); t = tvdiff(&tv0, &tv1); + 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", @@ -1316,5 +1231,5 @@ int main(int argc, char *argv[]) #undef f_stats #undef f_write - return (status); + return (0); }