c6b4c19846f579cdec88fcbe60f575c270aca325
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",
12 static int parse_range(const char *p
, unsigned f
,
13 secaddr
*start_out
, secaddr
*end_out
)
17 unsigned long start
, end
;
22 start
= strtoul(p
, &q
, 0);
23 if (errno
|| start
>= SECLIMIT
) { rc
= -1; goto end
; }
24 *start_out
= start
; p
= q
;
25 } else if (!(f
&PRF_HYPHEN
))
26 { rc
= -1; goto end
; }
31 if (*p
!= '-') { rc
= -1; goto end
; }
34 if (!ISSPACE(*p
)) { rc
= -1; goto end
; }
35 do p
++; while (ISSPACE(*p
));
39 end
= strtoul(p
, &q
, 0);
40 if (errno
|| end
> SECLIMIT
|| end
< start
) { rc
= -1; goto end
; }
41 *end_out
= end
; p
= q
;
42 } else if (!(f
&PRF_HYPHEN
))
43 { rc
= -1; goto end
; }
45 if (!(f
&PRF_HYPHEN
)) while (ISSPACE(*p
)) p
++;
46 if (*p
&& ((f
&PRF_HYPHEN
) || *p
!= '#')) { rc
= -1; goto end
; }
54 #define MAXFILES (1 + 2*99 + 1)
59 DEFVEC(file_v
, struct file
);
60 static file_v filetab
= VEC_INIT
;
62 enum { EV_STOP
, EV_BEGIN
, EV_END
, EV_WRITE
};
64 unsigned char ev
, file
;
67 DEFVEC(event_v
, struct event
);
68 static event_v eventq
= VEC_INIT
;
70 static int compare_event(const void *a
, const void *b
)
72 const struct event
*eva
= a
, *evb
= b
;
74 if (eva
->pos
< evb
->pos
) return (-1);
75 else if (eva
->pos
> evb
->pos
) return (+1);
77 if (eva
->ev
< evb
->ev
) return (-1);
78 else if (eva
->ev
> evb
->ev
) return (+1);
80 if (eva
->file
< evb
->file
) return (-1);
81 else if (eva
->file
> evb
->file
) return (+1);
87 static void dump_eventq(const char *what
)
90 const struct event
*ev
;
93 printf("\n;; event dump (%s):\n", what
);
94 for (i
= 0; i
< eventq
.n
; i
++) {
98 store_filename(fn
, filetab
.v
[ev
->file
].id
);
99 printf(";; %8"PRIuSEC
": begin %s\n", ev
->pos
, fn
);
102 store_filename(fn
, filetab
.v
[ev
->file
].id
);
103 printf(";; %8"PRIuSEC
": end %s\n", ev
->pos
, fn
);
106 printf(";; %8"PRIuSEC
": write\n", ev
->pos
);
109 printf(";; %8"PRIuSEC
": stop\n", ev
->pos
);
112 printf(";; %8"PRIuSEC
": ?%u\n", ev
->pos
, ev
->ev
);
119 typedef uint_least32_t bits
;
120 static bits live
[(MAXFILES
+ 31)/32];
122 static inline int livep(unsigned i
)
123 { return (live
[i
/32]&((bits
)1 << (i
%32))); }
124 static inline void set_live(unsigned i
)
125 { live
[i
/32] |= (bits
)1 << (i
%32); }
126 static inline void clear_live(unsigned i
)
127 { live
[i
/32] &= ~((bits
)1 << (i
%32)); }
128 static inline int least_live(void)
130 unsigned i
, n
= (filetab
.n
+ 32)/32;
133 for (i
= 0; i
< n
; i
++) { b
= live
[i
]; if (b
) goto found
; }
137 if (!(b
&0x0000ffff)) { b
>>= 16; i
+= 16; }
138 if (!(b
&0x000000ff)) { b
>>= 8; i
+= 8; }
139 if (!(b
&0x0000000f)) { b
>>= 4; i
+= 4; }
140 if (!(b
&0x00000003)) { b
>>= 2; i
+= 2; }
141 if (!(b
&0x00000001)) { b
>>= 1; i
+= 1; }
146 static void put_event(unsigned evtype
, unsigned file
, secaddr pos
)
150 VEC_PUSH(ev
, &eventq
);
151 ev
->ev
= evtype
; ev
->file
= file
; ev
->pos
= pos
;
154 static void put_file(ident id
, secaddr start
, secaddr end
)
159 VEC_PUSH(f
, &filetab
); i
= f
- filetab
.v
;
160 f
->id
= id
; f
->start
= start
; f
->end
= end
;
161 put_event(EV_BEGIN
, i
, start
);
162 put_event(EV_END
, i
, end
);
165 static void put_menu(dvd_reader_t
*dvd
, unsigned title
)
167 ident id
= mkident(VOB
, title
, 0);
171 store_filename(fn
, id
);
172 start
= UDFFindFile(dvd
, fn
, &len
); if (!start
) return;
174 printf(";; %8"PRIuSEC
" .. %-8"PRIuSEC
": %s\n",
175 start
, start
+ SECTORS(len
), fn
);
177 put_file(id
, start
, start
+ SECTORS(len
));
180 static void put_title(dvd_reader_t
*dvd
, unsigned title
)
183 secaddr start
[9], len
[9];
186 for (i
= 0; i
< 9; i
++) {
187 store_filename(fn
, mkident(VOB
, title
, i
+ 1));
188 start
[i
] = UDFFindFile(dvd
, fn
, &len
[i
]); if (!start
[i
]) break;
190 npart
= i
; if (!npart
) return;
193 for (i
= 0; i
< npart
; i
++) {
194 store_filename(fn
, mkident(VOB
, title
, i
+ 1));
195 printf(";; %8"PRIuSEC
" .. %-8"PRIuSEC
": %s\n",
196 start
[i
], start
[i
] + SECTORS(len
[i
]), fn
);
201 for (i
= 0; i
< npart
- 1; i
++) {
203 bail("title %u part %u length = %"PRIuSEC
" not a multiple of %d",
204 title
, i
, len
[i
], SECTORSZ
);
205 if (start
[i
] + len
[i
]/SECTORSZ
!= start
[i
+ 1])
207 ("title %u part %u end = %"PRIuSEC
" /= part %u start = %"PRIuSEC
"",
208 title
, i
, start
[i
] + len
[i
]/SECTORSZ
, i
+ 1, start
[i
+ 1]);
211 put_file(mkident(VOB
, title
, 1),
212 start
[0], start
[npart
- 1] + SECTORS(len
[npart
- 1]));
215 static dvd_reader_t
*dvd
;
216 static int dvdfd
= -1, outfd
= -1;
217 static struct file
*file
;
218 static dvd_file_t
*vob
;
219 static const char *mapfile
; static FILE *mapfp
;
220 static const char *errfile
; static FILE *errfp
;
221 static secaddr limit
;
222 static secaddr bad_start
;
223 static unsigned retry
, max_retries
= 4;
225 static secaddr nsectors
, ndone
;
226 static secaddr last_pos
;
227 static struct timeval last_time
;
228 static double alpha
= 0.1;
229 static double wsum
, wcount
;
232 static const char throbber
[] = "|<-<|>->";
233 static unsigned throbix
= 0;
235 static struct progress_item
236 copy_progress
, disc_progress
,
237 file_progress
, badblock_progress
;
239 static double scale_bytes(double n
, const char **unit_out
)
241 const char *unit
= "";
243 if (n
> 1600) { n
/= 1024; unit
= "k"; }
244 if (n
> 1600) { n
/= 1024; unit
= "M"; }
245 if (n
> 1600) { n
/= 1024; unit
= "G"; }
246 if (n
> 1600) { n
/= 1024; unit
= "T"; }
247 *unit_out
= unit
; return (n
);
250 #define TIMESTRMAX 16
251 static char *fmttime(unsigned long t
, char *buf
)
253 if (t
< 60) sprintf(buf
, "%ld s", t
);
254 else if (t
< 3600) sprintf(buf
, "%ld:%02ld", t
/60, t
%60);
255 else sprintf(buf
, "%ld:%02ld:%02ld", t
/3600, (t
/60)%60, t
%60);
259 static void render_perfstats(struct progress_render_state
*render
)
262 char timebuf
[TIMESTRMAX
];
266 if (!wsum
|| !wcount
) { rate
= 0; eta
= -1; }
267 else { rate
= wsum
/wcount
; eta
= (int)((nsectors
- ndone
)/rate
+ 0.5); }
269 rate
= scale_bytes(rate
*SECTORSZ
, &unit
);
270 progress_putright(render
, "ETA %s ", rate ?
fmttime(eta
, timebuf
) : "???");
271 progress_putright(render
, "%.1f %sB/s, ", rate
, unit
);
274 static void render_copy_progress(struct progress_item
*item
,
275 struct progress_render_state
*render
)
277 double frac
= (double)ndone
/nsectors
;
279 progress_putleft(render
, " %c copied %.1f%%",
280 throbber
[throbix
], 100.0*frac
);
281 render_perfstats(render
);
282 progress_putleft(render
, " (%"PRIuSEC
" of %"PRIuSEC
")", ndone
, nsectors
);
284 progress_showbar(render
, frac
);
287 static void render_disc_progress(struct progress_item
*item
,
288 struct progress_render_state
*render
)
290 double frac
= (double)last_pos
/limit
;
292 progress_putleft(render
, " disc %.1f%% (%"PRIuSEC
" of %"PRIuSEC
")",
293 100.0*frac
, last_pos
, limit
);
294 progress_showbar(render
, frac
);
297 static void render_file_progress(struct progress_item
*item
,
298 struct progress_render_state
*render
)
300 secaddr off
= last_pos
- file
->start
, len
= file
->end
- file
->start
;
304 store_filename(fn
, file
->id
);
305 frac
= (double)off
/len
;
306 progress_putleft(render
, " `%s' %.1f%% (%"PRIuSEC
" of %"PRIuSEC
")",
307 fn
, 100.0*frac
, off
, len
);
308 progress_showbar(render
, frac
);
311 static void render_badblock_progress(struct progress_item
*item
,
312 struct progress_render_state
*render
)
314 secaddr n
= last_pos
- bad_start
;
318 progress_putleft(render
, " Retrying bad sector %"PRIuSEC
"", bad_start
);
319 progress_putright(render
, "attempt %u/%u ", retry
+ 1, max_retries
);
322 progress_putleft(render
, " Found %"PRIuSEC
" bad %s",
323 n
, n
== 1 ?
"sector" : "sectors");
324 progress_putright(render
, "%"PRIuSEC
" .. %"PRIuSEC
" ",
325 bad_start
, last_pos
);
328 if (bad_err
&& bad_err
!= EIO
)
329 progress_putleft(render
, " (%s)", strerror(bad_err
));
330 progress_shownotice(render
, bg
, 7);
333 static void update_progress(secaddr pos
)
338 gettimeofday(&now
, 0);
339 t
= tvdiff(&last_time
, &now
);
341 #define BETA (1 - alpha)
344 g
= wcount ?
pow(BETA
, t
) : 0.0; f
= (1 - g
)/(1 - BETA
);
345 wsum
= f
*(pos
- last_pos
)/t
+ g
*wsum
;
346 wcount
= f
+ g
*wcount
;
347 ndone
+= pos
- last_pos
;
348 last_time
= now
; last_pos
= pos
;
353 throbix
++; if (!throbber
[throbix
]) throbix
= 0;
356 static void report_progress(secaddr pos
)
357 { update_progress(pos
); progress_update(&progress
); }
359 struct badblock
{ secaddr start
, end
; };
360 DEFVEC(badblock_v
, struct badblock
);
361 static badblock_v badblocks
= VEC_INIT
;
363 static int compare_badblock(const void *a
, const void *b
)
365 const struct badblock
*ba
= a
, *bb
= b
;
367 if (ba
->start
< bb
->start
) return (-1);
368 else if (ba
->start
> bb
->start
) return (+1);
370 if (ba
->end
< bb
->end
) return (-1);
371 else if (ba
->end
> bb
->end
) return (+1);
376 static double bad_block_delay
= 0.0;
377 static double good_block_delay
= 0.0;
379 static ssize_t
read_sectors(secaddr pos
, void *buf
, secaddr want
)
384 struct badblock
*bad
, *best
;
385 unsigned char *p
= buf
;
388 best
= 0; lo
= 0; hi
= badblocks
.n
;
390 progress_clear(&progress
);
391 printf(";; searching badblocks for %"PRIuSEC
" .. %"PRIuSEC
"\n",
395 mid
= lo
+ (hi
- lo
)/2; bad
= &badblocks
.v
[mid
];
397 printf(";; try %zu (%"PRIuSEC
" .. %"PRIuSEC
")... ",
398 mid
, bad
->start
, bad
->end
);
400 if (pos
< bad
->start
) { D( printf("high\n"); ) best
= bad
; hi
= mid
; }
401 else if (pos
>= bad
->end
) { D( printf("low\n"); ) lo
= mid
+ 1; }
403 D( printf("match!\n"); )
404 errno
= EIO
; sit(bad_block_delay
); return (-1);
409 printf(";; next is %"PRIuSEC
" .. %"PRIuSEC
"\n",
410 best
->start
, best
->end
);
412 if (best
&& pos
+ want
> best
->start
)
413 { want
= best
->start
- pos
; fakeerr
= EIO
; sit(bad_block_delay
); }
419 { errno
= 0; n
= DVDReadBlocks(vob
, pos
- file
->start
, want
, p
); }
421 if (lseek(dvdfd
, (off_t
)pos
*SECTORSZ
, SEEK_SET
) < 0)
422 bail_syserr(errno
, "failed to seek to sector %"PRIuSEC
"", pos
);
423 errno
= 0; n
= read(dvdfd
, p
, want
*SECTORSZ
);
424 if (n
>= 0) n
/= SECTORSZ
;
426 memset(p
, 0, want
*SECTORSZ
);
430 if (n
> 0) { done
+= n
; pos
+= n
; p
+= n
*SECTORSZ
; want
-= n
; }
432 else if (errno
== EIO
&& errfile
) {
433 open_file_on_demand(errfile
, &errfp
, "bad-sector error log");
434 fprintf(errfp
, "%"PRIuSEC
" %"PRIuSEC
"\n", pos
, pos
+ 1);
435 check_write(errfp
, "bad-sector error log");
437 } else if (errno
!= EINTR
) break;
439 if (fakeerr
&& !errno
) errno
= fakeerr
;
440 else if (done
> 0 && good_block_delay
) sit(done
*good_block_delay
);
441 return (!done
&& errno ?
-1 : done
);
446 secaddr sz
, pos
, start
, end
;
447 secaddr good_lo
, good_hi
;
450 static void rearrange_sectors(struct recoverybuf
*r
,
451 secaddr dest
, secaddr src
, secaddr len
)
453 assert(dest
+ len
<= r
->sz
); assert(src
+ len
<= r
->sz
);
454 memmove(r
->buf
+ dest
*SECTORSZ
, r
->buf
+ src
*SECTORSZ
, len
*SECTORSZ
);
458 static PRINTF_LIKE(2, 3)
459 void show_recovery_buffer_map(const struct recoverybuf
*r
,
460 const char *what
, ...)
465 progress_clear(&progress
);
466 printf(";; recovery buffer (");
469 "(%"PRIuSEC
") ..%"PRIuSEC
".. "
470 "[%"PRIuSEC
" ..%"PRIuSEC
".. %"PRIuSEC
"] "
471 "..%"PRIuSEC
".. (%"PRIuSEC
")\n",
473 r
->pos
+ r
->start
, r
->end
- r
->start
, r
->pos
+ r
->end
,
474 r
->sz
- r
->end
, r
->pos
+ r
->sz
);
476 assert(r
->start
<= r
->end
);
477 assert(r
->end
<= r
->sz
);
481 static ssize_t
recovery_read_sectors(struct recoverybuf
*r
,
482 secaddr pos
, secaddr off
, secaddr want
)
486 assert(off
<= r
->sz
); assert(want
<= r
->sz
- off
);
487 assert(pos
== r
->pos
+ off
);
488 n
= read_sectors(pos
, r
->buf
+ off
*SECTORSZ
, want
);
492 static ssize_t
recovery_read_buffer(struct recoverybuf
*r
,
493 secaddr pos
, secaddr want
)
495 secaddr diff
, pp
, nn
;
499 progress_clear(&progress
);
500 show_recovery_buffer_map(r
, "begin(%"PRIuSEC
", %"PRIuSEC
")", pos
, want
);
505 if (r
->start
+ diff
>= r
->sz
) {
506 r
->pos
= pos
; r
->start
= r
->end
= 0;
508 show_recovery_buffer_map(r
, "cleared; shift up by %"PRIuSEC
"", diff
);
511 if (r
->end
+ diff
> r
->sz
) r
->end
= r
->sz
- diff
;
512 rearrange_sectors(r
, r
->start
+ diff
, r
->start
, r
->end
- r
->start
);
513 r
->pos
-= diff
; r
->start
+= diff
; r
->end
+= diff
;
515 show_recovery_buffer_map(r
, "shifted up by %"PRIuSEC
"", diff
);
518 } else if (pos
> r
->pos
+ r
->end
) {
519 r
->pos
= pos
; r
->start
= r
->end
= 0;
521 show_recovery_buffer_map(r
, "cleared; beyond previous region");
523 } else if (pos
+ want
> r
->pos
+ r
->sz
) {
524 diff
= (pos
+ want
) - (r
->pos
+ r
->sz
);
525 if (r
->end
<= diff
) {
526 r
->pos
= pos
; r
->start
= r
->end
= 0;
528 show_recovery_buffer_map(r
, "cleared; shift down by %"PRIuSEC
"", diff
);
531 if (r
->start
< diff
) r
->start
= diff
;
532 rearrange_sectors(r
, r
->start
- diff
, r
->start
, r
->end
- r
->start
);
533 r
->pos
+= diff
; r
->start
-= diff
; r
->end
-= diff
;
535 show_recovery_buffer_map(r
, "shifted down by %"PRIuSEC
"", diff
);
540 if (pos
< r
->pos
+ r
->start
) {
541 pp
= pos
- r
->pos
; nn
= r
->start
- pp
;
543 printf(";; read low (%"PRIuSEC
"@%"PRIuSEC
", %"PRIuSEC
")", pos
, pp
, nn
);
546 n
= recovery_read_sectors(r
, pos
, pp
, nn
);
548 printf(" -> %zd\n", n
);
551 if (n
>= 0 && n
> want
) n
= want
;
556 show_recovery_buffer_map(r
, "joined new region");
560 if (pos
+ want
> r
->pos
+ r
->end
) {
561 pp
= r
->end
; nn
= (pos
+ want
) - (r
->pos
+ r
->end
);
563 printf(";; read high (%"PRIuSEC
"@%"PRIuSEC
", %"PRIuSEC
")",
564 r
->pos
+ pp
, pp
, nn
);
567 n
= recovery_read_sectors(r
, r
->pos
+ pp
, pp
, nn
);
569 printf(" -> %zd\n", n
);
574 show_recovery_buffer_map(r
, "joined new region");
579 n
= r
->pos
+ r
->end
- pos
;
580 if (!n
&& want
) n
= -1;
581 else if (n
> want
) n
= want
;
585 show_recovery_buffer_map(r
, "done; return %zd", n
);
590 static ssize_t
recovery_read_multiple(struct recoverybuf
*r
,
591 secaddr pos
, secaddr want
)
594 secaddr skip
, want0
= want
;
596 while (want
> r
->sz
) {
598 n
= recovery_read_buffer(r
, pos
+ skip
, r
->sz
);
599 if (n
< r
->sz
) return (skip
+ (n
>= 0 ? n
: 0));
602 n
= recovery_read_buffer(r
, pos
, want
);
603 if (n
< 0 || n
< want
) return (n
);
607 static ssize_t
recovery_read(struct recoverybuf
*r
,
608 secaddr pos
, secaddr want
)
610 secaddr lo
= pos
, hi
= pos
+ want
, span
;
613 if (hi
< r
->good_lo
|| lo
> r
->good_hi
) {
614 n
= recovery_read_multiple(r
, lo
, hi
- lo
);
615 if (n
> 0) { r
->good_lo
= lo
; r
->good_hi
= lo
+ n
; }
619 if (hi
> r
->good_hi
) {
620 span
= hi
- r
->good_hi
;
621 n
= recovery_read_multiple(r
, r
->good_hi
, span
);
622 if (n
> 0) r
->good_hi
+= n
;
623 if (n
< 0 || n
< span
) return (r
->good_hi
- lo
);
626 if (lo
< r
->good_lo
) {
627 span
= r
->good_lo
- lo
;
628 n
= recovery_read_multiple(r
, lo
, span
);
629 if (n
== span
) r
->good_lo
= lo
;
633 n
= r
->good_hi
- pos
; if (n
> want
) n
= want
;
634 if (!n
) { errno
= EIO
; n
= -1; }
638 static double clear_factor
= 1.5;
639 static secaddr clear_min
= 1, clear_max
= SECLIMIT
;
640 static double step_factor
= 2.0;
641 static secaddr step_min
= 1, step_max
= 0;
643 static void recovered(secaddr bad_lo
, secaddr bad_hi
)
647 progress_clear(&progress
);
649 if (!file
|| id_kind(file
->id
) == RAW
)
650 moan("skipping %"PRIuSEC
" bad sectors (%"PRIuSEC
" .. %"PRIuSEC
")",
651 bad_hi
- bad_lo
, bad_lo
, bad_hi
);
653 store_filename(fn
, file
->id
);
654 moan("skipping %"PRIuSEC
" bad sectors (%"PRIuSEC
" .. %"PRIuSEC
"; "
655 "`%s' %"PRIuSEC
" .. %"PRIuSEC
" of %"PRIuSEC
")",
656 bad_hi
- bad_lo
, bad_lo
, bad_hi
,
657 fn
, bad_lo
- file
->start
, bad_hi
- file
->start
,
658 file
->end
- file
->start
);
662 open_file_on_demand(mapfile
, &mapfp
, "bad-sector region map");
663 fprintf(mapfp
, "%"PRIuSEC
" %"PRIuSEC
" # %"PRIuSEC
" sectors",
664 bad_lo
, bad_hi
, bad_hi
- bad_lo
);
666 if (file
&& id_kind(file
->id
) != RAW
)
667 fprintf(mapfp
, "; `%s' %"PRIuSEC
" .. %"PRIuSEC
" of %"PRIuSEC
"",
668 fn
, bad_lo
- file
->start
, bad_hi
- file
->start
,
669 file
->end
- file
->start
);
672 check_write(mapfp
, "bad-sector region map");
675 if (lseek(outfd
, (off_t
)(bad_hi
- bad_lo
)*SECTORSZ
, SEEK_CUR
) < 0)
676 bail_syserr(errno
, "failed to seek past bad sectors");
678 progress_removeitem(&progress
, &badblock_progress
);
679 progress_update(&progress
);
682 static secaddr
run_length_wanted(secaddr pos
, secaddr badlen
, secaddr end
)
686 want
= ceil(clear_factor
*badlen
);
687 if (want
< clear_min
) want
= clear_min
;
688 if (want
> end
- pos
) want
= end
- pos
;
689 if (clear_max
&& want
> clear_max
) want
= clear_max
;
693 static void report_bad_blocks_progress(secaddr bad_hi
, int err
)
694 { bad_err
= err
; report_progress(bad_hi
); }
696 static ssize_t
find_good_sector(secaddr
*pos_inout
, secaddr end
,
697 unsigned char *buf
, secaddr sz
)
699 secaddr pos
= *pos_inout
, bad_lo
, bad_hi
, good
, step
, want
;
700 struct recoverybuf r
;
703 bad_start
= pos
; bad_err
= errno
;
704 badblock_progress
.render
= render_badblock_progress
;
705 progress_additem(&progress
, &badblock_progress
);
707 want
= sz
; if (want
> end
- pos
) want
= end
- pos
;
708 for (retry
= 0; retry
< max_retries
; retry
++) {
709 report_bad_blocks_progress(pos
, errno
);
710 n
= read_sectors(pos
, buf
, want
);
712 progress_clear(&progress
);
713 printf(";; [retry] try reading %"PRIuSEC
" .. %"PRIuSEC
" -> %zd\n",
717 progress_clear(&progress
);
718 moan("sector %"PRIuSEC
" read ok after retry", pos
);
719 progress_removeitem(&progress
, &badblock_progress
);
720 progress_update(&progress
);
725 r
.buf
= buf
; r
.sz
= sz
; r
.pos
= r
.start
= r
.end
= 0;
726 r
.good_lo
= r
.good_hi
= 0;
728 bad_lo
= pos
; bad_hi
= pos
+ 1;
731 progress_clear(&progress
);
732 printf(";; bounding bad-block region: "
733 "%"PRIuSEC
" ..%"PRIuSEC
".. %"PRIuSEC
"\n",
734 bad_lo
, bad_hi
- bad_lo
, bad_hi
);
737 progress_clear(&progress
);
738 moan("giving up on this extent");
739 recovered(bad_lo
, end
); *pos_inout
= end
;
742 report_bad_blocks_progress(bad_hi
, errno
);
743 step
= (step_factor
- 1)*(bad_hi
- bad_lo
);
744 if (step
< step_min
) step
= step_min
;
745 if (step_max
&& step
> step_max
) step
= step_max
;
746 step
+= bad_hi
- bad_lo
;
747 if (step
> end
- bad_lo
) step
= end
- bad_lo
;
748 want
= run_length_wanted(pos
, step
, end
);
749 n
= recovery_read(&r
, pos
, want
);
751 printf(";; [bound] try reading %"PRIuSEC
" .. %"PRIuSEC
" -> %zd\n",
754 if (n
== want
) break;
756 bad_hi
= pos
+ n
+ 1;
760 while (good
> bad_hi
) {
762 progress_clear(&progress
);
763 printf(";; limiting bad-block region: "
764 "%"PRIuSEC
" ..%"PRIuSEC
".. %"PRIuSEC
" ..%"PRIuSEC
".. %"PRIuSEC
"\n",
765 bad_lo
, bad_hi
- bad_lo
, bad_hi
, good
- bad_hi
, good
);
767 report_bad_blocks_progress(bad_hi
, errno
);
768 pos
= bad_hi
+ (good
- bad_hi
)/2;
770 want
= run_length_wanted(pos
, step
, end
);
771 n
= recovery_read(&r
, pos
, want
);
773 printf(";; [limit] try reading %"PRIuSEC
" .. %"PRIuSEC
" -> %zd\n",
777 if (n
== want
) good
= pos
;
778 else bad_hi
= pos
+ n
+ 1;
780 recovered(bad_lo
, good
); *pos_inout
= good
;
781 if (good
< r
.pos
+ r
.start
|| r
.pos
+ r
.end
<= good
)
784 n
= r
.pos
+ r
.end
- good
;
785 rearrange_sectors(&r
, 0, good
- r
.pos
, n
);
788 show_recovery_buffer_map(&r
, "returning %zd good sectors at %"PRIuSEC
"",
794 static void emit(secaddr start
, secaddr end
)
796 #define BUFSECTORS 512
799 unsigned char buf
[BUFSECTORS
*SECTORSZ
];
803 static int first_time
= 1;
811 least
= least_live();
814 printf(";; %8"PRIuSEC
" .. %"PRIuSEC
"\n", start
, end
);
816 for (i
= 0; i
< filetab
.n
; i
++) {
817 if (!livep(i
)) continue;
818 if (act
== -1) act
= i
;
819 f
= &filetab
.v
[i
]; store_filename(fn
, f
->id
);
820 printf(";;\t\t%8"PRIuSEC
" .. %-8"PRIuSEC
" %s\n",
821 start
- f
->start
, end
- f
->start
, fn
);
823 if (act
== -1) printf(";;\t\t#<no live source>\n");
824 assert(act
== least
);
828 { file
= 0; vob
= 0; }
830 file
= &filetab
.v
[least
];
831 switch (id_kind(file
->id
)) {
836 if (first_time
) { progress_clear(&progress
); first_time
= 0; }
837 vob
= DVDOpenFile(dvd
, id_title(file
->id
),
839 ? DVD_READ_TITLE_VOBS
840 : DVD_READ_MENU_VOBS
);
842 bail("failed to open %s %u",
843 id_part(file
->id
) ?
"title" : "menu",
851 if (file
&& id_kind(file
->id
) != RAW
) {
852 file_progress
.render
= render_file_progress
;
853 progress_additem(&progress
, &file_progress
);
856 progress_update(&progress
);
859 want
= end
- pos
; if (want
> BUFSECTORS
) want
= BUFSECTORS
;
860 n
= read_sectors(pos
, buf
, want
);
862 if (n
<= 0) n
= find_good_sector(&pos
, end
, buf
, BUFSECTORS
);
863 if (n
> 0) { carefully_write(outfd
, buf
, n
*SECTORSZ
); pos
+= n
; }
864 report_progress(pos
);
867 if (vob
) { DVDCloseFile(vob
); vob
= 0; }
869 if (file
&& id_kind(file
->id
) != RAW
)
870 progress_removeitem(&progress
, &file_progress
);
871 progress_update(&progress
);
876 int main(int argc
, char *argv
[])
883 secaddr start
, end
, last
;
884 const struct event
*ev
;
885 const char *device
, *outfile
;
886 struct badblock
*bad
;
890 struct buf buf
= BUF_INIT
;
891 struct timeval tv0
, tv1
;
893 const char *rateunit
, *totunit
;
894 char timebuf
[TIMESTRMAX
], id_in
[MAXIDSZ
], id_out
[MAXIDSZ
];
895 dvd_reader_t
*dvd_out
;
897 const struct file
*file
;
902 #define f_continue 2u
905 #define f_checkid 16u
911 opt
= getopt(argc
, argv
, "hB:E:FR:X:b:cir:s"); if (opt
< 0) break;
913 case 'h': usage(stderr
); exit(0);
916 #define SKIP_PREFIX(s) \
917 (STRNCMP(p, ==, s "=", sizeof(s)) && (p += sizeof(s), 1))
919 if (SKIP_PREFIX("cf"))
920 clear_factor
= parse_float(&p
, PNF_JUNK
, 0, DBL_MAX
,
922 else if (SKIP_PREFIX("cmin"))
923 clear_min
= parse_int(&p
, PNF_JUNK
, 1, SECLIMIT
,
925 else if (SKIP_PREFIX("cmax"))
926 clear_max
= parse_int(&p
, PNF_JUNK
, 1, SECLIMIT
,
928 else if (SKIP_PREFIX("sf"))
929 step_factor
= parse_float(&p
, PNF_JUNK
, 0, DBL_MAX
,
931 else if (SKIP_PREFIX("smin"))
932 step_min
= parse_int(&p
, PNF_JUNK
, 1, SECLIMIT
- 1,
934 else if (SKIP_PREFIX("smax"))
935 step_max
= parse_int(&p
, PNF_JUNK
, 1, SECLIMIT
- 1,
937 else if (SKIP_PREFIX("retry"))
938 max_retries
= parse_int(&p
, PNF_JUNK
, 0, INT_MAX
, "retries");
939 else if (SKIP_PREFIX("alpha"))
940 alpha
= parse_float(&p
, PNF_JUNK
, 0, 1, "average decay factor");
941 else if (SKIP_PREFIX("_badwait"))
942 bad_block_delay
= parse_float(&p
, PNF_JUNK
, 0, DBL_MAX
,
944 else if (SKIP_PREFIX("_blkwait"))
945 good_block_delay
= parse_float(&p
, PNF_JUNK
, 0, DBL_MAX
,
948 bail("unknown bad blocks parameter `%s'", p
);
950 if (*p
!= ',') bail("unexpected junk in parameters");
955 case 'E': errfile
= optarg
; break;
956 case 'F': f
|= f_fixup
; break;
958 fp
= fopen(optarg
, "r");
960 bail_syserr(errno
, "failed to open ranges file `%s'", optarg
);
963 buf_rewind(&buf
); if (read_line(fp
, &buf
)) break;
965 while (ISSPACE(*p
)) p
++;
966 if (!*p
|| *p
== '#') continue;
967 if (parse_range(p
, 0, &start
, &end
) ||
968 (last
<= SECLIMIT
&& start
< last
))
969 bail("bad range `%s' at `%s' line %zu", buf
.p
, optarg
, i
);
972 eventq
.v
[eventq
.n
- 1].pos
= end
;
974 put_event(EV_WRITE
, 0, start
);
975 put_event(EV_STOP
, 0, end
);
981 bail_syserr(errno
, "failed to read ranges file `%s'", optarg
);
985 fp
= fopen(optarg
, "r");
987 bail_syserr(errno
, "failed to open bad-blocks file `%s'", optarg
);
990 buf_rewind(&buf
); if (read_line(fp
, &buf
)) break;
992 while (ISSPACE(*p
)) p
++;
993 if (!*p
|| *p
== '#') continue;
994 if (parse_range(p
, 0, &start
, &end
) ||
995 (last
<= SECLIMIT
&& start
< last
))
996 bail("bad range `%s' at `%s' line %zu", buf
.p
, optarg
, i
);
998 VEC_PUSH(bad
, &badblocks
);
999 bad
->start
= start
; bad
->end
= end
;
1003 bail_syserr(errno
, "failed to read bad-blocks file `%s'", optarg
);
1006 if (mapfile
) bail("can't have multiple map files");
1009 case 'c': f
|= f_continue
; break;
1010 case 'i': f
|= f_checkid
; break;
1012 start
= 0; end
= -1; f
|= f_retry
;
1013 if (parse_range(optarg
, PRF_HYPHEN
, &start
, &end
))
1014 bail("bad range `%s'", optarg
);
1016 put_event(EV_WRITE
, 0, start
);
1017 if (end
<= SECLIMIT
) put_event(EV_STOP
, 0, end
);
1020 case 's': f
|= f_stats
; break;
1021 default: f
|= f_bogus
; break;
1024 if (argc
- optind
!= 2) f
|= f_bogus
;
1025 if (f
&f_bogus
) { usage(stderr
); exit(2); }
1027 device
= argv
[optind
]; outfile
= argv
[optind
+ 1];
1030 qsort(badblocks
.v
, badblocks
.n
, sizeof(struct badblock
),
1033 printf(";; fake bad blocks:\n");
1034 for (i
= 0; i
< badblocks
.n
; i
++)
1035 printf(";;\t%8"PRIuSEC
" .. %"PRIuSEC
"\n",
1036 badblocks
.v
[i
].start
, badblocks
.v
[i
].end
);
1040 setlocale(LC_ALL
, "");
1041 progress_init(&progress
);
1042 if (open_dvd(device
, O_RDONLY
, &dvdfd
, &dvd
)) exit(2);
1044 blksz
= SECTORSZ
; volsz
= device_size(dvdfd
, device
, &blksz
);
1045 if (blksz
!= SECTORSZ
)
1046 bail("device `%s' block size %d /= %d", device
, blksz
, SECTORSZ
);
1048 bail("device `%s' volume size %"PRIu64
" not a multiple of %d",
1049 device
, volsz
, SECTORSZ
);
1052 if (open_dvd(outfile
, O_RDONLY
, 0, &dvd_out
)) exit(2);
1053 if (dvd_id(id_in
, dvd
, DIF_MUSTIFOHASH
, device
) ||
1054 dvd_id(id_out
, dvd_out
, DIF_MUSTIFOHASH
, device
))
1056 if (STRCMP(id_in
, !=, id_out
))
1057 bail("DVD id mismatch: input `%s' is `%s'; output `%s' is `%s'",
1058 device
, id_in
, outfile
, id_out
);
1061 outfd
= open(outfile
, O_WRONLY
| O_CREAT
, 0666);
1063 bail_syserr(errno
, "failed to create output file `%s'", outfile
);
1066 off
= lseek(outfd
, 0, SEEK_END
);
1068 bail_syserr(errno
, "failed to seek to end of output file `%s'",
1070 put_event(EV_WRITE
, 0, off
/SECTORSZ
); f
|= f_retry
;
1072 if (!(f
&(f_retry
| f_fixup
)))
1073 put_event(EV_WRITE
, 0, 0);
1075 /* It's fast enough just to check everything. */
1077 for (i
= 1; i
< 100; i
++) {
1081 put_file(mkident(RAW
, 0, 0), 0, volsz
/SECTORSZ
);
1082 assert(filetab
.n
<= MAXFILES
);
1084 for (i
= 0, limit
= 0; i
< filetab
.n
; i
++)
1085 if (filetab
.v
[i
].end
> limit
) limit
= filetab
.v
[i
].end
;
1088 printf("\n;; files:\n");
1089 for (i
= 0; i
< filetab
.n
; i
++) {
1090 file
= &filetab
.v
[i
];
1091 store_filename(fn
, file
->id
);
1092 printf(";;\t%8"PRIuSEC
" .. %-8"PRIuSEC
" %s\n",
1093 file
->start
, file
->end
, fn
);
1097 qsort(eventq
.v
, eventq
.n
, sizeof(struct event
), compare_event
);
1099 for (i
= 0, f
&= ~f_write
, start
= 0; i
< eventq
.n
; i
++) {
1104 bail("overlapping ranges: range from %"PRIuSEC
" "
1105 "still open at %"PRIuSEC
"",
1107 f
|= f_write
; start
= ev
->pos
;
1116 dump_eventq("initial");
1118 for (i
= 0, f
&= ~f_write
, start
= last
= 0; i
< eventq
.n
; i
++) {
1120 if (ev
->ev
== EV_WRITE
) { start
= ev
->pos
; f
|= f_write
; }
1121 if (ev
->pos
>= limit
) break;
1122 if (ev
->ev
== EV_STOP
) { nsectors
+= ev
->pos
- start
; f
&= ~f_write
; }
1123 if (f
&f_fixup
) last
= ev
->pos
;
1127 dump_eventq("trimmed");
1130 put_event(EV_WRITE
, 0, last
);
1134 nsectors
+= limit
- start
;
1135 put_event(EV_STOP
, 0, limit
);
1138 dump_eventq("final");
1141 copy_progress
.render
= render_copy_progress
;
1142 progress_additem(&progress
, ©_progress
);
1143 if (nsectors
== limit
- start
)
1144 { ndone
= start
; nsectors
= limit
; }
1146 disc_progress
.render
= render_disc_progress
;
1147 progress_additem(&progress
, &disc_progress
);
1150 if (f
&f_stats
) gettimeofday(&tv0
, 0);
1153 printf("\n;; event sweep:\n");
1155 for (pos
= 0, i
= 0, f
&= ~f_write
; i
< eventq
.n
; i
++) {
1157 if (ev
->pos
> pos
) {
1158 if (f
&f_write
) emit(pos
, ev
->pos
);
1161 progress_clear(&progress
);
1169 store_filename(fn
, filetab
.v
[ev
->file
].id
);
1170 progress_clear(&progress
);
1171 printf(";; %8"PRIuSEC
": begin `%s'\n", pos
, fn
);
1175 gettimeofday(&last_time
, 0); last_pos
= pos
;
1176 if (lseek(outfd
, (off_t
)ev
->pos
*SECTORSZ
, SEEK_SET
) < 0)
1178 "failed to seek to resume position "
1179 "(sector %"PRIuSEC
") in output file `%s'",
1183 progress_clear(&progress
);
1184 printf(";; %8"PRIuSEC
": begin write\n", pos
);
1190 progress_clear(&progress
);
1191 printf(";; %8"PRIuSEC
": end write\n", pos
);
1195 clear_live(ev
->file
);
1197 store_filename(fn
, filetab
.v
[ev
->file
].id
);
1198 progress_clear(&progress
);
1199 printf(";; %8"PRIuSEC
": end `%s'\n", pos
, fn
);
1206 progress_clear(&progress
);
1208 if (ftruncate(outfd
, (off_t
)limit
*SECTORSZ
) < 0)
1209 bail_syserr(errno
, "failed to set output file `%s' length", outfile
);
1212 gettimeofday(&tv1
, 0); t
= tvdiff(&tv0
, &tv1
);
1213 if (nsectors
== limit
) { ndone
-= start
; nsectors
-= start
; }
1214 tot
= scale_bytes((double)nsectors
*SECTORSZ
, &totunit
);
1215 rate
= scale_bytes((double)nsectors
*SECTORSZ
/t
, &rateunit
);
1216 moan("all done: %.1f %sB in %s -- %.1f %sB/s",
1217 tot
, totunit
, fmttime(t
, timebuf
), rate
, rateunit
);
1220 if (dvd
) DVDClose(dvd
);
1221 if (dvdfd
>= 0) close(dvdfd
);
1222 if (outfd
>= 0) close(outfd
);
1223 carefully_fclose(mapfp
, "bad-sector region map");
1224 carefully_fclose(errfp
, "bad-sector error log");
1225 progress_free(&progress
);