3 static void usage(FILE *fp
)
6 "usage: %s [-ci] [-B PARAM=VALUE,...] [-R MAP]\n"
7 "\t[-b OUTMAP] [-r [START]-[END]] DEVICE OUTFILE\n",
11 static double tvdiff(const struct timeval
*tv_lo
,
12 const struct timeval
*tv_hi
)
14 return ((tv_hi
->tv_sec
- tv_lo
->tv_sec
) +
15 (tv_hi
->tv_usec
- tv_lo
->tv_usec
)/1.0e6
);
18 #define DEFVEC(vtype, etype) \
19 typedef struct { etype *v; size_t n, sz; } vtype
20 #define VEC_INIT { 0, 0, 0 }
21 #define VEC_FREE(vv) do { \
22 free((vv)->v); (vv)->v 0; (vv)->n = (vv)->sz = 0; \
24 #define VEC_PUSH(p, vv) do { \
26 if ((vv)->n >= (vv)->sz) { \
27 (vv)->sz = (vv)->sz ? 2*(vv)->sz : 32; \
28 _want = (vv)->sz*sizeof(*(vv)->v); \
29 (vv)->v = realloc((vv)->v, _want); \
30 if (!(vv)->v) bail("out of memory allocating %zu bytes", _want); \
32 (p) = &(vv)->v[(vv)->n++]; \
35 #define MAXFILES (1 + 2*99 + 1)
40 DEFVEC(file_v
, struct file
);
41 static file_v filetab
= VEC_INIT
;
43 enum { EV_STOP
, EV_BEGIN
, EV_END
, EV_WRITE
};
45 unsigned char ev
, file
;
48 DEFVEC(event_v
, struct event
);
49 static event_v eventq
= VEC_INIT
;
51 static int compare_event(const void *a
, const void *b
)
53 const struct event
*eva
= a
, *evb
= b
;
55 if (eva
->pos
< evb
->pos
) return (-1);
56 else if (eva
->pos
> evb
->pos
) return (+1);
58 if (eva
->ev
< evb
->ev
) return (-1);
59 else if (eva
->ev
> evb
->ev
) return (+1);
61 if (eva
->file
< evb
->file
) return (-1);
62 else if (eva
->file
> evb
->file
) return (+1);
67 typedef uint_least32_t bits
;
68 static bits live
[(MAXFILES
+ 31)/32];
70 static inline int livep(unsigned i
)
71 { return (live
[i
/32]&((bits
)1 << (i
%32))); }
72 static inline void set_live(unsigned i
)
73 { live
[i
/32] |= (bits
)1 << (i
%32); }
74 static inline void clear_live(unsigned i
)
75 { live
[i
/32] &= ~((bits
)1 << (i
%32)); }
76 static inline int least_live(void)
78 unsigned i
, n
= (filetab
.n
+ 32)/32;
81 for (i
= 0; i
< n
; i
++) { b
= live
[i
]; if (b
) goto found
; }
85 if (!(b
&0x0000ffff)) { b
>>= 16; i
+= 16; }
86 if (!(b
&0x000000ff)) { b
>>= 8; i
+= 8; }
87 if (!(b
&0x0000000f)) { b
>>= 4; i
+= 4; }
88 if (!(b
&0x00000003)) { b
>>= 2; i
+= 2; }
89 if (!(b
&0x00000001)) { b
>>= 1; i
+= 1; }
94 static void put_event(unsigned evtype
, unsigned file
, secaddr pos
)
98 VEC_PUSH(ev
, &eventq
);
99 ev
->ev
= evtype
; ev
->file
= file
; ev
->pos
= pos
;
102 static void put_file(ident id
, secaddr start
, secaddr end
)
107 VEC_PUSH(f
, &filetab
); i
= f
- filetab
.v
;
108 f
->id
= id
; f
->start
= start
; f
->end
= end
;
109 put_event(EV_BEGIN
, i
, start
);
110 put_event(EV_END
, i
, end
);
113 static void put_menu(dvd_reader_t
*dvd
, unsigned title
)
115 ident id
= mkident(VOB
, title
, 0);
119 store_filename(fn
, id
);
120 start
= UDFFindFile(dvd
, fn
, &len
); if (!start
) return;
122 printf(";; %8"PRIuSEC
" .. %-8"PRIuSEC
": %s\n",
123 start
, start
+ SECTORS(len
), fn
);
125 put_file(id
, start
, start
+ SECTORS(len
));
128 static void put_title(dvd_reader_t
*dvd
, unsigned title
)
131 secaddr start
[9], len
[9];
134 for (i
= 0; i
< 9; i
++) {
135 store_filename(fn
, mkident(VOB
, title
, i
+ 1));
136 start
[i
] = UDFFindFile(dvd
, fn
, &len
[i
]); if (!start
[i
]) break;
138 npart
= i
; if (!npart
) return;
141 for (i
= 0; i
< npart
; i
++) {
142 store_filename(fn
, mkident(VOB
, title
, i
+ 1));
143 printf(";; %8"PRIuSEC
" .. %-8"PRIuSEC
": %s\n",
144 start
[i
], start
[i
] + SECTORS(len
[i
]), fn
);
149 for (i
= 0; i
< npart
- 1; i
++) {
151 bail("title %u part %u length = %"PRIuSEC
" not a multiple of %d",
152 title
, i
, len
[i
], SECTORSZ
);
153 if (start
[i
] + len
[i
]/SECTORSZ
!= start
[i
+ 1])
155 ("title %u part %u end = %"PRIuSEC
" /= part %u start = %"PRIuSEC
"",
156 title
, i
, start
[i
] + len
[i
]/SECTORSZ
, i
+ 1, start
[i
+ 1]);
159 put_file(mkident(VOB
, title
, 1),
160 start
[0], start
[npart
- 1] + SECTORS(len
[npart
- 1]));
163 static secaddr last_pos
, limit
, nsectors
, ndone
;
164 static struct timeval last_time
;
165 static double wsum
, wcount
;
166 static struct file
*file
;
167 static secaddr bad_start
;
168 static unsigned retry
, max_retries
= 4;
171 static const char throbber
[] = "|<-<|>->";
172 static unsigned throbix
= 0;
174 static double scale_bytes(double n
, const char **unit_out
)
176 const char *unit
= "";
178 if (n
> 1600) { n
/= 1024; unit
= "k"; }
179 if (n
> 1600) { n
/= 1024; unit
= "M"; }
180 if (n
> 1600) { n
/= 1024; unit
= "G"; }
181 if (n
> 1600) { n
/= 1024; unit
= "T"; }
183 *unit_out
= unit
; return (n
);
186 static struct progress_item
187 copy_progress
, disc_progress
,
188 file_progress
, badblock_progress
;
190 #define TIMESTRMAX 16
191 static char *fmttime(unsigned long t
, char *buf
)
193 if (t
< 60) sprintf(buf
, "%ld s", t
);
194 else if (t
< 3600) sprintf(buf
, "%ld:%02ld", t
/60, t
%60);
195 else sprintf(buf
, "%ld:%02ld:%02ld", t
/3600, (t
/60)%60, t
%60);
199 static void render_perfstats(struct progress_render_state
*render
)
202 char timebuf
[TIMESTRMAX
];
206 if (!wsum
|| !wcount
) { rate
= 0; eta
= -1; }
207 else { rate
= wsum
/wcount
; eta
= (int)((nsectors
- ndone
)/rate
+ 0.5); }
209 rate
= scale_bytes(rate
*SECTORSZ
, &unit
);
210 progress_putright(render
, "ETA %s ", rate ?
fmttime(eta
, timebuf
) : "???");
211 progress_putright(render
, "%.1f %sB/s, ", rate
, unit
);
214 static void render_copy_progress(struct progress_item
*item
,
215 struct progress_render_state
*render
)
217 double frac
= (double)ndone
/nsectors
;
219 progress_putleft(render
, " %c copied %.1f%%",
220 throbber
[throbix
], 100.0*frac
);
221 render_perfstats(render
);
222 progress_putleft(render
, " (%"PRIuSEC
" of %"PRIuSEC
")", ndone
, nsectors
);
224 progress_showbar(render
, frac
);
227 static void render_disc_progress(struct progress_item
*item
,
228 struct progress_render_state
*render
)
230 double frac
= (double)last_pos
/limit
;
232 progress_putleft(render
, " disc %.1f%% (%"PRIuSEC
" of %"PRIuSEC
")",
233 100.0*frac
, last_pos
, limit
);
234 progress_showbar(render
, frac
);
237 static void render_file_progress(struct progress_item
*item
,
238 struct progress_render_state
*render
)
240 secaddr off
= last_pos
- file
->start
, len
= file
->end
- file
->start
;
244 store_filename(fn
, file
->id
);
245 frac
= (double)off
/len
;
246 progress_putleft(render
, " `%s' %.1f%% (%"PRIuSEC
" of %"PRIuSEC
")",
247 fn
, 100.0*frac
, off
, len
);
248 progress_showbar(render
, frac
);
251 static void render_badblock_progress(struct progress_item
*item
,
252 struct progress_render_state
*render
)
254 secaddr n
= last_pos
- bad_start
;
258 progress_putleft(render
, " Retrying bad sector %"PRIuSEC
"", bad_start
);
259 progress_putright(render
, "attempt %u/%u ", retry
+ 1, max_retries
);
262 progress_putleft(render
, " Found %"PRIuSEC
" bad %s",
263 n
, n
== 1 ?
"sector" : "sectors");
264 progress_putright(render
, "%"PRIuSEC
" .. %"PRIuSEC
" ",
265 bad_start
, last_pos
);
268 if (bad_err
&& bad_err
!= EIO
)
269 progress_putleft(render
, " (%s)", strerror(bad_err
));
270 progress_shownotice(render
, bg
, 7);
273 static double alpha
= 0.1;
275 static void update_progress(secaddr pos
)
280 gettimeofday(&now
, 0);
281 t
= tvdiff(&last_time
, &now
);
283 #define BETA (1 - alpha)
286 g
= wcount ?
pow(BETA
, t
) : 0.0; f
= (1 - g
)/(1 - BETA
);
287 wsum
= f
*(pos
- last_pos
)/t
+ g
*wsum
;
288 wcount
= f
+ g
*wcount
;
289 ndone
+= pos
- last_pos
;
290 last_time
= now
; last_pos
= pos
;
295 throbix
++; if (!throbber
[throbix
]) throbix
= 0;
298 static void report_progress(secaddr pos
)
299 { update_progress(pos
); progress_update(&progress
); }
301 static dvd_reader_t
*dvd
;
302 static int dvdfd
= -1, outfd
= -1;
303 static dvd_file_t
*vob
;
304 static const char *mapfile
; static FILE *mapfp
;
305 static const char *errfile
; static FILE *errfp
;
307 struct badblock
{ secaddr start
, end
; };
308 DEFVEC(badblock_v
, struct badblock
);
309 static badblock_v badblocks
= VEC_INIT
;
311 static int compare_badblock(const void *a
, const void *b
)
313 const struct badblock
*ba
= a
, *bb
= b
;
315 if (ba
->start
< bb
->start
) return (-1);
316 else if (ba
->start
> bb
->start
) return (+1);
318 if (ba
->end
< bb
->end
) return (-1);
319 else if (ba
->end
> bb
->end
) return (+1);
324 static double bad_block_delay
= 0.0;
325 static double good_block_delay
= 0.0;
327 static ssize_t
read_sectors(secaddr pos
, void *buf
, secaddr want
)
332 struct badblock
*bad
, *best
;
333 unsigned char *p
= buf
;
336 best
= 0; lo
= 0; hi
= badblocks
.n
;
338 progress_clear(&progress
);
339 printf(";; searching badblocks for %"PRIuSEC
" .. %"PRIuSEC
"\n",
343 mid
= lo
+ (hi
- lo
)/2; bad
= &badblocks
.v
[mid
];
345 printf(";; try %zu (%"PRIuSEC
" .. %"PRIuSEC
")... ",
346 mid
, bad
->start
, bad
->end
);
348 if (pos
< bad
->start
) { D( printf("high\n"); ) best
= bad
; hi
= mid
; }
349 else if (pos
>= bad
->end
) { D( printf("low\n"); ) lo
= mid
+ 1; }
350 else { D( printf("match!\n"); ) errno
= EIO
; return (-1); }
354 printf(";; next is %"PRIuSEC
" .. %"PRIuSEC
"\n",
355 best
->start
, best
->end
);
357 if (best
&& pos
+ want
> best
->start
)
358 { want
= best
->start
- pos
; fakeerr
= EIO
; sit(bad_block_delay
); }
363 { errno
= 0; n
= DVDReadBlocks(vob
, pos
- file
->start
, want
, p
); }
365 if (lseek(dvdfd
, (off_t
)pos
*SECTORSZ
, SEEK_SET
) < 0)
366 bail_syserr(errno
, "failed to seek to sector %"PRIuSEC
"", pos
);
367 errno
= 0; n
= read(dvdfd
, p
, want
*SECTORSZ
);
368 if (n
>= 0) n
/= SECTORSZ
;
370 memset(p
, 0, want
*SECTORSZ
);
374 if (n
> 0) { done
+= n
; pos
+= n
; p
+= n
*SECTORSZ
; want
-= n
; }
376 else if (errno
== EIO
&& errfile
) {
377 open_file_on_demand(errfile
, &errfp
, "bad-sector error log");
378 fprintf(errfp
, "%"PRIuSEC
" %"PRIuSEC
"\n", pos
, pos
+ 1);
379 check_write(errfp
, "bad-sector error log");
381 } else if (errno
!= EINTR
) break;
383 if (fakeerr
&& !errno
) errno
= fakeerr
;
384 else if (done
> 0 && good_block_delay
) sit(done
*good_block_delay
);
385 return (!done
&& errno ?
-1 : done
);
388 static void record_bad_sectors(secaddr bad_lo
, secaddr bad_hi
)
392 if (!mapfile
) return;
394 open_file_on_demand(mapfile
, &mapfp
, "bad-sector region map");
395 fprintf(mapfp
, "%"PRIuSEC
" %"PRIuSEC
" # %"PRIuSEC
" sectors",
396 bad_lo
, bad_hi
, bad_hi
- bad_lo
);
398 if (file
&& id_kind(file
->id
) != RAW
) {
399 store_filename(fn
, file
->id
);
400 fprintf(mapfp
, "; `%s' %"PRIuSEC
" .. %"PRIuSEC
" of %"PRIuSEC
"",
401 fn
, bad_lo
- file
->start
, bad_hi
- file
->start
,
402 file
->end
- file
->start
);
406 check_write(mapfp
, "bad-sector region map");
409 static void recovered(secaddr bad_lo
, secaddr bad_hi
)
413 progress_clear(&progress
);
415 if (!file
|| id_kind(file
->id
) == RAW
)
416 moan("skipping %"PRIuSEC
" bad sectors (%"PRIuSEC
" .. %"PRIuSEC
")",
417 bad_hi
- bad_lo
, bad_lo
, bad_hi
);
419 store_filename(fn
, file
->id
);
420 moan("skipping %"PRIuSEC
" bad sectors (%"PRIuSEC
" .. %"PRIuSEC
"; "
421 "`%s' %"PRIuSEC
" .. %"PRIuSEC
" of %"PRIuSEC
")",
422 bad_hi
- bad_lo
, bad_lo
, bad_hi
,
423 fn
, bad_lo
- file
->start
, bad_hi
- file
->start
,
424 file
->end
- file
->start
);
427 record_bad_sectors(bad_lo
, bad_hi
);
429 if (lseek(outfd
, (off_t
)(bad_hi
- bad_lo
)*SECTORSZ
, SEEK_CUR
) < 0)
430 bail_syserr(errno
, "failed to seek past bad sectors");
432 progress_removeitem(&progress
, &badblock_progress
);
433 progress_update(&progress
);
438 secaddr sz
, pos
, start
, end
;
439 secaddr good_lo
, good_hi
;
442 static void rearrange_sectors(struct recoverybuf
*r
,
443 secaddr dest
, secaddr src
, secaddr len
)
445 assert(dest
+ len
<= r
->sz
);
446 assert(src
+ len
<= r
->sz
);
447 memmove(r
->buf
+ dest
*SECTORSZ
, r
->buf
+ src
*SECTORSZ
, len
*SECTORSZ
);
451 static PRINTF_LIKE(2, 3)
452 void show_recovery_buffer_map(const struct recoverybuf
*r
,
453 const char *what
, ...)
458 progress_clear(&progress
);
459 printf(";; recovery buffer (");
462 "(%"PRIuSEC
") ..%"PRIuSEC
".. "
463 "[%"PRIuSEC
" ..%"PRIuSEC
".. %"PRIuSEC
"] "
464 "..%"PRIuSEC
".. (%"PRIuSEC
")\n",
466 r
->pos
+ r
->start
, r
->end
- r
->start
, r
->pos
+ r
->end
,
467 r
->sz
- r
->end
, r
->pos
+ r
->sz
);
469 assert(r
->start
<= r
->end
);
470 assert(r
->end
<= r
->sz
);
474 static ssize_t
recovery_read_sectors(struct recoverybuf
*r
,
475 secaddr pos
, secaddr off
, secaddr want
)
479 assert(off
<= r
->sz
); assert(want
<= r
->sz
- off
);
480 n
= read_sectors(pos
, r
->buf
+ off
*SECTORSZ
, want
);
484 static ssize_t
recovery_read_buffer(struct recoverybuf
*r
,
485 secaddr pos
, secaddr want
)
487 secaddr diff
, pp
, nn
;
491 progress_clear(&progress
);
492 show_recovery_buffer_map(r
, "begin(%"PRIuSEC
", %"PRIuSEC
")", pos
, want
);
497 if (r
->start
+ diff
>= r
->sz
) {
498 r
->pos
= pos
; r
->start
= r
->end
= 0;
500 show_recovery_buffer_map(r
, "cleared; shift up by %"PRIuSEC
"", diff
);
503 if (r
->end
+ diff
> r
->sz
) r
->end
= r
->sz
- diff
;
504 rearrange_sectors(r
, r
->start
+ diff
, r
->start
, r
->end
- r
->start
);
505 r
->pos
-= diff
; r
->start
+= diff
; r
->end
+= diff
;
507 show_recovery_buffer_map(r
, "shifted up by %"PRIuSEC
"", diff
);
510 } else if (pos
> r
->pos
+ r
->end
) {
511 r
->pos
= pos
; r
->start
= r
->end
= 0;
513 show_recovery_buffer_map(r
, "cleared; beyond previous region");
515 } else if (pos
+ want
> r
->pos
+ r
->sz
) {
516 diff
= (pos
+ want
) - (r
->pos
+ r
->sz
);
517 if (r
->end
<= diff
) {
518 r
->pos
= pos
; r
->start
= r
->end
= 0;
520 show_recovery_buffer_map(r
, "cleared; shift down by %"PRIuSEC
"", diff
);
523 if (r
->start
< diff
) r
->start
= diff
;
524 rearrange_sectors(r
, r
->start
- diff
, r
->start
, r
->end
- r
->start
);
525 r
->pos
+= diff
; r
->start
-= diff
; r
->end
-= diff
;
527 show_recovery_buffer_map(r
, "shifted down by %"PRIuSEC
"", diff
);
532 if (pos
< r
->pos
+ r
->start
) {
533 pp
= pos
- r
->pos
; nn
= r
->start
- pp
;
535 printf(";; read low (%"PRIuSEC
"@%"PRIuSEC
", %"PRIuSEC
")", pos
, pp
, nn
);
538 n
= recovery_read_sectors(r
, pos
, pp
, nn
);
540 printf(" -> %zd\n", n
);
543 if (n
>= 0 && n
> want
) n
= want
;
548 show_recovery_buffer_map(r
, "joined new region");
552 if (pos
+ want
> r
->pos
+ r
->end
) {
553 pp
= r
->end
; nn
= (pos
+ want
) - (r
->pos
+ r
->end
);
555 printf(";; read high (%"PRIuSEC
"@%"PRIuSEC
", %"PRIuSEC
")",
556 r
->pos
+ pp
, pp
, nn
);
559 n
= recovery_read_sectors(r
, r
->pos
+ pp
, pp
, nn
);
561 printf(" -> %zd\n", n
);
566 show_recovery_buffer_map(r
, "joined new region");
571 n
= r
->pos
+ r
->end
- pos
;
572 if (!n
&& want
) n
= -1;
573 else if (n
> want
) n
= want
;
577 show_recovery_buffer_map(r
, "done; return %zd", n
);
582 static ssize_t
recovery_read_multiple(struct recoverybuf
*r
,
583 secaddr pos
, secaddr want
)
586 secaddr skip
, want0
= want
;
588 while (want
> r
->sz
) {
590 n
= recovery_read_buffer(r
, pos
+ skip
, r
->sz
);
591 if (n
< r
->sz
) return (skip
+ (n
>= 0 ? n
: 0));
594 n
= recovery_read_buffer(r
, pos
, want
);
595 if (n
< 0 || n
< want
) return (n
);
599 static ssize_t
recovery_read(struct recoverybuf
*r
,
600 secaddr pos
, secaddr want
)
602 secaddr lo
= pos
, hi
= pos
+ want
, span
;
605 if (hi
< r
->good_lo
|| lo
> r
->good_hi
) {
606 n
= recovery_read_multiple(r
, lo
, hi
- lo
);
607 if (n
> 0) { r
->good_lo
= lo
; r
->good_hi
= lo
+ n
; }
611 if (hi
> r
->good_hi
) {
612 span
= hi
- r
->good_hi
;
613 n
= recovery_read_multiple(r
, r
->good_hi
, span
);
614 if (n
> 0) r
->good_hi
+= n
;
615 if (n
< 0 || n
< span
) return (r
->good_hi
- lo
);
618 if (lo
< r
->good_lo
) {
619 span
= r
->good_lo
- lo
;
620 n
= recovery_read_multiple(r
, lo
, span
);
621 if (n
== span
) r
->good_lo
= lo
;
625 n
= r
->good_hi
- pos
; if (n
> want
) n
= want
;
626 if (!n
) { errno
= EIO
; n
= -1; }
630 static double clear_factor
= 1.5;
631 static secaddr clear_min
= 1, clear_max
= SECLIMIT
;
632 static double step_factor
= 2.0;
633 static secaddr step_min
= 1, step_max
= 0;
635 static secaddr
run_length_wanted(secaddr pos
, secaddr badlen
, secaddr end
)
639 want
= clear_factor
*badlen
;
640 if (want
< clear_min
) want
= clear_min
;
641 if (want
> end
- pos
) want
= end
- pos
;
642 if (clear_max
&& want
> clear_max
) want
= clear_max
;
646 static void report_bad_blocks_progress(secaddr bad_hi
, int err
)
647 { bad_err
= err
; report_progress(bad_hi
); }
649 static ssize_t
find_good_sector(secaddr
*pos_inout
, secaddr end
,
650 unsigned char *buf
, secaddr sz
)
652 secaddr pos
= *pos_inout
, bad_lo
, bad_hi
, good
, step
, want
;
653 struct recoverybuf r
;
656 bad_start
= pos
; bad_err
= errno
;
657 badblock_progress
.render
= render_badblock_progress
;
658 progress_additem(&progress
, &badblock_progress
);
660 r
.buf
= buf
; r
.sz
= sz
; r
.pos
= r
.start
= r
.end
= 0;
661 r
.good_lo
= r
.good_hi
= 0;
663 want
= sz
; if (want
> end
- pos
) want
= end
- pos
;
664 for (retry
= 0; retry
< max_retries
; retry
++) {
665 report_bad_blocks_progress(pos
, errno
);
666 n
= recovery_read(&r
, pos
, want
);
668 progress_clear(&progress
);
669 printf(";; [retry] try reading %"PRIuSEC
" .. %"PRIuSEC
" -> %zd\n",
673 progress_clear(&progress
);
674 moan("sector %"PRIuSEC
" read ok after retry", pos
);
675 progress_removeitem(&progress
, &badblock_progress
);
676 progress_update(&progress
);
681 bad_lo
= pos
; bad_hi
= pos
+ 1;
683 report_bad_blocks_progress(bad_hi
, errno
);
685 progress_clear(&progress
);
686 printf(";; bounding bad-block region: "
687 "%"PRIuSEC
" ..%"PRIuSEC
".. %"PRIuSEC
"\n",
688 bad_lo
, bad_hi
- bad_lo
, bad_hi
);
691 progress_clear(&progress
);
692 moan("giving up on this extent");
693 recovered(bad_lo
, end
); *pos_inout
= end
;
696 step
= (step_factor
- 1)*(bad_hi
- bad_lo
);
697 if (step
< step_min
) step
= step_min
;
698 if (step_max
&& step
> step_max
) step
= step_max
;
699 if (step
> end
- bad_hi
) step
= end
- bad_hi
;
700 pos
= bad_hi
+ step
- 1;
701 want
= run_length_wanted(pos
, step
, end
);
702 n
= recovery_read(&r
, pos
, want
);
704 printf(";; [bound] try reading %"PRIuSEC
" .. %"PRIuSEC
" -> %zd\n",
707 if (n
== want
) break;
709 bad_hi
= pos
+ n
+ 1;
713 while (good
> bad_hi
) {
714 report_bad_blocks_progress(bad_hi
, errno
);
716 progress_clear(&progress
);
717 printf(";; limiting bad-block region: "
718 "%"PRIuSEC
" ..%"PRIuSEC
".. %"PRIuSEC
" ..%"PRIuSEC
".. %"PRIuSEC
"\n",
719 bad_lo
, bad_hi
- bad_lo
, bad_hi
, good
- bad_hi
, good
);
721 pos
= bad_hi
+ (good
- bad_hi
)/2;
723 want
= run_length_wanted(pos
, step
, end
);
724 n
= recovery_read(&r
, pos
, want
);
726 printf(";; [limit] try reading %"PRIuSEC
" .. %"PRIuSEC
" -> %zd\n",
730 if (n
== want
) good
= pos
;
731 else bad_hi
= pos
+ n
+ 1;
733 recovered(bad_lo
, bad_hi
); *pos_inout
= bad_hi
;
734 if (bad_hi
< r
.pos
+ r
.start
|| r
.pos
+ r
.end
<= bad_hi
)
737 n
= r
.pos
+ r
.end
- bad_hi
;
738 rearrange_sectors(&r
, 0, bad_hi
- r
.pos
, n
);
741 show_recovery_buffer_map(&r
, "returning %zd good sectors at %"PRIuSEC
"",
747 static void emit(secaddr start
, secaddr end
)
749 #define BUFSECTORS 512
752 unsigned char buf
[BUFSECTORS
*SECTORSZ
];
756 static int first_time
= 1;
764 least
= least_live();
767 printf(";; %8"PRIuSEC
" .. %"PRIuSEC
"\n", start
, end
);
769 for (i
= 0; i
< filetab
.n
; i
++) {
770 if (!livep(i
)) continue;
771 if (act
== -1) act
= i
;
772 f
= &filetab
.v
[i
]; store_filename(fn
, f
->id
);
773 printf(";;\t\t%8"PRIuSEC
" .. %-8"PRIuSEC
" %s\n",
774 start
- f
->start
, end
- f
->start
, fn
);
776 if (act
== -1) printf(";;\t\t#<no live source>\n");
777 assert(act
== least
);
781 { file
= 0; vob
= 0; }
783 file
= &filetab
.v
[least
];
784 switch (id_kind(file
->id
)) {
789 if (first_time
) { progress_clear(&progress
); first_time
= 0; }
790 vob
= DVDOpenFile(dvd
, id_title(file
->id
),
792 ? DVD_READ_TITLE_VOBS
793 : DVD_READ_MENU_VOBS
);
795 bail("failed to open %s %u",
796 id_part(file
->id
) ?
"title" : "menu",
798 progress_update(&progress
);
805 if (file
&& id_kind(file
->id
) != RAW
) {
806 file_progress
.render
= render_file_progress
;
807 progress_additem(&progress
, &file_progress
);
812 want
= end
- pos
; if (want
> BUFSECTORS
) want
= BUFSECTORS
;
813 n
= read_sectors(pos
, buf
, want
);
815 if (n
<= 0) n
= find_good_sector(&pos
, end
, buf
, BUFSECTORS
);
816 if (n
> 0) { carefully_write(outfd
, buf
, n
*SECTORSZ
); pos
+= n
; }
817 report_progress(pos
);
820 if (vob
) { DVDCloseFile(vob
); vob
= 0; }
822 if (file
&& id_kind(file
->id
) != RAW
)
823 progress_removeitem(&progress
, &file_progress
);
824 progress_update(&progress
);
833 #define BUF_INIT { 0, 0, 0 }
834 #define BUF_REWIND(b) do { (b)->n = 0; } while (0)
835 #define BUF_FREE(b) do { \
837 free(_b->p); _b->p = 0; _b->n = _b->sz = 0; \
839 #define BUF_PUTC(b, ch) do { \
840 struct buf *_b = (b); \
841 if (_b->n >= _b->sz) { \
842 _b->sz = _b->sz ? 2*_b->sz : 32; \
843 _b->p = realloc(_b->p, _b->sz); \
844 if (!_b->p) bail("out of memory allocating %zu bytes", _b->sz); \
846 _b->p[_b->n] = (ch); \
849 static int read_line(FILE *fp
, struct buf
*b
)
856 else if (ch
!= '\n') do {
857 BUF_PUTC(b
, ch
); b
->n
++;
859 } while (ch
!= EOF
&& ch
!= '\n');
864 #define PRF_HYPHEN 1u
865 static int parse_range(const char *p
, unsigned f
,
866 secaddr
*start_out
, secaddr
*end_out
)
870 unsigned long start
, end
;
875 start
= strtoul(p
, &q
, 0);
876 if (errno
|| start
>= SECLIMIT
) { rc
= -1; goto end
; }
877 *start_out
= start
; p
= q
;
878 } else if (!(f
&PRF_HYPHEN
))
879 { rc
= -1; goto end
; }
884 if (*p
!= '-') { rc
= -1; goto end
; }
887 if (!ISSPACE(*p
)) { rc
= -1; goto end
; }
888 do p
++; while (ISSPACE(*p
));
892 end
= strtoul(p
, &q
, 0);
893 if (errno
|| end
> SECLIMIT
|| end
< start
) { rc
= -1; goto end
; }
894 *end_out
= end
; p
= q
;
895 } else if (!(f
&PRF_HYPHEN
))
896 { rc
= -1; goto end
; }
898 if (!(f
&PRF_HYPHEN
)) while (ISSPACE(*p
)) p
++;
899 if (*p
&& ((f
&PRF_HYPHEN
) || *p
!= '#')) { rc
= -1; goto end
; }
908 static void dump_eventq(const char *what
)
911 const struct event
*ev
;
914 printf("\n;; event dump (%s):\n", what
);
915 for (i
= 0; i
< eventq
.n
; i
++) {
919 store_filename(fn
, filetab
.v
[ev
->file
].id
);
920 printf(";; %8"PRIuSEC
": begin %s\n", ev
->pos
, fn
);
923 store_filename(fn
, filetab
.v
[ev
->file
].id
);
924 printf(";; %8"PRIuSEC
": end %s\n", ev
->pos
, fn
);
927 printf(";; %8"PRIuSEC
": write\n", ev
->pos
);
930 printf(";; %8"PRIuSEC
": stop\n", ev
->pos
);
933 printf(";; %8"PRIuSEC
": ?%u\n", ev
->pos
, ev
->ev
);
940 int main(int argc
, char *argv
[])
947 secaddr start
, end
, last
;
948 const struct event
*ev
;
949 const char *device
, *outfile
;
950 struct badblock
*bad
;
954 struct buf buf
= BUF_INIT
;
955 struct timeval tv0
, tv1
;
957 const char *rateunit
, *totunit
;
958 char timebuf
[TIMESTRMAX
], id_in
[MAXIDSZ
], id_out
[MAXIDSZ
];
959 dvd_reader_t
*dvd_out
;
961 const struct file
*file
;
966 #define f_continue 2u
969 #define f_checkid 16u
974 opt
= getopt(argc
, argv
, "hB:E:FR:X:b:cir:s"); if (opt
< 0) break;
976 case 'h': usage(stderr
); exit(0);
979 #define SKIP_PREFIX(s) \
980 (STRNCMP(p, ==, s "=", sizeof(s)) && (p += sizeof(s), 1))
982 if (SKIP_PREFIX("cf"))
983 clear_factor
= parse_float(&p
, PNF_JUNK
, 0, DBL_MAX
,
985 else if (SKIP_PREFIX("cmin"))
986 clear_min
= parse_int(&p
, PNF_JUNK
, 1, SECLIMIT
,
988 else if (SKIP_PREFIX("cmax"))
989 clear_max
= parse_int(&p
, PNF_JUNK
, 1, SECLIMIT
,
991 else if (SKIP_PREFIX("sf"))
992 step_factor
= parse_float(&p
, PNF_JUNK
, 0, DBL_MAX
,
994 else if (SKIP_PREFIX("smin"))
995 step_min
= parse_int(&p
, PNF_JUNK
, 1, SECLIMIT
- 1,
997 else if (SKIP_PREFIX("smax"))
998 step_max
= parse_int(&p
, PNF_JUNK
, 1, SECLIMIT
- 1,
1000 else if (SKIP_PREFIX("retry"))
1001 max_retries
= parse_int(&p
, PNF_JUNK
, 0, INT_MAX
, "retries");
1002 else if (SKIP_PREFIX("alpha"))
1003 alpha
= parse_float(&p
, PNF_JUNK
, 0, 1, "average decay factor");
1004 else if (SKIP_PREFIX("_badwait"))
1005 bad_block_delay
= parse_float(&p
, PNF_JUNK
, 0, DBL_MAX
,
1007 else if (SKIP_PREFIX("_blkwait"))
1008 good_block_delay
= parse_float(&p
, PNF_JUNK
, 0, DBL_MAX
,
1009 "good block delay");
1011 bail("unknown bad blocks parameter `%s'", p
);
1013 else if (*p
!= ',') bail("unexpected junk in parameters");
1018 case 'E': errfile
= optarg
; break;
1019 case 'F': f
|= f_fixup
; break;
1021 fp
= fopen(optarg
, "r");
1023 bail_syserr(errno
, "failed to open ranges file `%s'", optarg
);
1026 BUF_REWIND(&buf
); if (read_line(fp
, &buf
)) break;
1028 while (ISSPACE(*p
)) p
++;
1029 if (!*p
|| *p
== '#') continue;
1030 if (parse_range(p
, 0, &start
, &end
) ||
1031 (last
<= SECLIMIT
&& start
< last
))
1032 bail("bad range `%s' at `%s' line %zu", buf
.p
, optarg
, i
);
1035 eventq
.v
[eventq
.n
- 1].pos
= end
;
1037 put_event(EV_WRITE
, 0, start
);
1038 put_event(EV_STOP
, 0, end
);
1044 bail_syserr(errno
, "failed to read ranges file `%s'", optarg
);
1047 fp
= fopen(optarg
, "r");
1049 bail_syserr(errno
, "failed to open bad-blocks file `%s'", optarg
);
1052 BUF_REWIND(&buf
); if (read_line(fp
, &buf
)) break;
1054 while (ISSPACE(*p
)) p
++;
1055 if (!*p
|| *p
== '#') continue;
1056 if (parse_range(p
, 0, &start
, &end
) ||
1057 (last
<= SECLIMIT
&& start
< last
))
1058 bail("bad range `%s' at `%s' line %zu", buf
.p
, optarg
, i
);
1060 { VEC_PUSH(bad
, &badblocks
); bad
->start
= start
; bad
->end
= end
; }
1063 bail_syserr(errno
, "failed to read bad-blocks file `%s'", optarg
);
1066 if (mapfile
) bail("can't have multiple map files");
1069 case 'c': f
|= f_continue
; break;
1070 case 'i': f
|= f_checkid
; break;
1072 start
= 0; end
= -1;
1073 if (parse_range(optarg
, PRF_HYPHEN
, &start
, &end
))
1074 bail("bad range `%s'", optarg
);
1076 put_event(EV_WRITE
, 0, start
);
1077 if (end
<= SECLIMIT
) put_event(EV_STOP
, 0, end
);
1080 case 's': f
|= f_stats
; break;
1081 default: f
|= f_bogus
; break;
1084 if (argc
- optind
!= 2) f
|= f_bogus
;
1085 if (f
&f_bogus
) { usage(stderr
); exit(2); }
1087 setlocale(LC_ALL
, "");
1088 progress_init(&progress
);
1089 device
= argv
[optind
]; outfile
= argv
[optind
+ 1];
1092 qsort(badblocks
.v
, badblocks
.n
, sizeof(struct badblock
),
1095 printf(";; fake bad blocks:\n");
1096 for (i
= 0; i
< badblocks
.n
; i
++)
1097 printf(";;\t%8"PRIuSEC
" .. %"PRIuSEC
"\n",
1098 badblocks
.v
[i
].start
, badblocks
.v
[i
].end
);
1102 open_dvd(device
, O_RDONLY
, &dvdfd
, &dvd
);
1104 blksz
= SECTORSZ
; volsz
= device_size(dvdfd
, device
, &blksz
);
1105 if (blksz
!= SECTORSZ
)
1106 bail("device `%s' block size %d /= %d", device
, blksz
, SECTORSZ
);
1108 bail("device `%s' volume size %"PRIu64
" not a multiple of %d",
1109 device
, volsz
, SECTORSZ
);
1112 open_dvd(outfile
, O_RDONLY
, 0, &dvd_out
);
1113 if (dvd_id(id_in
, dvd
, DIF_MUSTIFOHASH
, device
) ||
1114 dvd_id(id_out
, dvd_out
, DIF_MUSTIFOHASH
, device
))
1116 if (STRCMP(id_in
, !=, id_out
))
1117 bail("DVD id mismatch: input `%s' is `%s'; output `%s' is `%s'",
1118 device
, id_in
, outfile
, id_out
);
1121 outfd
= open(outfile
, O_WRONLY
| O_CREAT
, 0666);
1123 bail_syserr(errno
, "failed to create output file `%s'", outfile
);
1126 off
= lseek(outfd
, 0, SEEK_END
);
1128 bail_syserr(errno
, "failed to seek to end of output file `%s'",
1130 put_event(EV_WRITE
, 0, off
/SECTORSZ
);
1131 } else if (!eventq
.n
&& !(f
&f_fixup
))
1132 put_event(EV_WRITE
, 0, 0);
1134 /* It's fast enough just to check everything. */
1136 for (i
= 1; i
< 100; i
++) {
1140 put_file(mkident(RAW
, 0, 0), 0, volsz
/SECTORSZ
);
1141 assert(filetab
.n
<= MAXFILES
);
1143 for (i
= 0, limit
= 0; i
< filetab
.n
; i
++)
1144 if (filetab
.v
[i
].end
> limit
) limit
= filetab
.v
[i
].end
;
1146 if (end
> limit
) end
= limit
;
1149 printf("\n;; files:\n");
1150 for (i
= 0; i
< filetab
.n
; i
++) {
1151 file
= &filetab
.v
[i
];
1152 store_filename(fn
, file
->id
);
1153 printf(";;\t%8"PRIuSEC
" .. %-8"PRIuSEC
" %s\n",
1154 file
->start
, file
->end
, fn
);
1158 qsort(eventq
.v
, eventq
.n
, sizeof(struct event
), compare_event
);
1160 f
&= ~f_write
; start
= 0;
1161 for (i
= 0; i
< eventq
.n
; i
++) {
1166 bail("overlapping ranges: range from %"PRIuSEC
" "
1167 "still open at %"PRIuSEC
"",
1169 f
|= f_write
; start
= ev
->pos
;
1178 dump_eventq("initial");
1180 f
&= ~f_write
; start
= 0;
1181 for (i
= 0; i
< eventq
.n
; i
++) {
1183 if (ev
->ev
== EV_WRITE
) { start
= ev
->pos
; f
|= f_write
; }
1184 if (ev
->pos
>= limit
) break;
1185 if (ev
->ev
== EV_STOP
) { nsectors
+= ev
->pos
- start
; f
&= ~f_write
; }
1186 if (f
&f_fixup
) start
= ev
->pos
;
1190 dump_eventq("trimmed");
1193 put_event(EV_WRITE
, 0, start
);
1197 nsectors
+= limit
- start
;
1198 put_event(EV_STOP
, 0, limit
);
1201 dump_eventq("final");
1204 copy_progress
.render
= render_copy_progress
;
1205 progress_additem(&progress
, ©_progress
);
1206 if (nsectors
== limit
- start
)
1207 { ndone
= start
; nsectors
= limit
; }
1209 disc_progress
.render
= render_disc_progress
;
1210 progress_additem(&progress
, &disc_progress
);
1213 if (f
&f_stats
) gettimeofday(&tv0
, 0);
1216 printf("\n;; event sweep:\n");
1219 for (pos
= 0, i
= 0; i
< eventq
.n
; i
++) {
1221 if (ev
->pos
> pos
) {
1222 if (f
&f_write
) emit(pos
, ev
->pos
);
1225 progress_clear(&progress
);
1233 store_filename(fn
, filetab
.v
[ev
->file
].id
);
1234 progress_clear(&progress
);
1235 printf(";; %8"PRIuSEC
": begin `%s'\n", pos
, fn
);
1239 gettimeofday(&last_time
, 0); last_pos
= pos
;
1240 if (lseek(outfd
, (off_t
)ev
->pos
*SECTORSZ
, SEEK_SET
) < 0)
1242 "failed to seek to resume position "
1243 "(sector %"PRIuSEC
") in output file `%s'",
1246 progress_clear(&progress
);
1247 printf(";; %8"PRIuSEC
": begin write\n", pos
);
1254 progress_clear(&progress
);
1255 printf(";; %8"PRIuSEC
": end write\n", pos
);
1259 clear_live(ev
->file
);
1261 store_filename(fn
, filetab
.v
[ev
->file
].id
);
1262 progress_clear(&progress
);
1263 printf(";; %8"PRIuSEC
": end `%s'\n", pos
, fn
);
1270 progress_clear(&progress
);
1272 if (ftruncate(outfd
, (off_t
)limit
*SECTORSZ
) < 0)
1273 bail_syserr(errno
, "failed to set output file `%s' length", outfile
);
1276 gettimeofday(&tv1
, 0); t
= tvdiff(&tv0
, &tv1
);
1277 if (nsectors
== limit
) { ndone
-= start
; nsectors
-= start
; }
1278 tot
= scale_bytes((double)nsectors
*SECTORSZ
, &totunit
);
1279 rate
= scale_bytes((double)nsectors
*SECTORSZ
/t
, &rateunit
);
1280 moan("all done: %.1f %sB in %s -- %.1f %sB/s",
1281 tot
, totunit
, fmttime(t
, timebuf
), rate
, rateunit
);
1284 if (dvd
) DVDClose(dvd
);
1285 if (dvdfd
>= 0) close(dvdfd
);
1286 if (outfd
>= 0) close(outfd
);
1287 carefully_fclose(mapfp
, "bad-sector region map");
1288 carefully_fclose(errfp
, "bad-sector error log");
1289 progress_free(&progress
);