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 #define MAXFILES (1 + 2*99 + 1)
16 DEFVEC(file_v
, struct file
);
17 static file_v filetab
= VEC_INIT
;
19 enum { EV_STOP
, EV_BEGIN
, EV_END
, EV_WRITE
};
21 unsigned char ev
, file
;
24 DEFVEC(event_v
, struct event
);
25 static event_v eventq
= VEC_INIT
;
27 static int compare_event(const void *a
, const void *b
)
29 const struct event
*eva
= a
, *evb
= b
;
31 if (eva
->pos
< evb
->pos
) return (-1);
32 else if (eva
->pos
> evb
->pos
) return (+1);
34 if (eva
->ev
< evb
->ev
) return (-1);
35 else if (eva
->ev
> evb
->ev
) return (+1);
37 if (eva
->file
< evb
->file
) return (-1);
38 else if (eva
->file
> evb
->file
) return (+1);
43 typedef uint_least32_t bits
;
44 static bits live
[(MAXFILES
+ 31)/32];
46 static inline int livep(unsigned i
)
47 { return (live
[i
/32]&((bits
)1 << (i
%32))); }
48 static inline void set_live(unsigned i
)
49 { live
[i
/32] |= (bits
)1 << (i
%32); }
50 static inline void clear_live(unsigned i
)
51 { live
[i
/32] &= ~((bits
)1 << (i
%32)); }
52 static inline int least_live(void)
54 unsigned i
, n
= (filetab
.n
+ 32)/32;
57 for (i
= 0; i
< n
; i
++) { b
= live
[i
]; if (b
) goto found
; }
61 if (!(b
&0x0000ffff)) { b
>>= 16; i
+= 16; }
62 if (!(b
&0x000000ff)) { b
>>= 8; i
+= 8; }
63 if (!(b
&0x0000000f)) { b
>>= 4; i
+= 4; }
64 if (!(b
&0x00000003)) { b
>>= 2; i
+= 2; }
65 if (!(b
&0x00000001)) { b
>>= 1; i
+= 1; }
70 static void put_event(unsigned evtype
, unsigned file
, secaddr pos
)
74 VEC_PUSH(ev
, &eventq
);
75 ev
->ev
= evtype
; ev
->file
= file
; ev
->pos
= pos
;
78 static void put_file(ident id
, secaddr start
, secaddr end
)
83 VEC_PUSH(f
, &filetab
); i
= f
- filetab
.v
;
84 f
->id
= id
; f
->start
= start
; f
->end
= end
;
85 put_event(EV_BEGIN
, i
, start
);
86 put_event(EV_END
, i
, end
);
89 static void put_menu(dvd_reader_t
*dvd
, unsigned title
)
91 ident id
= mkident(VOB
, title
, 0);
95 store_filename(fn
, id
);
96 start
= UDFFindFile(dvd
, fn
, &len
); if (!start
) return;
98 printf(";; %8"PRIuSEC
" .. %-8"PRIuSEC
": %s\n",
99 start
, start
+ SECTORS(len
), fn
);
101 put_file(id
, start
, start
+ SECTORS(len
));
104 static void put_title(dvd_reader_t
*dvd
, unsigned title
)
107 secaddr start
[9], len
[9];
110 for (i
= 0; i
< 9; i
++) {
111 store_filename(fn
, mkident(VOB
, title
, i
+ 1));
112 start
[i
] = UDFFindFile(dvd
, fn
, &len
[i
]); if (!start
[i
]) break;
114 npart
= i
; if (!npart
) return;
117 for (i
= 0; i
< npart
; i
++) {
118 store_filename(fn
, mkident(VOB
, title
, i
+ 1));
119 printf(";; %8"PRIuSEC
" .. %-8"PRIuSEC
": %s\n",
120 start
[i
], start
[i
] + SECTORS(len
[i
]), fn
);
125 for (i
= 0; i
< npart
- 1; i
++) {
127 bail("title %u part %u length = %"PRIuSEC
" not a multiple of %d",
128 title
, i
, len
[i
], SECTORSZ
);
129 if (start
[i
] + len
[i
]/SECTORSZ
!= start
[i
+ 1])
131 ("title %u part %u end = %"PRIuSEC
" /= part %u start = %"PRIuSEC
"",
132 title
, i
, start
[i
] + len
[i
]/SECTORSZ
, i
+ 1, start
[i
+ 1]);
135 put_file(mkident(VOB
, title
, 1),
136 start
[0], start
[npart
- 1] + SECTORS(len
[npart
- 1]));
139 static secaddr last_pos
, limit
, nsectors
, ndone
;
140 static struct timeval last_time
;
141 static double wsum
, wcount
;
142 static struct file
*file
;
143 static secaddr bad_start
;
144 static unsigned retry
, max_retries
= 4;
147 static const char throbber
[] = "|<-<|>->";
148 static unsigned throbix
= 0;
150 static double scale_bytes(double n
, const char **unit_out
)
152 const char *unit
= "";
154 if (n
> 1600) { n
/= 1024; unit
= "k"; }
155 if (n
> 1600) { n
/= 1024; unit
= "M"; }
156 if (n
> 1600) { n
/= 1024; unit
= "G"; }
157 if (n
> 1600) { n
/= 1024; unit
= "T"; }
159 *unit_out
= unit
; return (n
);
162 static struct progress_item
163 copy_progress
, disc_progress
,
164 file_progress
, badblock_progress
;
166 #define TIMESTRMAX 16
167 static char *fmttime(unsigned long t
, char *buf
)
169 if (t
< 60) sprintf(buf
, "%ld s", t
);
170 else if (t
< 3600) sprintf(buf
, "%ld:%02ld", t
/60, t
%60);
171 else sprintf(buf
, "%ld:%02ld:%02ld", t
/3600, (t
/60)%60, t
%60);
175 static void render_perfstats(struct progress_render_state
*render
)
178 char timebuf
[TIMESTRMAX
];
182 if (!wsum
|| !wcount
) { rate
= 0; eta
= -1; }
183 else { rate
= wsum
/wcount
; eta
= (int)((nsectors
- ndone
)/rate
+ 0.5); }
185 rate
= scale_bytes(rate
*SECTORSZ
, &unit
);
186 progress_putright(render
, "ETA %s ", rate ?
fmttime(eta
, timebuf
) : "???");
187 progress_putright(render
, "%.1f %sB/s, ", rate
, unit
);
190 static void render_copy_progress(struct progress_item
*item
,
191 struct progress_render_state
*render
)
193 double frac
= (double)ndone
/nsectors
;
195 progress_putleft(render
, " %c copied %.1f%%",
196 throbber
[throbix
], 100.0*frac
);
197 render_perfstats(render
);
198 progress_putleft(render
, " (%"PRIuSEC
" of %"PRIuSEC
")", ndone
, nsectors
);
200 progress_showbar(render
, frac
);
203 static void render_disc_progress(struct progress_item
*item
,
204 struct progress_render_state
*render
)
206 double frac
= (double)last_pos
/limit
;
208 progress_putleft(render
, " disc %.1f%% (%"PRIuSEC
" of %"PRIuSEC
")",
209 100.0*frac
, last_pos
, limit
);
210 progress_showbar(render
, frac
);
213 static void render_file_progress(struct progress_item
*item
,
214 struct progress_render_state
*render
)
216 secaddr off
= last_pos
- file
->start
, len
= file
->end
- file
->start
;
220 store_filename(fn
, file
->id
);
221 frac
= (double)off
/len
;
222 progress_putleft(render
, " `%s' %.1f%% (%"PRIuSEC
" of %"PRIuSEC
")",
223 fn
, 100.0*frac
, off
, len
);
224 progress_showbar(render
, frac
);
227 static void render_badblock_progress(struct progress_item
*item
,
228 struct progress_render_state
*render
)
230 secaddr n
= last_pos
- bad_start
;
234 progress_putleft(render
, " Retrying bad sector %"PRIuSEC
"", bad_start
);
235 progress_putright(render
, "attempt %u/%u ", retry
+ 1, max_retries
);
238 progress_putleft(render
, " Found %"PRIuSEC
" bad %s",
239 n
, n
== 1 ?
"sector" : "sectors");
240 progress_putright(render
, "%"PRIuSEC
" .. %"PRIuSEC
" ",
241 bad_start
, last_pos
);
244 if (bad_err
&& bad_err
!= EIO
)
245 progress_putleft(render
, " (%s)", strerror(bad_err
));
246 progress_shownotice(render
, bg
, 7);
249 static double alpha
= 0.1;
251 static void update_progress(secaddr pos
)
256 gettimeofday(&now
, 0);
257 t
= tvdiff(&last_time
, &now
);
259 #define BETA (1 - alpha)
262 g
= wcount ?
pow(BETA
, t
) : 0.0; f
= (1 - g
)/(1 - BETA
);
263 wsum
= f
*(pos
- last_pos
)/t
+ g
*wsum
;
264 wcount
= f
+ g
*wcount
;
265 ndone
+= pos
- last_pos
;
266 last_time
= now
; last_pos
= pos
;
271 throbix
++; if (!throbber
[throbix
]) throbix
= 0;
274 static void report_progress(secaddr pos
)
275 { update_progress(pos
); progress_update(&progress
); }
277 static dvd_reader_t
*dvd
;
278 static int dvdfd
= -1, outfd
= -1;
279 static dvd_file_t
*vob
;
280 static const char *mapfile
; static FILE *mapfp
;
281 static const char *errfile
; static FILE *errfp
;
283 struct badblock
{ secaddr start
, end
; };
284 DEFVEC(badblock_v
, struct badblock
);
285 static badblock_v badblocks
= VEC_INIT
;
287 static int compare_badblock(const void *a
, const void *b
)
289 const struct badblock
*ba
= a
, *bb
= b
;
291 if (ba
->start
< bb
->start
) return (-1);
292 else if (ba
->start
> bb
->start
) return (+1);
294 if (ba
->end
< bb
->end
) return (-1);
295 else if (ba
->end
> bb
->end
) return (+1);
300 static double bad_block_delay
= 0.0;
301 static double good_block_delay
= 0.0;
303 static ssize_t
read_sectors(secaddr pos
, void *buf
, secaddr want
)
308 struct badblock
*bad
, *best
;
309 unsigned char *p
= buf
;
312 best
= 0; lo
= 0; hi
= badblocks
.n
;
314 progress_clear(&progress
);
315 printf(";; searching badblocks for %"PRIuSEC
" .. %"PRIuSEC
"\n",
319 mid
= lo
+ (hi
- lo
)/2; bad
= &badblocks
.v
[mid
];
321 printf(";; try %zu (%"PRIuSEC
" .. %"PRIuSEC
")... ",
322 mid
, bad
->start
, bad
->end
);
324 if (pos
< bad
->start
) { D( printf("high\n"); ) best
= bad
; hi
= mid
; }
325 else if (pos
>= bad
->end
) { D( printf("low\n"); ) lo
= mid
+ 1; }
327 D( printf("match!\n"); )
328 errno
= EIO
; sit(bad_block_delay
); return (-1);
333 printf(";; next is %"PRIuSEC
" .. %"PRIuSEC
"\n",
334 best
->start
, best
->end
);
336 if (best
&& pos
+ want
> best
->start
)
337 { want
= best
->start
- pos
; fakeerr
= EIO
; sit(bad_block_delay
); }
342 { errno
= 0; n
= DVDReadBlocks(vob
, pos
- file
->start
, want
, p
); }
344 if (lseek(dvdfd
, (off_t
)pos
*SECTORSZ
, SEEK_SET
) < 0)
345 bail_syserr(errno
, "failed to seek to sector %"PRIuSEC
"", pos
);
346 errno
= 0; n
= read(dvdfd
, p
, want
*SECTORSZ
);
347 if (n
>= 0) n
/= SECTORSZ
;
349 memset(p
, 0, want
*SECTORSZ
);
353 if (n
> 0) { done
+= n
; pos
+= n
; p
+= n
*SECTORSZ
; want
-= n
; }
355 else if (errno
== EIO
&& errfile
) {
356 open_file_on_demand(errfile
, &errfp
, "bad-sector error log");
357 fprintf(errfp
, "%"PRIuSEC
" %"PRIuSEC
"\n", pos
, pos
+ 1);
358 check_write(errfp
, "bad-sector error log");
360 } else if (errno
!= EINTR
) break;
362 if (fakeerr
&& !errno
) errno
= fakeerr
;
363 else if (done
> 0 && good_block_delay
) sit(done
*good_block_delay
);
364 return (!done
&& errno ?
-1 : done
);
367 static void record_bad_sectors(secaddr bad_lo
, secaddr bad_hi
)
371 if (!mapfile
) return;
373 open_file_on_demand(mapfile
, &mapfp
, "bad-sector region map");
374 fprintf(mapfp
, "%"PRIuSEC
" %"PRIuSEC
" # %"PRIuSEC
" sectors",
375 bad_lo
, bad_hi
, bad_hi
- bad_lo
);
377 if (file
&& id_kind(file
->id
) != RAW
) {
378 store_filename(fn
, file
->id
);
379 fprintf(mapfp
, "; `%s' %"PRIuSEC
" .. %"PRIuSEC
" of %"PRIuSEC
"",
380 fn
, bad_lo
- file
->start
, bad_hi
- file
->start
,
381 file
->end
- file
->start
);
385 check_write(mapfp
, "bad-sector region map");
388 static void recovered(secaddr bad_lo
, secaddr bad_hi
)
392 progress_clear(&progress
);
394 if (!file
|| id_kind(file
->id
) == RAW
)
395 moan("skipping %"PRIuSEC
" bad sectors (%"PRIuSEC
" .. %"PRIuSEC
")",
396 bad_hi
- bad_lo
, bad_lo
, bad_hi
);
398 store_filename(fn
, file
->id
);
399 moan("skipping %"PRIuSEC
" bad sectors (%"PRIuSEC
" .. %"PRIuSEC
"; "
400 "`%s' %"PRIuSEC
" .. %"PRIuSEC
" of %"PRIuSEC
")",
401 bad_hi
- bad_lo
, bad_lo
, bad_hi
,
402 fn
, bad_lo
- file
->start
, bad_hi
- file
->start
,
403 file
->end
- file
->start
);
406 record_bad_sectors(bad_lo
, bad_hi
);
408 if (lseek(outfd
, (off_t
)(bad_hi
- bad_lo
)*SECTORSZ
, SEEK_CUR
) < 0)
409 bail_syserr(errno
, "failed to seek past bad sectors");
411 progress_removeitem(&progress
, &badblock_progress
);
412 progress_update(&progress
);
417 secaddr sz
, pos
, start
, end
;
418 secaddr good_lo
, good_hi
;
421 static void rearrange_sectors(struct recoverybuf
*r
,
422 secaddr dest
, secaddr src
, secaddr len
)
424 assert(dest
+ len
<= r
->sz
);
425 assert(src
+ len
<= r
->sz
);
426 memmove(r
->buf
+ dest
*SECTORSZ
, r
->buf
+ src
*SECTORSZ
, len
*SECTORSZ
);
430 static PRINTF_LIKE(2, 3)
431 void show_recovery_buffer_map(const struct recoverybuf
*r
,
432 const char *what
, ...)
437 progress_clear(&progress
);
438 printf(";; recovery buffer (");
441 "(%"PRIuSEC
") ..%"PRIuSEC
".. "
442 "[%"PRIuSEC
" ..%"PRIuSEC
".. %"PRIuSEC
"] "
443 "..%"PRIuSEC
".. (%"PRIuSEC
")\n",
445 r
->pos
+ r
->start
, r
->end
- r
->start
, r
->pos
+ r
->end
,
446 r
->sz
- r
->end
, r
->pos
+ r
->sz
);
448 assert(r
->start
<= r
->end
);
449 assert(r
->end
<= r
->sz
);
453 static ssize_t
recovery_read_sectors(struct recoverybuf
*r
,
454 secaddr pos
, secaddr off
, secaddr want
)
458 assert(off
<= r
->sz
); assert(want
<= r
->sz
- off
);
459 n
= read_sectors(pos
, r
->buf
+ off
*SECTORSZ
, want
);
463 static ssize_t
recovery_read_buffer(struct recoverybuf
*r
,
464 secaddr pos
, secaddr want
)
466 secaddr diff
, pp
, nn
;
470 progress_clear(&progress
);
471 show_recovery_buffer_map(r
, "begin(%"PRIuSEC
", %"PRIuSEC
")", pos
, want
);
476 if (r
->start
+ diff
>= r
->sz
) {
477 r
->pos
= pos
; r
->start
= r
->end
= 0;
479 show_recovery_buffer_map(r
, "cleared; shift up by %"PRIuSEC
"", diff
);
482 if (r
->end
+ diff
> r
->sz
) r
->end
= r
->sz
- diff
;
483 rearrange_sectors(r
, r
->start
+ diff
, r
->start
, r
->end
- r
->start
);
484 r
->pos
-= diff
; r
->start
+= diff
; r
->end
+= diff
;
486 show_recovery_buffer_map(r
, "shifted up by %"PRIuSEC
"", diff
);
489 } else if (pos
> r
->pos
+ r
->end
) {
490 r
->pos
= pos
; r
->start
= r
->end
= 0;
492 show_recovery_buffer_map(r
, "cleared; beyond previous region");
494 } else if (pos
+ want
> r
->pos
+ r
->sz
) {
495 diff
= (pos
+ want
) - (r
->pos
+ r
->sz
);
496 if (r
->end
<= diff
) {
497 r
->pos
= pos
; r
->start
= r
->end
= 0;
499 show_recovery_buffer_map(r
, "cleared; shift down by %"PRIuSEC
"", diff
);
502 if (r
->start
< diff
) r
->start
= diff
;
503 rearrange_sectors(r
, r
->start
- diff
, r
->start
, r
->end
- r
->start
);
504 r
->pos
+= diff
; r
->start
-= diff
; r
->end
-= diff
;
506 show_recovery_buffer_map(r
, "shifted down by %"PRIuSEC
"", diff
);
511 if (pos
< r
->pos
+ r
->start
) {
512 pp
= pos
- r
->pos
; nn
= r
->start
- pp
;
514 printf(";; read low (%"PRIuSEC
"@%"PRIuSEC
", %"PRIuSEC
")", pos
, pp
, nn
);
517 n
= recovery_read_sectors(r
, pos
, pp
, nn
);
519 printf(" -> %zd\n", n
);
522 if (n
>= 0 && n
> want
) n
= want
;
527 show_recovery_buffer_map(r
, "joined new region");
531 if (pos
+ want
> r
->pos
+ r
->end
) {
532 pp
= r
->end
; nn
= (pos
+ want
) - (r
->pos
+ r
->end
);
534 printf(";; read high (%"PRIuSEC
"@%"PRIuSEC
", %"PRIuSEC
")",
535 r
->pos
+ pp
, pp
, nn
);
538 n
= recovery_read_sectors(r
, r
->pos
+ pp
, pp
, nn
);
540 printf(" -> %zd\n", n
);
545 show_recovery_buffer_map(r
, "joined new region");
550 n
= r
->pos
+ r
->end
- pos
;
551 if (!n
&& want
) n
= -1;
552 else if (n
> want
) n
= want
;
556 show_recovery_buffer_map(r
, "done; return %zd", n
);
561 static ssize_t
recovery_read_multiple(struct recoverybuf
*r
,
562 secaddr pos
, secaddr want
)
565 secaddr skip
, want0
= want
;
567 while (want
> r
->sz
) {
569 n
= recovery_read_buffer(r
, pos
+ skip
, r
->sz
);
570 if (n
< r
->sz
) return (skip
+ (n
>= 0 ? n
: 0));
573 n
= recovery_read_buffer(r
, pos
, want
);
574 if (n
< 0 || n
< want
) return (n
);
578 static ssize_t
recovery_read(struct recoverybuf
*r
,
579 secaddr pos
, secaddr want
)
581 secaddr lo
= pos
, hi
= pos
+ want
, span
;
584 if (hi
< r
->good_lo
|| lo
> r
->good_hi
) {
585 n
= recovery_read_multiple(r
, lo
, hi
- lo
);
586 if (n
> 0) { r
->good_lo
= lo
; r
->good_hi
= lo
+ n
; }
590 if (hi
> r
->good_hi
) {
591 span
= hi
- r
->good_hi
;
592 n
= recovery_read_multiple(r
, r
->good_hi
, span
);
593 if (n
> 0) r
->good_hi
+= n
;
594 if (n
< 0 || n
< span
) return (r
->good_hi
- lo
);
597 if (lo
< r
->good_lo
) {
598 span
= r
->good_lo
- lo
;
599 n
= recovery_read_multiple(r
, lo
, span
);
600 if (n
== span
) r
->good_lo
= lo
;
604 n
= r
->good_hi
- pos
; if (n
> want
) n
= want
;
605 if (!n
) { errno
= EIO
; n
= -1; }
609 static double clear_factor
= 1.5;
610 static secaddr clear_min
= 1, clear_max
= SECLIMIT
;
611 static double step_factor
= 2.0;
612 static secaddr step_min
= 1, step_max
= 0;
614 static secaddr
run_length_wanted(secaddr pos
, secaddr badlen
, secaddr end
)
618 want
= clear_factor
*badlen
;
619 if (want
< clear_min
) want
= clear_min
;
620 if (want
> end
- pos
) want
= end
- pos
;
621 if (clear_max
&& want
> clear_max
) want
= clear_max
;
625 static void report_bad_blocks_progress(secaddr bad_hi
, int err
)
626 { bad_err
= err
; report_progress(bad_hi
); }
628 static ssize_t
find_good_sector(secaddr
*pos_inout
, secaddr end
,
629 unsigned char *buf
, secaddr sz
)
631 secaddr pos
= *pos_inout
, bad_lo
, bad_hi
, good
, step
, want
;
632 struct recoverybuf r
;
635 bad_start
= pos
; bad_err
= errno
;
636 badblock_progress
.render
= render_badblock_progress
;
637 progress_additem(&progress
, &badblock_progress
);
639 r
.buf
= buf
; r
.sz
= sz
; r
.pos
= r
.start
= r
.end
= 0;
640 r
.good_lo
= r
.good_hi
= 0;
642 want
= sz
; if (want
> end
- pos
) want
= end
- pos
;
643 for (retry
= 0; retry
< max_retries
; retry
++) {
644 report_bad_blocks_progress(pos
, errno
);
645 n
= recovery_read(&r
, pos
, want
);
647 progress_clear(&progress
);
648 printf(";; [retry] try reading %"PRIuSEC
" .. %"PRIuSEC
" -> %zd\n",
652 progress_clear(&progress
);
653 moan("sector %"PRIuSEC
" read ok after retry", pos
);
654 progress_removeitem(&progress
, &badblock_progress
);
655 progress_update(&progress
);
660 bad_lo
= pos
; bad_hi
= pos
+ 1;
662 report_bad_blocks_progress(bad_hi
, errno
);
664 progress_clear(&progress
);
665 printf(";; bounding bad-block region: "
666 "%"PRIuSEC
" ..%"PRIuSEC
".. %"PRIuSEC
"\n",
667 bad_lo
, bad_hi
- bad_lo
, bad_hi
);
670 progress_clear(&progress
);
671 moan("giving up on this extent");
672 recovered(bad_lo
, end
); *pos_inout
= end
;
675 step
= (step_factor
- 1)*(bad_hi
- bad_lo
);
676 if (step
< step_min
) step
= step_min
;
677 if (step_max
&& step
> step_max
) step
= step_max
;
678 if (step
> end
- bad_hi
) step
= end
- bad_hi
;
679 pos
= bad_hi
+ step
- 1;
680 want
= run_length_wanted(pos
, step
, end
);
681 n
= recovery_read(&r
, pos
, want
);
683 printf(";; [bound] try reading %"PRIuSEC
" .. %"PRIuSEC
" -> %zd\n",
686 if (n
== want
) break;
688 bad_hi
= pos
+ n
+ 1;
692 while (good
> bad_hi
) {
693 report_bad_blocks_progress(bad_hi
, errno
);
695 progress_clear(&progress
);
696 printf(";; limiting bad-block region: "
697 "%"PRIuSEC
" ..%"PRIuSEC
".. %"PRIuSEC
" ..%"PRIuSEC
".. %"PRIuSEC
"\n",
698 bad_lo
, bad_hi
- bad_lo
, bad_hi
, good
- bad_hi
, good
);
700 pos
= bad_hi
+ (good
- bad_hi
)/2;
702 want
= run_length_wanted(pos
, step
, end
);
703 n
= recovery_read(&r
, pos
, want
);
705 printf(";; [limit] try reading %"PRIuSEC
" .. %"PRIuSEC
" -> %zd\n",
709 if (n
== want
) good
= pos
;
710 else bad_hi
= pos
+ n
+ 1;
712 recovered(bad_lo
, bad_hi
); *pos_inout
= bad_hi
;
713 if (bad_hi
< r
.pos
+ r
.start
|| r
.pos
+ r
.end
<= bad_hi
)
716 n
= r
.pos
+ r
.end
- bad_hi
;
717 rearrange_sectors(&r
, 0, bad_hi
- r
.pos
, n
);
720 show_recovery_buffer_map(&r
, "returning %zd good sectors at %"PRIuSEC
"",
726 static void emit(secaddr start
, secaddr end
)
728 #define BUFSECTORS 512
731 unsigned char buf
[BUFSECTORS
*SECTORSZ
];
735 static int first_time
= 1;
743 least
= least_live();
746 printf(";; %8"PRIuSEC
" .. %"PRIuSEC
"\n", start
, end
);
748 for (i
= 0; i
< filetab
.n
; i
++) {
749 if (!livep(i
)) continue;
750 if (act
== -1) act
= i
;
751 f
= &filetab
.v
[i
]; store_filename(fn
, f
->id
);
752 printf(";;\t\t%8"PRIuSEC
" .. %-8"PRIuSEC
" %s\n",
753 start
- f
->start
, end
- f
->start
, fn
);
755 if (act
== -1) printf(";;\t\t#<no live source>\n");
756 assert(act
== least
);
760 { file
= 0; vob
= 0; }
762 file
= &filetab
.v
[least
];
763 switch (id_kind(file
->id
)) {
768 if (first_time
) { progress_clear(&progress
); first_time
= 0; }
769 vob
= DVDOpenFile(dvd
, id_title(file
->id
),
771 ? DVD_READ_TITLE_VOBS
772 : DVD_READ_MENU_VOBS
);
774 bail("failed to open %s %u",
775 id_part(file
->id
) ?
"title" : "menu",
777 progress_update(&progress
);
784 if (file
&& id_kind(file
->id
) != RAW
) {
785 file_progress
.render
= render_file_progress
;
786 progress_additem(&progress
, &file_progress
);
791 want
= end
- pos
; if (want
> BUFSECTORS
) want
= BUFSECTORS
;
792 n
= read_sectors(pos
, buf
, want
);
794 if (n
<= 0) n
= find_good_sector(&pos
, end
, buf
, BUFSECTORS
);
795 if (n
> 0) { carefully_write(outfd
, buf
, n
*SECTORSZ
); pos
+= n
; }
796 report_progress(pos
);
799 if (vob
) { DVDCloseFile(vob
); vob
= 0; }
801 if (file
&& id_kind(file
->id
) != RAW
)
802 progress_removeitem(&progress
, &file_progress
);
803 progress_update(&progress
);
808 #define PRF_HYPHEN 1u
809 static int parse_range(const char *p
, unsigned f
,
810 secaddr
*start_out
, secaddr
*end_out
)
814 unsigned long start
, end
;
819 start
= strtoul(p
, &q
, 0);
820 if (errno
|| start
>= SECLIMIT
) { rc
= -1; goto end
; }
821 *start_out
= start
; p
= q
;
822 } else if (!(f
&PRF_HYPHEN
))
823 { rc
= -1; goto end
; }
828 if (*p
!= '-') { rc
= -1; goto end
; }
831 if (!ISSPACE(*p
)) { rc
= -1; goto end
; }
832 do p
++; while (ISSPACE(*p
));
836 end
= strtoul(p
, &q
, 0);
837 if (errno
|| end
> SECLIMIT
|| end
< start
) { rc
= -1; goto end
; }
838 *end_out
= end
; p
= q
;
839 } else if (!(f
&PRF_HYPHEN
))
840 { rc
= -1; goto end
; }
842 if (!(f
&PRF_HYPHEN
)) while (ISSPACE(*p
)) p
++;
843 if (*p
&& ((f
&PRF_HYPHEN
) || *p
!= '#')) { rc
= -1; goto end
; }
852 static void dump_eventq(const char *what
)
855 const struct event
*ev
;
858 printf("\n;; event dump (%s):\n", what
);
859 for (i
= 0; i
< eventq
.n
; i
++) {
863 store_filename(fn
, filetab
.v
[ev
->file
].id
);
864 printf(";; %8"PRIuSEC
": begin %s\n", ev
->pos
, fn
);
867 store_filename(fn
, filetab
.v
[ev
->file
].id
);
868 printf(";; %8"PRIuSEC
": end %s\n", ev
->pos
, fn
);
871 printf(";; %8"PRIuSEC
": write\n", ev
->pos
);
874 printf(";; %8"PRIuSEC
": stop\n", ev
->pos
);
877 printf(";; %8"PRIuSEC
": ?%u\n", ev
->pos
, ev
->ev
);
884 int main(int argc
, char *argv
[])
891 secaddr start
, end
, last
;
892 const struct event
*ev
;
893 const char *device
, *outfile
;
894 struct badblock
*bad
;
898 struct buf buf
= BUF_INIT
;
899 struct timeval tv0
, tv1
;
901 const char *rateunit
, *totunit
;
902 char timebuf
[TIMESTRMAX
], id_in
[MAXIDSZ
], id_out
[MAXIDSZ
];
903 dvd_reader_t
*dvd_out
;
905 const struct file
*file
;
910 #define f_continue 2u
913 #define f_checkid 16u
918 opt
= getopt(argc
, argv
, "hB:E:FR:X:b:cir:s"); if (opt
< 0) break;
920 case 'h': usage(stderr
); exit(0);
923 #define SKIP_PREFIX(s) \
924 (STRNCMP(p, ==, s "=", sizeof(s)) && (p += sizeof(s), 1))
926 if (SKIP_PREFIX("cf"))
927 clear_factor
= parse_float(&p
, PNF_JUNK
, 0, DBL_MAX
,
929 else if (SKIP_PREFIX("cmin"))
930 clear_min
= parse_int(&p
, PNF_JUNK
, 1, SECLIMIT
,
932 else if (SKIP_PREFIX("cmax"))
933 clear_max
= parse_int(&p
, PNF_JUNK
, 1, SECLIMIT
,
935 else if (SKIP_PREFIX("sf"))
936 step_factor
= parse_float(&p
, PNF_JUNK
, 0, DBL_MAX
,
938 else if (SKIP_PREFIX("smin"))
939 step_min
= parse_int(&p
, PNF_JUNK
, 1, SECLIMIT
- 1,
941 else if (SKIP_PREFIX("smax"))
942 step_max
= parse_int(&p
, PNF_JUNK
, 1, SECLIMIT
- 1,
944 else if (SKIP_PREFIX("retry"))
945 max_retries
= parse_int(&p
, PNF_JUNK
, 0, INT_MAX
, "retries");
946 else if (SKIP_PREFIX("alpha"))
947 alpha
= parse_float(&p
, PNF_JUNK
, 0, 1, "average decay factor");
948 else if (SKIP_PREFIX("_badwait"))
949 bad_block_delay
= parse_float(&p
, PNF_JUNK
, 0, DBL_MAX
,
951 else if (SKIP_PREFIX("_blkwait"))
952 good_block_delay
= parse_float(&p
, PNF_JUNK
, 0, DBL_MAX
,
955 bail("unknown bad blocks parameter `%s'", p
);
957 else if (*p
!= ',') bail("unexpected junk in parameters");
962 case 'E': errfile
= optarg
; break;
963 case 'F': f
|= f_fixup
; break;
965 fp
= fopen(optarg
, "r");
967 bail_syserr(errno
, "failed to open ranges file `%s'", optarg
);
970 buf_rewind(&buf
); if (read_line(fp
, &buf
)) break;
972 while (ISSPACE(*p
)) p
++;
973 if (!*p
|| *p
== '#') continue;
974 if (parse_range(p
, 0, &start
, &end
) ||
975 (last
<= SECLIMIT
&& start
< last
))
976 bail("bad range `%s' at `%s' line %zu", buf
.p
, optarg
, i
);
979 eventq
.v
[eventq
.n
- 1].pos
= end
;
981 put_event(EV_WRITE
, 0, start
);
982 put_event(EV_STOP
, 0, end
);
988 bail_syserr(errno
, "failed to read ranges file `%s'", optarg
);
991 fp
= fopen(optarg
, "r");
993 bail_syserr(errno
, "failed to open bad-blocks file `%s'", optarg
);
996 buf_rewind(&buf
); if (read_line(fp
, &buf
)) break;
998 while (ISSPACE(*p
)) p
++;
999 if (!*p
|| *p
== '#') continue;
1000 if (parse_range(p
, 0, &start
, &end
) ||
1001 (last
<= SECLIMIT
&& start
< last
))
1002 bail("bad range `%s' at `%s' line %zu", buf
.p
, optarg
, i
);
1004 { VEC_PUSH(bad
, &badblocks
); bad
->start
= start
; bad
->end
= end
; }
1007 bail_syserr(errno
, "failed to read bad-blocks file `%s'", optarg
);
1010 if (mapfile
) bail("can't have multiple map files");
1013 case 'c': f
|= f_continue
; break;
1014 case 'i': f
|= f_checkid
; break;
1016 start
= 0; end
= -1;
1017 if (parse_range(optarg
, PRF_HYPHEN
, &start
, &end
))
1018 bail("bad range `%s'", optarg
);
1020 put_event(EV_WRITE
, 0, start
);
1021 if (end
<= SECLIMIT
) put_event(EV_STOP
, 0, end
);
1024 case 's': f
|= f_stats
; break;
1025 default: f
|= f_bogus
; break;
1028 if (argc
- optind
!= 2) f
|= f_bogus
;
1029 if (f
&f_bogus
) { usage(stderr
); exit(2); }
1031 setlocale(LC_ALL
, "");
1032 progress_init(&progress
);
1033 device
= argv
[optind
]; outfile
= argv
[optind
+ 1];
1036 qsort(badblocks
.v
, badblocks
.n
, sizeof(struct badblock
),
1039 printf(";; fake bad blocks:\n");
1040 for (i
= 0; i
< badblocks
.n
; i
++)
1041 printf(";;\t%8"PRIuSEC
" .. %"PRIuSEC
"\n",
1042 badblocks
.v
[i
].start
, badblocks
.v
[i
].end
);
1046 if (open_dvd(device
, O_RDONLY
, &dvdfd
, &dvd
)) exit(2);
1048 blksz
= SECTORSZ
; volsz
= device_size(dvdfd
, device
, &blksz
);
1049 if (blksz
!= SECTORSZ
)
1050 bail("device `%s' block size %d /= %d", device
, blksz
, SECTORSZ
);
1052 bail("device `%s' volume size %"PRIu64
" not a multiple of %d",
1053 device
, volsz
, SECTORSZ
);
1056 if (open_dvd(outfile
, O_RDONLY
, 0, &dvd_out
)) exit(2);
1057 if (dvd_id(id_in
, dvd
, DIF_MUSTIFOHASH
, device
) ||
1058 dvd_id(id_out
, dvd_out
, DIF_MUSTIFOHASH
, device
))
1060 if (STRCMP(id_in
, !=, id_out
))
1061 bail("DVD id mismatch: input `%s' is `%s'; output `%s' is `%s'",
1062 device
, id_in
, outfile
, id_out
);
1065 outfd
= open(outfile
, O_WRONLY
| O_CREAT
, 0666);
1067 bail_syserr(errno
, "failed to create output file `%s'", outfile
);
1070 off
= lseek(outfd
, 0, SEEK_END
);
1072 bail_syserr(errno
, "failed to seek to end of output file `%s'",
1074 put_event(EV_WRITE
, 0, off
/SECTORSZ
);
1075 } else if (!eventq
.n
&& !(f
&f_fixup
))
1076 put_event(EV_WRITE
, 0, 0);
1078 /* It's fast enough just to check everything. */
1080 for (i
= 1; i
< 100; i
++) {
1084 put_file(mkident(RAW
, 0, 0), 0, volsz
/SECTORSZ
);
1085 assert(filetab
.n
<= MAXFILES
);
1087 for (i
= 0, limit
= 0; i
< filetab
.n
; i
++)
1088 if (filetab
.v
[i
].end
> limit
) limit
= filetab
.v
[i
].end
;
1090 if (end
> limit
) end
= limit
;
1093 printf("\n;; files:\n");
1094 for (i
= 0; i
< filetab
.n
; i
++) {
1095 file
= &filetab
.v
[i
];
1096 store_filename(fn
, file
->id
);
1097 printf(";;\t%8"PRIuSEC
" .. %-8"PRIuSEC
" %s\n",
1098 file
->start
, file
->end
, fn
);
1102 qsort(eventq
.v
, eventq
.n
, sizeof(struct event
), compare_event
);
1104 f
&= ~f_write
; start
= 0;
1105 for (i
= 0; i
< eventq
.n
; i
++) {
1110 bail("overlapping ranges: range from %"PRIuSEC
" "
1111 "still open at %"PRIuSEC
"",
1113 f
|= f_write
; start
= ev
->pos
;
1122 dump_eventq("initial");
1124 f
&= ~f_write
; start
= 0;
1125 for (i
= 0; i
< eventq
.n
; i
++) {
1127 if (ev
->ev
== EV_WRITE
) { start
= ev
->pos
; f
|= f_write
; }
1128 if (ev
->pos
>= limit
) break;
1129 if (ev
->ev
== EV_STOP
) { nsectors
+= ev
->pos
- start
; f
&= ~f_write
; }
1130 if (f
&f_fixup
) start
= ev
->pos
;
1134 dump_eventq("trimmed");
1137 put_event(EV_WRITE
, 0, start
);
1141 nsectors
+= limit
- start
;
1142 put_event(EV_STOP
, 0, limit
);
1145 dump_eventq("final");
1148 copy_progress
.render
= render_copy_progress
;
1149 progress_additem(&progress
, ©_progress
);
1150 if (nsectors
== limit
- start
)
1151 { ndone
= start
; nsectors
= limit
; }
1153 disc_progress
.render
= render_disc_progress
;
1154 progress_additem(&progress
, &disc_progress
);
1157 if (f
&f_stats
) gettimeofday(&tv0
, 0);
1160 printf("\n;; event sweep:\n");
1163 for (pos
= 0, i
= 0; i
< eventq
.n
; i
++) {
1165 if (ev
->pos
> pos
) {
1166 if (f
&f_write
) emit(pos
, ev
->pos
);
1169 progress_clear(&progress
);
1177 store_filename(fn
, filetab
.v
[ev
->file
].id
);
1178 progress_clear(&progress
);
1179 printf(";; %8"PRIuSEC
": begin `%s'\n", pos
, fn
);
1183 gettimeofday(&last_time
, 0); last_pos
= pos
;
1184 if (lseek(outfd
, (off_t
)ev
->pos
*SECTORSZ
, SEEK_SET
) < 0)
1186 "failed to seek to resume position "
1187 "(sector %"PRIuSEC
") in output file `%s'",
1190 progress_clear(&progress
);
1191 printf(";; %8"PRIuSEC
": begin write\n", pos
);
1198 progress_clear(&progress
);
1199 printf(";; %8"PRIuSEC
": end write\n", pos
);
1203 clear_live(ev
->file
);
1205 store_filename(fn
, filetab
.v
[ev
->file
].id
);
1206 progress_clear(&progress
);
1207 printf(";; %8"PRIuSEC
": end `%s'\n", pos
, fn
);
1214 progress_clear(&progress
);
1216 if (ftruncate(outfd
, (off_t
)limit
*SECTORSZ
) < 0)
1217 bail_syserr(errno
, "failed to set output file `%s' length", outfile
);
1220 gettimeofday(&tv1
, 0); t
= tvdiff(&tv0
, &tv1
);
1221 if (nsectors
== limit
) { ndone
-= start
; nsectors
-= start
; }
1222 tot
= scale_bytes((double)nsectors
*SECTORSZ
, &totunit
);
1223 rate
= scale_bytes((double)nsectors
*SECTORSZ
/t
, &rateunit
);
1224 moan("all done: %.1f %sB in %s -- %.1f %sB/s",
1225 tot
, totunit
, fmttime(t
, timebuf
), rate
, rateunit
);
1228 if (dvd
) DVDClose(dvd
);
1229 if (dvdfd
>= 0) close(dvdfd
);
1230 if (outfd
>= 0) close(outfd
);
1231 carefully_fclose(mapfp
, "bad-sector region map");
1232 carefully_fclose(errfp
, "bad-sector error log");
1233 progress_free(&progress
);