prog);
}
+#define PRF_HYPHEN 1u
+static int parse_range(const char *p, unsigned f,
+ secaddr *start_out, secaddr *end_out)
+{
+ char *q;
+ int err, rc;
+ unsigned long start, end;
+
+ err = errno;
+
+ if (ISDIGIT(*p)) {
+ start = strtoul(p, &q, 0);
+ if (errno || start >= SECLIMIT) { rc = -1; goto end; }
+ *start_out = start; p = q;
+ } else if (!(f&PRF_HYPHEN))
+ { rc = -1; goto end; }
+ else
+ start = 0;
+
+ if (f&PRF_HYPHEN) {
+ if (*p != '-') { rc = -1; goto end; }
+ p++;
+ } else {
+ if (!ISSPACE(*p)) { rc = -1; goto end; }
+ do p++; while (ISSPACE(*p));
+ }
+
+ if (ISDIGIT(*p)) {
+ end = strtoul(p, &q, 0);
+ if (errno || end > SECLIMIT || end < start) { rc = -1; goto end; }
+ *end_out = end; p = q;
+ } else if (!(f&PRF_HYPHEN))
+ { rc = -1; goto end; }
+
+ if (!(f&PRF_HYPHEN)) while (ISSPACE(*p)) p++;
+ if (*p && ((f&PRF_HYPHEN) || *p != '#')) { rc = -1; goto end; }
+
+ rc = 0;
+end:
+ errno = err;
+ return (rc);
+}
+
#define MAXFILES (1 + 2*99 + 1)
struct file {
ident id;
return (0);
}
+#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
+
typedef uint_least32_t bits;
static bits live[(MAXFILES + 31)/32];
start[0], start[npart - 1] + SECTORS(len[npart - 1]));
}
-static secaddr last_pos, limit, nsectors, ndone;
-static struct timeval last_time;
-static double wsum, wcount;
+static dvd_reader_t *dvd;
+static int dvdfd = -1, outfd = -1;
static struct file *file;
+static dvd_file_t *vob;
+static const char *mapfile; static FILE *mapfp;
+static const char *errfile; static FILE *errfp;
+static secaddr limit;
static secaddr bad_start;
static unsigned retry, max_retries = 4;
+
+static secaddr nsectors, ndone;
+static secaddr last_pos;
+static struct timeval last_time;
+static double alpha = 0.1;
+static double wsum, wcount;
static int bad_err;
static const char throbber[] = "|<-<|>->";
static unsigned throbix = 0;
+static struct progress_item
+ copy_progress, disc_progress,
+ file_progress, badblock_progress;
+
static double scale_bytes(double n, const char **unit_out)
{
const char *unit = "";
if (n > 1600) { n /= 1024; unit = "M"; }
if (n > 1600) { n /= 1024; unit = "G"; }
if (n > 1600) { n /= 1024; unit = "T"; }
-
*unit_out = unit; return (n);
}
-static struct progress_item
- copy_progress, disc_progress,
- file_progress, badblock_progress;
-
#define TIMESTRMAX 16
static char *fmttime(unsigned long t, char *buf)
{
progress_shownotice(render, bg, 7);
}
-static double alpha = 0.1;
-
static void update_progress(secaddr pos)
{
struct timeval now;
static void report_progress(secaddr pos)
{ update_progress(pos); progress_update(&progress); }
-static dvd_reader_t *dvd;
-static int dvdfd = -1, outfd = -1;
-static dvd_file_t *vob;
-static const char *mapfile; static FILE *mapfp;
-static const char *errfile; static FILE *errfp;
-
struct badblock { secaddr start, end; };
DEFVEC(badblock_v, struct badblock);
static badblock_v badblocks = VEC_INIT;
return (!done && errno ? -1 : done);
}
-static void recovered(secaddr bad_lo, secaddr bad_hi)
-{
- char fn[MAXFNSZ];
-
- progress_clear(&progress);
-
- if (!file || id_kind(file->id) == RAW)
- moan("skipping %"PRIuSEC" bad sectors (%"PRIuSEC" .. %"PRIuSEC")",
- bad_hi - bad_lo, bad_lo, bad_hi);
- else {
- store_filename(fn, file->id);
- moan("skipping %"PRIuSEC" bad sectors (%"PRIuSEC" .. %"PRIuSEC"; "
- "`%s' %"PRIuSEC" .. %"PRIuSEC" of %"PRIuSEC")",
- bad_hi - bad_lo, bad_lo, bad_hi,
- fn, bad_lo - file->start, bad_hi - file->start,
- file->end - file->start);
- }
-
- 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");
-
- progress_removeitem(&progress, &badblock_progress);
- progress_update(&progress);
-}
-
struct recoverybuf {
unsigned char *buf;
secaddr sz, pos, start, end;
static void rearrange_sectors(struct recoverybuf *r,
secaddr dest, secaddr src, secaddr len)
{
- assert(dest + len <= r->sz);
- assert(src + len <= r->sz);
+ assert(dest + len <= r->sz); assert(src + len <= r->sz);
memmove(r->buf + dest*SECTORSZ, r->buf + src*SECTORSZ, len*SECTORSZ);
}
static double step_factor = 2.0;
static secaddr step_min = 1, step_max = 0;
+static void recovered(secaddr bad_lo, secaddr bad_hi)
+{
+ char fn[MAXFNSZ];
+
+ progress_clear(&progress);
+
+ if (!file || id_kind(file->id) == RAW)
+ moan("skipping %"PRIuSEC" bad sectors (%"PRIuSEC" .. %"PRIuSEC")",
+ bad_hi - bad_lo, bad_lo, bad_hi);
+ else {
+ store_filename(fn, file->id);
+ moan("skipping %"PRIuSEC" bad sectors (%"PRIuSEC" .. %"PRIuSEC"; "
+ "`%s' %"PRIuSEC" .. %"PRIuSEC" of %"PRIuSEC")",
+ bad_hi - bad_lo, bad_lo, bad_hi,
+ fn, bad_lo - file->start, bad_hi - file->start,
+ file->end - file->start);
+ }
+
+ 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");
+
+ progress_removeitem(&progress, &badblock_progress);
+ progress_update(&progress);
+}
+
static secaddr run_length_wanted(secaddr pos, secaddr badlen, secaddr end)
{
secaddr want;
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",
}
}
+ 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);
#ifdef DEBUG
progress_clear(&progress);
printf(";; bounding bad-block region: "
recovered(bad_lo, end); *pos_inout = end;
return (0);
}
+ report_bad_blocks_progress(bad_hi, errno);
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_hi) step = end - bad_hi;
- pos = bad_hi + step - 1;
+ step += bad_hi - bad_lo;
+ if (step > end - bad_lo) step = end - bad_lo;
want = run_length_wanted(pos, step, end);
n = recovery_read(&r, pos, want);
#ifdef DEBUG
good = pos;
while (good > bad_hi) {
- report_bad_blocks_progress(bad_hi, errno);
#ifdef DEBUG
progress_clear(&progress);
printf(";; limiting bad-block region: "
"%"PRIuSEC" ..%"PRIuSEC".. %"PRIuSEC" ..%"PRIuSEC".. %"PRIuSEC"\n",
bad_lo, bad_hi - bad_lo, bad_hi, good - bad_hi, good);
#endif
+ report_bad_blocks_progress(bad_hi, errno);
pos = bad_hi + (good - bad_hi)/2;
step = pos - bad_lo;
want = run_length_wanted(pos, step, end);
if (n == want) good = pos;
else bad_hi = pos + n + 1;
}
- recovered(bad_lo, bad_hi); *pos_inout = bad_hi;
- if (bad_hi < r.pos + r.start || r.pos + r.end <= bad_hi)
+ recovered(bad_lo, good); *pos_inout = good;
+ if (good < r.pos + r.start || r.pos + r.end <= good)
n = 0;
else {
- n = r.pos + r.end - bad_hi;
- rearrange_sectors(&r, 0, bad_hi - r.pos, n);
+ n = r.pos + r.end - good;
+ rearrange_sectors(&r, 0, good - r.pos, n);
}
#ifdef DEBUG
show_recovery_buffer_map(&r, "returning %zd good sectors at %"PRIuSEC"",
- n, bad_hi);
+ n, good);
#endif
return (n);
}
bail("failed to open %s %u",
id_part(file->id) ? "title" : "menu",
id_title(file->id));
- progress_update(&progress);
break;
default:
abort();
progress_additem(&progress, &file_progress);
}
+ progress_update(&progress);
pos = start;
while (pos < end) {
want = end - pos; if (want > BUFSECTORS) want = BUFSECTORS;
#undef BUFSECTORS
}
-#define PRF_HYPHEN 1u
-static int parse_range(const char *p, unsigned f,
- secaddr *start_out, secaddr *end_out)
-{
- char *q;
- int err, rc;
- unsigned long start, end;
-
- err = errno;
-
- if (ISDIGIT(*p)) {
- start = strtoul(p, &q, 0);
- if (errno || start >= SECLIMIT) { rc = -1; goto end; }
- *start_out = start; p = q;
- } else if (!(f&PRF_HYPHEN))
- { rc = -1; goto end; }
- else
- start = 0;
-
- if (f&PRF_HYPHEN) {
- if (*p != '-') { rc = -1; goto end; }
- p++;
- } else {
- if (!ISSPACE(*p)) { rc = -1; goto end; }
- do p++; while (ISSPACE(*p));
- }
-
- if (ISDIGIT(*p)) {
- end = strtoul(p, &q, 0);
- if (errno || end > SECLIMIT || end < start) { rc = -1; goto end; }
- *end_out = end; p = q;
- } else if (!(f&PRF_HYPHEN))
- { rc = -1; goto end; }
-
- if (!(f&PRF_HYPHEN)) while (ISSPACE(*p)) p++;
- if (*p && ((f&PRF_HYPHEN) || *p != '#')) { rc = -1; goto end; }
-
- rc = 0;
-end:
- errno = err;
- 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;
#define f_fixup 4u
#define f_stats 8u
#define f_checkid 16u
+#define f_retry 32u
#define f_write 256u
set_prog(argv[0]);
else
bail("unknown bad blocks parameter `%s'", p);
if (!*p) break;
- else if (*p != ',') bail("unexpected junk in parameters");
+ if (*p != ',') bail("unexpected junk in parameters");
p++;
}
#undef SKIP_PREFIX
i = 0; last = -1;
for (;;) {
buf_rewind(&buf); if (read_line(fp, &buf)) break;
- p = buf.p; i++;
+ i++; p = buf.p;
while (ISSPACE(*p)) p++;
if (!*p || *p == '#') continue;
if (parse_range(p, 0, &start, &end) ||
}
if (ferror(fp))
bail_syserr(errno, "failed to read ranges file `%s'", optarg);
+ f |= f_retry;
break;
case 'X':
fp = fopen(optarg, "r");
if (parse_range(p, 0, &start, &end) ||
(last <= SECLIMIT && start < last))
bail("bad range `%s' at `%s' line %zu", buf.p, optarg, i);
- if (start < end)
- { VEC_PUSH(bad, &badblocks); bad->start = start; bad->end = end; }
+ if (start < end) {
+ VEC_PUSH(bad, &badblocks);
+ bad->start = start; bad->end = end;
+ }
}
if (ferror(fp))
bail_syserr(errno, "failed to read bad-blocks file `%s'", optarg);
case 'c': f |= f_continue; break;
case 'i': f |= f_checkid; break;
case 'r':
- start = 0; end = -1;
+ start = 0; end = -1; f |= f_retry;
if (parse_range(optarg, PRF_HYPHEN, &start, &end))
bail("bad range `%s'", optarg);
if (start < end) {
if (argc - optind != 2) f |= f_bogus;
if (f&f_bogus) { usage(stderr); exit(2); }
- setlocale(LC_ALL, "");
- progress_init(&progress);
device = argv[optind]; outfile = argv[optind + 1];
if (badblocks.n) {
#endif
}
+ setlocale(LC_ALL, "");
+ progress_init(&progress);
if (open_dvd(device, O_RDONLY, &dvdfd, &dvd)) exit(2);
blksz = SECTORSZ; volsz = device_size(dvdfd, device, &blksz);
if (off < 0)
bail_syserr(errno, "failed to seek to end of output file `%s'",
outfile);
- put_event(EV_WRITE, 0, off/SECTORSZ);
- } else if (!eventq.n && !(f&f_fixup))
+ put_event(EV_WRITE, 0, off/SECTORSZ); f |= f_retry;
+ }
+ if (!(f&(f_retry | f_fixup)))
put_event(EV_WRITE, 0, 0);
/* It's fast enough just to check everything. */
for (i = 0, limit = 0; i < filetab.n; i++)
if (filetab.v[i].end > limit) limit = filetab.v[i].end;
- if (end > limit) end = limit;
-
#ifdef DEBUG
printf("\n;; files:\n");
for (i = 0; i < filetab.n; i++) {
qsort(eventq.v, eventq.n, sizeof(struct event), compare_event);
- f &= ~f_write; start = 0;
- for (i = 0; i < eventq.n; i++) {
+ for (i = 0, f &= ~f_write, start = 0; i < eventq.n; i++) {
ev = &eventq.v[i];
switch (ev->ev) {
case EV_WRITE:
#ifdef DEBUG
dump_eventq("initial");
#endif
- f &= ~f_write; start = 0;
- for (i = 0; i < eventq.n; i++) {
+ for (i = 0, f &= ~f_write, start = last = 0; i < eventq.n; i++) {
ev = &eventq.v[i];
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;
+ if (f&f_fixup) last = ev->pos;
}
eventq.n = i;
#ifdef DEBUG
dump_eventq("trimmed");
#endif
if (f&f_fixup) {
- put_event(EV_WRITE, 0, start);
+ put_event(EV_WRITE, 0, last);
f |= f_write;
}
if (f&f_write) {
#ifdef DEBUG
printf("\n;; event sweep:\n");
#endif
- f &= ~f_write;
- for (pos = 0, i = 0; i < eventq.n; i++) {
+ for (pos = 0, i = 0, f &= ~f_write; i < eventq.n; i++) {
ev = &eventq.v[i];
if (ev->pos > pos) {
if (f&f_write) emit(pos, ev->pos);
"failed to seek to resume position "
"(sector %"PRIuSEC") in output file `%s'",
ev->pos, outfile);
+ f |= f_write;
#ifdef DEBUG
progress_clear(&progress);
printf(";; %8"PRIuSEC": begin write\n", pos);
#endif
- f |= f_write;
break;
case EV_STOP:
f &= ~f_write;