+ n = r->pos + r->end - pos;
+ if (!n && want) n = -1;
+
+end:
+#ifdef DEBUG
+ show_recovery_buffer_map(r, "done; return %zd", n);
+#endif
+ return (n);
+}
+
+static secaddr run_length_wanted(secaddr pos, secaddr badlen,
+ secaddr sz, secaddr end)
+{
+ secaddr want;
+
+ want = 3*badlen/2;
+ if (!want) want = 1;
+ if (want > end - pos) want = end - pos;
+ if (want > sz) want = sz;
+ return (want);
+}
+
+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, want;
+ struct recoverybuf r;
+ ssize_t n;
+
+ r.buf = buf; r.sz = sz; r.pos = r.start = r.end = 0;
+ report_bad_blocks_progress(pos, pos, errno);
+
+ want = sz; if (want > end - pos) want = end - pos;
+ for (i = 0; i < 4; i++) {
+ n = recovery_read(&r, pos, want);
+#ifdef DEBUG
+ clear_progress();
+ printf(";; [retry] try reading %"PRIuSEC" .. %"PRIuSEC" -> %zd\n",
+ pos, pos + want, n);
+#endif
+ if (n > 0) {
+ clear_progress();
+ moan("sector %"PRIuSEC" read ok after retry", pos);
+ return (n);
+ }
+ }
+
+ bad_lo = pos; 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");
+ recovered(bad_lo, end); *pos_inout = end; return (0);
+ }
+ step = 2*(bad_hi - bad_lo); if (step > end - bad_lo) step = end - bad_lo;
+ pos = bad_lo + step - 1;
+ want = run_length_wanted(pos, step, sz, end);
+ n = recovery_read(&r, pos, want);
+#ifdef DEBUG
+ clear_progress();
+ printf(";; [bound] try reading %"PRIuSEC" .. %"PRIuSEC" -> %zd\n",
+ pos, pos + want, n);
+#endif
+ if (n == want) break;
+ if (n < 0) n = 0;
+ bad_hi = pos + n + 1;
+ }
+
+ good = pos;
+ while (good > bad_hi) {
+ report_bad_blocks_progress(bad_lo, bad_hi, errno);
+ pos = bad_hi + (good - bad_hi)/2;
+ step = pos - bad_lo;
+ want = run_length_wanted(pos, step, sz, end);
+ n = recovery_read(&r, pos, want);
+#ifdef DEBUG
+ clear_progress();
+ printf(";; [limit] try reading %"PRIuSEC" .. %"PRIuSEC" -> %zd\n",
+ pos, pos + want, n);
+#endif
+ if (n < 0) n = 0;
+ if (n == want) good = pos;
+ else bad_hi = pos + n + 1;
+ }
+ recovered(bad_lo, bad_hi); *pos_inout = good;
+ if (r.pos + r.start <= good && good <= r.pos + r.end) {
+ rearrange_sectors(&r, 0, good - r.pos, r.pos + r.end - good);
+ } else
+ n = 0;