- if (best && pos + want > best->start) want = best->start - pos;
-
-again:
- if (vob)
- n = DVDReadBlocks(vob, pos - file->start, want, buf);
- else if (file) {
- if (lseek(dvdfd, (off_t)pos*SECTORSZ, SEEK_SET) < 0)
- bail_syserr(errno, "failed to seek to sector %"PRIuSEC"", pos);
- n = read(dvdfd, buf, want*SECTORSZ);
- if (n >= 0) n /= SECTORSZ;
- } else {
- memset(buf, 0, want*SECTORSZ);
- n = want;
+ if (best && pos + want > best->start)
+ { want = best->start - pos; fakeerr = EIO; }
+
+ done = 0;
+ while (want) {
+ if (vob)
+ { errno = 0; n = DVDReadBlocks(vob, pos - file->start, want, p); }
+ else if (file) {
+ if (lseek(dvdfd, (off_t)pos*SECTORSZ, SEEK_SET) < 0)
+ bail_syserr(errno, "failed to seek to sector %"PRIuSEC"", pos);
+ errno = 0; n = read(dvdfd, p, want*SECTORSZ);
+ if (n >= 0) n /= SECTORSZ;
+ } else {
+ memset(p, 0, want*SECTORSZ);
+ n = want;
+ }
+
+ if (n > 0) { done += n; pos += n; p += n*SECTORSZ; want -= n; }
+ else if (!n || errno != EINTR) break;
+ }
+ if (fakeerr && !errno) errno = fakeerr;
+ return (!done && errno ? -1 : done);
+}
+
+static ssize_t find_good_sector(secaddr *pos_inout, secaddr end,
+ unsigned char *buf, secaddr sz)
+{
+ int i;
+ secaddr pos = *pos_inout, bad_lo, bad_hi, good, step;
+ ssize_t n;
+
+ report_bad_blocks_progress(pos, pos, errno);
+ for (i = 0; i < 4; i++) {
+ n = read_sectors(pos, buf, 1);
+ if (n > 0) {
+ clear_progress();
+ moan("sector %"PRIuSEC" read ok after retry", pos);
+ bad_lo = bad_hi = pos;
+ goto recovered;
+ }
+ }
+
+ bad_lo = pos; step = 1; bad_hi = pos + 1;
+ for (;;) {
+ report_bad_blocks_progress(bad_lo, bad_hi, errno);
+ if (bad_hi >= end) {
+ clear_progress();
+ moan("giving up on this extent");
+ n = 0; goto recovered;
+ }
+ step *= 2;
+ if (step > end - bad_lo) step = end - bad_lo;
+ pos = bad_lo + step - 1;
+ n = read_sectors(pos, buf, 1);
+ if (n > 0) break;
+ bad_hi = pos + 1;