0f2ddb77a06c164fc20922709c5a9b694500b402
3 static void usage(FILE *fp
)
6 "usage: %s [-c] [-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 static void carefully_write(int fd
, const void *buf
, size_t sz
)
20 const unsigned char *p
= buf
;
27 if (errno
== EINTR
) continue;
28 bail_syserr(errno
, "failed to write to output file");
30 if (!n
) bail("unexpected short write to output file");
35 static void open_file_on_demand(const char *file
, FILE **fp_inout
,
41 fp
= fopen(file
, "w");
43 bail_syserr(errno
, "failed to open %s file `%s'", what
, file
);
44 fprintf(fp
, "## %s\n\n", what
);
49 static void check_write(FILE *fp
, const char *what
)
52 if (ferror(fp
)) bail_syserr(errno
, "error writing %s file", what
);
55 static void carefully_fclose(FILE *fp
, const char *what
)
57 if (fp
&& (ferror(fp
) || fclose(fp
)))
58 bail_syserr(errno
, "error writing %s file", what
);
61 #define DEFVEC(vtype, etype) \
62 typedef struct { etype *v; size_t n, sz; } vtype
63 #define VEC_INIT { 0, 0, 0 }
64 #define VEC_FREE(vv) do { \
65 free((vv)->v); (vv)->v 0; (vv)->n = (vv)->sz = 0; \
67 #define VEC_PUSH(p, vv) do { \
69 if ((vv)->n >= (vv)->sz) { \
70 (vv)->sz = (vv)->sz ? 2*(vv)->sz : 32; \
71 _want = (vv)->sz*sizeof(*(vv)->v); \
72 (vv)->v = realloc((vv)->v, _want); \
73 if (!(vv)->v) bail("out of memory allocating %zu bytes", _want); \
75 (p) = &(vv)->v[(vv)->n++]; \
78 #define MAXFILES (1 + 2*99 + 1)
83 DEFVEC(file_v
, struct file
);
84 static file_v filetab
= VEC_INIT
;
86 enum { EV_STOP
, EV_BEGIN
, EV_END
, EV_WRITE
};
88 unsigned char ev
, file
;
91 DEFVEC(event_v
, struct event
);
92 static event_v eventq
= VEC_INIT
;
94 static int compare_event(const void *a
, const void *b
)
96 const struct event
*eva
= a
, *evb
= b
;
98 if (eva
->pos
< evb
->pos
) return (-1);
99 else if (eva
->pos
> evb
->pos
) return (+1);
101 if (eva
->ev
< evb
->ev
) return (-1);
102 else if (eva
->ev
> evb
->ev
) return (+1);
104 if (eva
->file
< evb
->file
) return (-1);
105 else if (eva
->file
> evb
->file
) return (+1);
110 typedef uint_least32_t bits
;
111 static bits live
[(MAXFILES
+ 31)/32];
113 static inline int livep(unsigned i
)
114 { return (live
[i
/32]&((bits
)1 << (i
%32))); }
115 static inline void set_live(unsigned i
)
116 { live
[i
/32] |= (bits
)1 << (i
%32); }
117 static inline void clear_live(unsigned i
)
118 { live
[i
/32] &= ~((bits
)1 << (i
%32)); }
119 static inline int least_live(void)
121 unsigned i
, n
= (filetab
.n
+ 32)/32;
124 for (i
= 0; i
< n
; i
++) { b
= live
[i
]; if (b
) goto found
; }
128 if (!(b
&0x0000ffff)) { b
>>= 16; i
+= 16; }
129 if (!(b
&0x000000ff)) { b
>>= 8; i
+= 8; }
130 if (!(b
&0x0000000f)) { b
>>= 4; i
+= 4; }
131 if (!(b
&0x00000003)) { b
>>= 2; i
+= 2; }
132 if (!(b
&0x00000001)) { b
>>= 1; i
+= 1; }
137 static void put_event(unsigned evtype
, unsigned file
, secaddr pos
)
141 VEC_PUSH(ev
, &eventq
);
142 ev
->ev
= evtype
; ev
->file
= file
; ev
->pos
= pos
;
145 static void put_file(ident id
, secaddr start
, secaddr end
)
150 VEC_PUSH(f
, &filetab
); i
= f
- filetab
.v
;
151 f
->id
= id
; f
->start
= start
; f
->end
= end
;
152 put_event(EV_BEGIN
, i
, start
);
153 put_event(EV_END
, i
, end
);
156 static void put_menu(dvd_reader_t
*dvd
, unsigned title
)
158 ident id
= mkident(VOB
, title
, 0);
162 store_filename(fn
, id
);
163 start
= UDFFindFile(dvd
, fn
, &len
); if (!start
) return;
165 printf(";; %8"PRIuSEC
" .. %-8"PRIuSEC
": %s\n",
166 start
, start
+ SECTORS(len
), fn
);
168 put_file(id
, start
, start
+ SECTORS(len
));
171 static void put_title(dvd_reader_t
*dvd
, unsigned title
)
174 secaddr start
[9], len
[9];
177 for (i
= 0; i
< 9; i
++) {
178 store_filename(fn
, mkident(VOB
, title
, i
+ 1));
179 start
[i
] = UDFFindFile(dvd
, fn
, &len
[i
]); if (!start
[i
]) break;
181 npart
= i
; if (!npart
) return;
184 for (i
= 0; i
< npart
; i
++) {
185 store_filename(fn
, mkident(VOB
, title
, i
+ 1));
186 printf(";; %8"PRIuSEC
" .. %-8"PRIuSEC
": %s\n",
187 start
[i
], start
[i
] + SECTORS(len
[i
]), fn
);
192 for (i
= 0; i
< npart
- 1; i
++) {
194 bail("title %u part %u length = %"PRIuSEC
" not a multiple of %d",
195 title
, i
, len
[i
], SECTORSZ
);
196 if (start
[i
] + len
[i
]/SECTORSZ
!= start
[i
+ 1])
198 ("title %u part %u end = %"PRIuSEC
" /= part %u start = %"PRIuSEC
"",
199 title
, i
, start
[i
] + len
[i
]/SECTORSZ
, i
+ 1, start
[i
+ 1]);
202 put_file(mkident(VOB
, title
, 1),
203 start
[0], start
[npart
- 1] + SECTORS(len
[npart
- 1]));
206 static secaddr last_pos
, limit
, nsectors
, ndone
;
207 static struct timeval last_time
;
208 static double wsum
, wcount
;
209 static struct file
*file
;
210 static secaddr bad_start
;
211 static unsigned retry
, max_retries
= 4;
214 static const char throbber
[] = "|<-<|>->";
215 static unsigned throbix
= 0;
217 static double scale_bytes(double n
, const char **unit_out
)
219 const char *unit
= "";
221 if (n
> 1600) { n
/= 1024; unit
= "k"; }
222 if (n
> 1600) { n
/= 1024; unit
= "M"; }
223 if (n
> 1600) { n
/= 1024; unit
= "G"; }
224 if (n
> 1600) { n
/= 1024; unit
= "T"; }
226 *unit_out
= unit
; return (n
);
229 static struct progress_item
230 copy_progress
, disc_progress
,
231 file_progress
, badblock_progress
;
233 #define TIMESTRMAX 16
234 static char *fmttime(unsigned long t
, char *buf
)
236 if (t
< 60) sprintf(buf
, "%ld s", t
);
237 else if (t
< 3600) sprintf(buf
, "%ld:%02ld", t
/60, t
%60);
238 else sprintf(buf
, "%ld:%02ld:%02ld", t
/3600, (t
/60)%60, t
%60);
242 static void render_perfstats(struct progress_render_state
*render
)
245 char timebuf
[TIMESTRMAX
];
249 if (!wsum
|| !wcount
) { rate
= 0; eta
= -1; }
250 else { rate
= wsum
/wcount
; eta
= (int)((nsectors
- ndone
)/rate
+ 0.5); }
252 rate
= scale_bytes(rate
*SECTORSZ
, &unit
);
253 progress_putright(render
, "ETA %s ", rate ?
fmttime(eta
, timebuf
) : "???");
254 progress_putright(render
, "%.1f %sB/s, ", rate
, unit
);
257 static void render_copy_progress(struct progress_item
*item
,
258 struct progress_render_state
*render
)
260 double frac
= (double)ndone
/nsectors
;
262 progress_putleft(render
, " %c copied %.1f%%",
263 throbber
[throbix
], 100.0*frac
);
264 render_perfstats(render
);
265 progress_putleft(render
, " (%"PRIuSEC
" of %"PRIuSEC
")", ndone
, nsectors
);
267 progress_showbar(render
, frac
);
270 static void render_disc_progress(struct progress_item
*item
,
271 struct progress_render_state
*render
)
273 double frac
= (double)last_pos
/limit
;
275 progress_putleft(render
, " disc %.1f%% (%"PRIuSEC
" of %"PRIuSEC
")",
276 100.0*frac
, last_pos
, limit
);
277 progress_showbar(render
, frac
);
280 static void render_file_progress(struct progress_item
*item
,
281 struct progress_render_state
*render
)
283 secaddr off
= last_pos
- file
->start
, len
= file
->end
- file
->start
;
287 store_filename(fn
, file
->id
);
288 frac
= (double)off
/len
;
289 progress_putleft(render
, " `%s' %.1f%% (%"PRIuSEC
" of %"PRIuSEC
")",
290 fn
, 100.0*frac
, off
, len
);
291 progress_showbar(render
, frac
);
294 static void render_badblock_progress(struct progress_item
*item
,
295 struct progress_render_state
*render
)
297 secaddr n
= last_pos
- bad_start
;
301 progress_putleft(render
, " Retrying bad sector %"PRIuSEC
"", bad_start
);
302 progress_putright(render
, "attempt %u/%u ", retry
+ 1, max_retries
);
305 progress_putleft(render
, " Found %"PRIuSEC
" bad %s",
306 n
, n
== 1 ?
"sector" : "sectors");
307 progress_putright(render
, "%"PRIuSEC
" .. %"PRIuSEC
" ",
308 bad_start
, last_pos
);
311 if (bad_err
&& bad_err
!= EIO
)
312 progress_putleft(render
, " (%s)", strerror(bad_err
));
313 progress_shownotice(render
, bg
, 7);
316 static void update_progress(secaddr pos
)
321 gettimeofday(&now
, 0);
322 t
= tvdiff(&last_time
, &now
);
325 #define BETA (1 - ALPHA)
328 g
= wcount ?
pow(BETA
, t
) : 0.0; f
= (1 - g
)/(1 - BETA
);
329 wsum
= f
*(pos
- last_pos
)/t
+ g
*wsum
;
330 wcount
= f
+ g
*wcount
;
331 ndone
+= pos
- last_pos
;
332 last_time
= now
; last_pos
= pos
;
338 throbix
++; if (!throbber
[throbix
]) throbix
= 0;
341 static void report_progress(secaddr pos
)
342 { update_progress(pos
); progress_update(&progress
); }
344 static dvd_reader_t
*dvd
;
345 static int dvdfd
= -1, outfd
= -1;
346 static dvd_file_t
*vob
;
347 static const char *mapfile
; static FILE *mapfp
;
348 static const char *errfile
; static FILE *errfp
;
350 struct badblock
{ secaddr start
, end
; };
351 DEFVEC(badblock_v
, struct badblock
);
352 static badblock_v badblocks
= VEC_INIT
;
354 static int compare_badblock(const void *a
, const void *b
)
356 const struct badblock
*ba
= a
, *bb
= b
;
358 if (ba
->start
< bb
->start
) return (-1);
359 else if (ba
->start
> bb
->start
) return (+1);
361 if (ba
->end
< bb
->end
) return (-1);
362 else if (ba
->end
> bb
->end
) return (+1);
367 static double bad_block_delay
= 0.0;
368 static double good_block_delay
= 0.0;
370 static ssize_t
read_sectors(secaddr pos
, void *buf
, secaddr want
)
375 struct badblock
*bad
, *best
;
376 unsigned char *p
= buf
;
379 best
= 0; lo
= 0; hi
= badblocks
.n
;
382 printf(";; searching badblocks for %"PRIuSEC
" .. %"PRIuSEC
"\n",
386 mid
= lo
+ (hi
- lo
)/2; bad
= &badblocks
.v
[mid
];
388 printf(";; try %zu (%"PRIuSEC
" .. %"PRIuSEC
")... ",
389 mid
, bad
->start
, bad
->end
);
391 if (pos
< bad
->start
) { D( printf("high\n"); ) best
= bad
; hi
= mid
; }
392 else if (pos
>= bad
->end
) { D( printf("low\n"); ) lo
= mid
+ 1; }
393 else { D( printf("match!\n"); ) errno
= EIO
; return (-1); }
397 printf(";; next is %"PRIuSEC
" .. %"PRIuSEC
"\n",
398 best
->start
, best
->end
);
400 if (best
&& pos
+ want
> best
->start
)
401 { want
= best
->start
- pos
; fakeerr
= EIO
; sit(bad_block_delay
); }
406 { errno
= 0; n
= DVDReadBlocks(vob
, pos
- file
->start
, want
, p
); }
408 if (lseek(dvdfd
, (off_t
)pos
*SECTORSZ
, SEEK_SET
) < 0)
409 bail_syserr(errno
, "failed to seek to sector %"PRIuSEC
"", pos
);
410 errno
= 0; n
= read(dvdfd
, p
, want
*SECTORSZ
);
411 if (n
>= 0) n
/= SECTORSZ
;
413 memset(p
, 0, want
*SECTORSZ
);
417 if (n
> 0) { done
+= n
; pos
+= n
; p
+= n
*SECTORSZ
; want
-= n
; }
419 else if (errno
== EIO
&& errfile
) {
420 open_file_on_demand(errfile
, &errfp
, "bad-sector error log");
421 fprintf(errfp
, "%"PRIuSEC
" %"PRIuSEC
"\n", pos
, pos
+ 1);
422 check_write(errfp
, "bad-sector error log");
424 } else if (errno
!= EINTR
) break;
426 if (fakeerr
&& !errno
) errno
= fakeerr
;
427 else if (done
> 0 && good_block_delay
) sit(done
*good_block_delay
);
428 return (!done
&& errno ?
-1 : done
);
431 static void record_bad_sectors(secaddr bad_lo
, secaddr bad_hi
)
435 if (!mapfile
) return;
437 open_file_on_demand(mapfile
, &mapfp
, "bad-sector region map");
438 fprintf(mapfp
, "%"PRIuSEC
" %"PRIuSEC
" # %"PRIuSEC
" sectors",
439 bad_lo
, bad_hi
, bad_hi
- bad_lo
);
441 if (file
&& id_kind(file
->id
) != RAW
) {
442 store_filename(fn
, file
->id
);
443 fprintf(mapfp
, "; `%s' %"PRIuSEC
" .. %"PRIuSEC
" of %"PRIuSEC
"",
444 fn
, bad_lo
- file
->start
, bad_hi
- file
->start
,
445 file
->end
- file
->start
);
449 check_write(mapfp
, "bad-sector region map");
452 static void recovered(secaddr bad_lo
, secaddr bad_hi
)
456 progress_clear(&progress
);
458 if (!file
|| id_kind(file
->id
) == RAW
)
459 moan("skipping %"PRIuSEC
" bad sectors (%"PRIuSEC
" .. %"PRIuSEC
")",
460 bad_hi
- bad_lo
, bad_lo
, bad_hi
);
462 store_filename(fn
, file
->id
);
463 moan("skipping %"PRIuSEC
" bad sectors (%"PRIuSEC
" .. %"PRIuSEC
"; "
464 "`%s' %"PRIuSEC
" .. %"PRIuSEC
" of %"PRIuSEC
")",
465 bad_hi
- bad_lo
, bad_lo
, bad_hi
,
466 fn
, bad_lo
- file
->start
, bad_hi
- file
->start
,
467 file
->end
- file
->start
);
470 record_bad_sectors(bad_lo
, bad_hi
);
472 if (lseek(outfd
, (off_t
)(bad_hi
- bad_lo
)*SECTORSZ
, SEEK_CUR
) < 0)
473 bail_syserr(errno
, "failed to seek past bad sectors");
475 progress_removeitem(&progress
, &badblock_progress
);
476 progress_update(&progress
);
481 secaddr sz
, pos
, start
, end
;
482 secaddr good_lo
, good_hi
;
485 static void rearrange_sectors(struct recoverybuf
*r
,
486 secaddr dest
, secaddr src
, secaddr len
)
488 assert(dest
+ len
<= r
->sz
);
489 assert(src
+ len
<= r
->sz
);
490 memmove(r
->buf
+ dest
*SECTORSZ
, r
->buf
+ src
*SECTORSZ
, len
*SECTORSZ
);
494 static PRINTF_LIKE(2, 3)
495 void show_recovery_buffer_map(const struct recoverybuf
*r
,
496 const char *what
, ...)
502 printf(";; recovery buffer (");
505 "(%"PRIuSEC
") ..%"PRIuSEC
".. "
506 "[%"PRIuSEC
" ..%"PRIuSEC
".. %"PRIuSEC
"] "
507 "..%"PRIuSEC
".. (%"PRIuSEC
")\n",
509 r
->pos
+ r
->start
, r
->end
- r
->start
, r
->pos
+ r
->end
,
510 r
->sz
- r
->end
, r
->pos
+ r
->sz
);
512 assert(r
->start
<= r
->end
);
513 assert(r
->end
<= r
->sz
);
517 static ssize_t
recovery_read_sectors(struct recoverybuf
*r
,
518 secaddr pos
, secaddr off
, secaddr want
)
522 assert(off
<= r
->sz
); assert(want
<= r
->sz
- off
);
523 n
= read_sectors(pos
, r
->buf
+ off
*SECTORSZ
, want
);
527 static ssize_t
recovery_read_buffer(struct recoverybuf
*r
,
528 secaddr pos
, secaddr want
)
530 secaddr diff
, pp
, nn
;
535 show_recovery_buffer_map(r
, "begin(%"PRIuSEC
", %"PRIuSEC
")", pos
, want
);
540 if (r
->start
+ diff
>= r
->sz
) {
541 r
->pos
= pos
; r
->start
= r
->end
= 0;
543 show_recovery_buffer_map(r
, "cleared; shift up by %"PRIuSEC
"", diff
);
546 if (r
->end
+ diff
> r
->sz
) r
->end
= r
->sz
- diff
;
547 rearrange_sectors(r
, r
->start
+ diff
, r
->start
, r
->end
- r
->start
);
548 r
->pos
-= diff
; r
->start
+= diff
; r
->end
+= diff
;
550 show_recovery_buffer_map(r
, "shifted up by %"PRIuSEC
"", diff
);
553 } else if (pos
> r
->pos
+ r
->end
) {
554 r
->pos
= pos
; r
->start
= r
->end
= 0;
556 p
show_recovery_buffer_map(r
, "cleared; beyond previous region");
558 } else if (pos
+ want
> r
->pos
+ r
->sz
) {
559 diff
= (pos
+ want
) - (r
->pos
+ r
->sz
);
560 if (r
->end
<= diff
) {
561 r
->pos
= pos
; r
->start
= r
->end
= 0;
563 show_recovery_buffer_map(r
, "cleared; shift down by %"PRIuSEC
"", diff
);
566 if (r
->start
< diff
) r
->start
= diff
;
567 rearrange_sectors(r
, r
->start
- diff
, r
->start
, r
->end
- r
->start
);
568 r
->pos
+= diff
; r
->start
-= diff
; r
->end
-= diff
;
570 show_recovery_buffer_map(r
, "shifted down by %"PRIuSEC
"", diff
);
575 if (pos
< r
->pos
+ r
->start
) {
576 pp
= pos
- r
->pos
; nn
= r
->start
- pp
;
578 printf(";; read low (%"PRIuSEC
"@%"PRIuSEC
", %"PRIuSEC
")", pos
, pp
, nn
);
581 n
= recovery_read_sectors(r
, pos
, pp
, nn
);
583 printf(" -> %zd\n", n
);
586 if (n
>= 0 && n
> want
) n
= want
;
591 show_recovery_buffer_map(r
, "joined new region");
595 if (pos
+ want
> r
->pos
+ r
->end
) {
596 pp
= r
->end
; nn
= (pos
+ want
) - (r
->pos
+ r
->end
);
598 printf(";; read high (%"PRIuSEC
"@%"PRIuSEC
", %"PRIuSEC
")",
599 r
->pos
+ pp
, pp
, nn
);
602 n
= recovery_read_sectors(r
, r
->pos
+ pp
, pp
, nn
);
604 printf(" -> %zd\n", n
);
609 show_recovery_buffer_map(r
, "joined new region");
614 n
= r
->pos
+ r
->end
- pos
;
615 if (!n
&& want
) n
= -1;
616 else if (n
> want
) n
= want
;
620 show_recovery_buffer_map(r
, "done; return %zd", n
);
625 static ssize_t
recovery_read_multiple(struct recoverybuf
*r
,
626 secaddr pos
, secaddr want
)
629 secaddr skip
, want0
= want
;
631 while (want
> r
->sz
) {
633 n
= recovery_read_buffer(r
, pos
+ skip
, r
->sz
);
634 if (n
< r
->sz
) return (skip
+ (n
>= 0 ? n
: 0));
637 n
= recovery_read_buffer(r
, pos
, want
);
638 if (n
< 0 || n
< want
) return (n
);
642 static ssize_t
recovery_read(struct recoverybuf
*r
,
643 secaddr pos
, secaddr want
)
645 secaddr lo
= pos
, hi
= pos
+ want
, span
;
648 if (hi
< r
->good_lo
|| lo
> r
->good_hi
) {
649 n
= recovery_read_multiple(r
, lo
, hi
- lo
);
650 if (n
> 0) { r
->good_lo
= lo
; r
->good_hi
= lo
+ n
; }
654 if (hi
> r
->good_hi
) {
655 span
= hi
- r
->good_hi
;
656 n
= recovery_read_multiple(r
, r
->good_hi
, span
);
657 if (n
> 0) r
->good_hi
+= n
;
658 if (n
< 0 || n
< span
) return (r
->good_hi
- lo
);
661 if (lo
< r
->good_lo
) {
662 span
= r
->good_lo
- lo
;
663 n
= recovery_read_multiple(r
, lo
, span
);
664 if (n
== span
) r
->good_lo
= lo
;
668 n
= r
->good_hi
- pos
; if (n
> want
) n
= want
;
669 if (!n
) { errno
= EIO
; n
= -1; }
673 static double clear_factor
= 1.5;
674 static secaddr clear_min
= 1, clear_max
= SECLIMIT
;
675 static double step_factor
= 2.0;
676 static secaddr step_min
= 1, step_max
= 0;
678 static secaddr
run_length_wanted(secaddr pos
, secaddr badlen
, secaddr end
)
682 want
= clear_factor
*badlen
;
683 if (want
< clear_min
) want
= clear_min
;
684 if (want
> end
- pos
) want
= end
- pos
;
685 if (clear_max
&& want
> clear_max
) want
= clear_max
;
689 static void report_bad_blocks_progress(secaddr bad_hi
, int err
)
690 { bad_err
= err
; report_progress(bad_hi
); }
692 static ssize_t
find_good_sector(secaddr
*pos_inout
, secaddr end
,
693 unsigned char *buf
, secaddr sz
)
695 secaddr pos
= *pos_inout
, bad_lo
, bad_hi
, good
, step
, want
;
696 struct recoverybuf r
;
699 bad_start
= pos
; bad_err
= errno
;
700 badblock_progress
.render
= render_badblock_progress
;
701 progress_additem(&progress
, &badblock_progress
);
703 r
.buf
= buf
; r
.sz
= sz
; r
.pos
= r
.start
= r
.end
= 0;
704 r
.good_lo
= r
.good_hi
= 0;
706 want
= sz
; if (want
> end
- pos
) want
= end
- pos
;
707 for (retry
= 0; retry
< max_retries
; retry
++) {
708 report_bad_blocks_progress(pos
, errno
);
709 n
= recovery_read(&r
, pos
, want
);
711 progress_clear(&progress
);
712 printf(";; [retry] try reading %"PRIuSEC
" .. %"PRIuSEC
" -> %zd\n",
716 progress_clear(&progress
);
717 moan("sector %"PRIuSEC
" read ok after retry", pos
);
718 progress_removeitem(&progress
, &badblock_progress
);
719 progress_update(&progress
);
724 bad_lo
= pos
; bad_hi
= pos
+ 1;
726 report_bad_blocks_progress(bad_hi
, errno
);
728 progress_clear(&progress
);
729 printf(";; bounding bad-block region: "
730 "%"PRIuSEC
" ..%"PRIuSEC
".. %"PRIuSEC
"\n",
731 bad_lo
, bad_hi
- bad_lo
, bad_hi
);
734 progress_clear(&progress
);
735 moan("giving up on this extent");
736 recovered(bad_lo
, end
); *pos_inout
= end
;
739 step
= step_factor
*(bad_hi
- bad_lo
);
740 if (step
< step_min
) step
= step_min
;
741 if (step_max
&& step
> step_max
) step
= step_max
;
742 if (step
> end
- bad_lo
) step
= end
- bad_lo
;
743 pos
= bad_lo
+ step
- 1;
744 want
= run_length_wanted(pos
, step
, end
);
745 n
= recovery_read(&r
, pos
, want
);
747 printf(";; [bound] try reading %"PRIuSEC
" .. %"PRIuSEC
" -> %zd\n",
750 if (n
== want
) break;
752 bad_hi
= pos
+ n
+ 1;
756 while (good
> bad_hi
) {
757 report_bad_blocks_progress(bad_hi
, errno
);
759 progress_clear(&progress
);
760 printf(";; limiting bad-block region: "
761 "%"PRIuSEC
" ..%"PRIuSEC
".. %"PRIuSEC
" ..%"PRIuSEC
".. %"PRIuSEC
"\n",
762 bad_lo
, bad_hi
- bad_lo
, bad_hi
, good
- bad_hi
, good
);
764 pos
= bad_hi
+ (good
- bad_hi
)/2;
766 want
= run_length_wanted(pos
, step
, end
);
767 n
= recovery_read(&r
, pos
, want
);
769 printf(";; [limit] try reading %"PRIuSEC
" .. %"PRIuSEC
" -> %zd\n",
773 if (n
== want
) good
= pos
;
774 else bad_hi
= pos
+ n
+ 1;
776 recovered(bad_lo
, bad_hi
); *pos_inout
= good
;
777 if (good
< r
.pos
+ r
.start
|| r
.pos
+ r
.end
<= good
)
780 n
= r
.pos
+ r
.end
- good
;
781 rearrange_sectors(&r
, 0, good
- r
.pos
, n
);
784 show_recovery_buffer_map(&r
, "returning %zd good sectors at %"PRIuSEC
"",
790 static void emit(secaddr start
, secaddr end
)
792 #define BUFSECTORS 512
795 unsigned char buf
[BUFSECTORS
*SECTORSZ
];
799 static int first_time
= 1;
807 least
= least_live();
810 printf(";; %8"PRIuSEC
" .. %"PRIuSEC
"\n", start
, end
);
812 for (i
= 0; i
< filetab
.n
; i
++) {
813 if (!livep(i
)) continue;
814 if (act
== -1) act
= i
;
815 f
= &filetab
.v
[i
]; store_filename(fn
, f
->id
);
816 printf(";;\t\t%8"PRIuSEC
" .. %-8"PRIuSEC
" %s\n",
817 start
- f
->start
, end
- f
->start
, fn
);
819 if (act
== -1) printf(";;\t\t#<no live source>\n");
820 assert(act
== least
);
824 { file
= 0; vob
= 0; }
826 file
= &filetab
.v
[least
];
827 switch (id_kind(file
->id
)) {
832 if (first_time
) { progress_clear(&progress
); first_time
= 0; }
833 vob
= DVDOpenFile(dvd
, id_title(file
->id
),
835 ? DVD_READ_TITLE_VOBS
836 : DVD_READ_MENU_VOBS
);
838 bail("failed to open %s %u",
839 id_part(file
->id
) ?
"title" : "menu",
841 progress_update(&progress
);
848 if (file
&& id_kind(file
->id
) != RAW
) {
849 file_progress
.render
= render_file_progress
;
850 progress_additem(&progress
, &file_progress
);
855 want
= end
- pos
; if (want
> BUFSECTORS
) want
= BUFSECTORS
;
856 n
= read_sectors(pos
, buf
, want
);
858 if (n
<= 0) n
= find_good_sector(&pos
, end
, buf
, BUFSECTORS
);
859 if (n
> 0) { carefully_write(outfd
, buf
, n
*SECTORSZ
); pos
+= n
; }
860 report_progress(pos
);
863 if (vob
) { DVDCloseFile(vob
); vob
= 0; }
865 if (file
&& id_kind(file
->id
) != RAW
)
866 progress_removeitem(&progress
, &file_progress
);
867 progress_update(&progress
);
876 #define BUF_INIT { 0, 0, 0 }
877 #define BUF_REWIND(b) do { (b)->n = 0; } while (0)
878 #define BUF_FREE(b) do { \
880 free(_b->p); _b->p = 0; _b->n = _b->sz = 0; \
882 #define BUF_PUTC(b, ch) do { \
883 struct buf *_b = (b); \
884 if (_b->n >= _b->sz) { \
885 _b->sz = _b->sz ? 2*_b->sz : 32; \
886 _b->p = realloc(_b->p, _b->sz); \
887 if (!_b->p) bail("out of memory allocating %zu bytes", _b->sz); \
889 _b->p[_b->n] = (ch); \
892 static int read_line(FILE *fp
, struct buf
*b
)
899 else if (ch
!= '\n') do {
900 BUF_PUTC(b
, ch
); b
->n
++;
902 } while (ch
!= EOF
&& ch
!= '\n');
907 static double parse_float(const char **p_inout
, double min
, double max
,
915 err
= errno
; errno
= 0;
918 if (errno
|| x
< min
|| x
> max
) bail("bad %s `%s'", what
, p
);
919 *p_inout
= q
; errno
= err
;
923 static long parse_int(const char **p_inout
, long min
, long max
,
931 err
= errno
; errno
= 0;
933 x
= strtoul(p
, &q
, 0);
934 if (errno
|| x
< min
|| x
> max
) bail("bad %s `%s'", what
, p
);
935 *p_inout
= q
; errno
= err
;
939 #define PRF_HYPHEN 1u
940 static int parse_range(const char *p
, unsigned f
,
941 secaddr
*start_out
, secaddr
*end_out
)
945 unsigned long start
, end
;
950 start
= strtoul(p
, &q
, 0);
951 if (errno
|| start
>= SECLIMIT
) { rc
= -1; goto end
; }
952 *start_out
= start
; p
= q
;
953 } else if (!(f
&PRF_HYPHEN
))
954 { rc
= -1; goto end
; }
959 if (*p
!= '-') { rc
= -1; goto end
; }
962 if (!ISSPACE(*p
)) { rc
= -1; goto end
; }
963 do p
++; while (ISSPACE(*p
));
967 end
= strtoul(p
, &q
, 0);
968 if (errno
|| end
> SECLIMIT
|| end
< start
) { rc
= -1; goto end
; }
969 *end_out
= end
; p
= q
;
970 } else if (!(f
&PRF_HYPHEN
))
971 { rc
= -1; goto end
; }
973 if (!(f
&PRF_HYPHEN
)) while (ISSPACE(*p
)) p
++;
974 if (*p
&& ((f
&PRF_HYPHEN
) || *p
!= '#')) { rc
= -1; goto end
; }
982 int main(int argc
, char *argv
[])
989 secaddr start
, end
, last
;
990 const struct event
*ev
;
991 const char *device
, *outfile
;
992 struct badblock
*bad
;
997 struct buf buf
= BUF_INIT
;
998 struct timeval tv0
, tv1
;
1000 const char *rateunit
, *totunit
;
1001 char timebuf
[TIMESTRMAX
];
1004 const struct file
*file
;
1009 #define f_continue 2u
1012 #define f_write 256u
1016 opt
= getopt(argc
, argv
, "hB:E:FR:X:b:cr:s"); if (opt
< 0) break;
1018 case 'h': usage(stderr
); exit(0);
1021 #define SKIP_PREFIX(s) \
1022 (STRNCMP(p, ==, s "=", sizeof(s)) && (p += sizeof(s), 1))
1024 if (SKIP_PREFIX("cf"))
1025 clear_factor
= parse_float(&p
, 0, DBL_MAX
, "clear factor");
1026 else if (SKIP_PREFIX("cmin"))
1027 clear_min
= parse_int(&p
, 1, SECLIMIT
, "clear minimum");
1028 else if (SKIP_PREFIX("cmax"))
1029 clear_max
= parse_int(&p
, 1, SECLIMIT
, "clear maximum");
1030 else if (SKIP_PREFIX("sf"))
1031 step_factor
= parse_float(&p
, 0, DBL_MAX
, "step factor");
1032 else if (SKIP_PREFIX("smin"))
1033 step_min
= parse_int(&p
, 1, SECLIMIT
- 1, "step minimum");
1034 else if (SKIP_PREFIX("smax"))
1035 step_max
= parse_int(&p
, 1, SECLIMIT
- 1, "step maximum");
1036 else if (SKIP_PREFIX("retry"))
1037 max_retries
= parse_int(&p
, 0, INT_MAX
, "retries");
1038 else if (SKIP_PREFIX("_badwait"))
1039 bad_block_delay
= parse_float(&p
, 0, DBL_MAX
, "bad-block delay");
1040 else if (SKIP_PREFIX("_blkwait"))
1041 good_block_delay
= parse_float(&p
, 0, DBL_MAX
, "good block delay");
1043 bail("unknown bad blocks parameter `%s'", p
);
1045 else if (*p
!= ',') bail("unexpected junk in parameters");
1050 case 'E': errfile
= optarg
; break;
1051 case 'F': f
|= f_fixup
; break;
1053 fp
= fopen(optarg
, "r");
1055 bail_syserr(errno
, "failed to open ranges file `%s'", optarg
);
1058 BUF_REWIND(&buf
); if (read_line(fp
, &buf
)) break;
1060 while (ISSPACE(*p
)) p
++;
1061 if (!*p
|| *p
== '#') continue;
1062 if (parse_range(p
, 0, &start
, &end
) ||
1063 (last
<= SECLIMIT
&& start
< last
))
1064 bail("bad range `%s' at `%s' line %zu", buf
.p
, optarg
, i
);
1067 eventq
.v
[eventq
.n
- 1].pos
= end
;
1069 put_event(EV_WRITE
, 0, start
);
1070 put_event(EV_STOP
, 0, end
);
1076 bail_syserr(errno
, "failed to read ranges file `%s'", optarg
);
1079 fp
= fopen(optarg
, "r");
1081 bail_syserr(errno
, "failed to open bad-blocks file `%s'", optarg
);
1084 BUF_REWIND(&buf
); if (read_line(fp
, &buf
)) break;
1086 while (ISSPACE(*p
)) p
++;
1087 if (!*p
|| *p
== '#') continue;
1088 if (parse_range(p
, 0, &start
, &end
) ||
1089 (last
<= SECLIMIT
&& start
< last
))
1090 bail("bad range `%s' at `%s' line %zu", buf
.p
, optarg
, i
);
1092 { VEC_PUSH(bad
, &badblocks
); bad
->start
= start
; bad
->end
= end
; }
1095 bail_syserr(errno
, "failed to read bad-blocks file `%s'", optarg
);
1098 if (mapfile
) bail("can't have multiple map files");
1101 case 'c': f
|= f_continue
; break;
1103 start
= 0; end
= -1;
1104 if (parse_range(optarg
, PRF_HYPHEN
, &start
, &end
))
1105 bail("bad range `%s'", optarg
);
1107 put_event(EV_WRITE
, 0, start
);
1108 if (end
<= SECLIMIT
) put_event(EV_STOP
, 0, end
);
1111 case 's': f
|= f_stats
; break;
1112 default: f
|= f_bogus
; break;
1115 if (argc
- optind
!= 2) f
|= f_bogus
;
1116 if (f
&f_bogus
) { usage(stderr
); exit(2); }
1118 setlocale(LC_ALL
, "");
1119 progress_init(&progress
);
1120 device
= argv
[optind
]; outfile
= argv
[optind
+ 1];
1123 qsort(badblocks
.v
, badblocks
.n
, sizeof(struct badblock
),
1126 printf(";; fake bad blocks:\n");
1127 for (i
= 0; i
< badblocks
.n
; i
++)
1128 printf(";;\t%8"PRIuSEC
" .. %"PRIuSEC
"\n",
1129 badblocks
.v
[i
].start
, badblocks
.v
[i
].end
);
1133 open_dvd(device
, &dvdfd
, &dvd
);
1134 if (fstat(dvdfd
, &st
))
1135 bail_syserr(errno
, "failed to stat device `%s'", device
);
1136 if (S_ISREG(st
.st_mode
)) {
1139 } else if (S_ISBLK(st
.st_mode
)) {
1140 if (ioctl(dvdfd
, BLKSSZGET
, &blksz
))
1141 bail_syserr(errno
, "failed to get block size for `%s'", device
);
1142 if (ioctl(dvdfd
, BLKGETSIZE64
, &volsz
))
1143 bail_syserr(errno
, "failed to get volume size for `%s'", device
);
1145 bail("can't use `%s' as source: expected file or block device", device
);
1147 if (blksz
!= SECTORSZ
)
1148 bail("device `%s' block size %d /= %d", device
, blksz
, SECTORSZ
);
1150 bail("device `%s' volume size %"PRIu64
" not a multiple of %d",
1151 device
, volsz
, SECTORSZ
);
1153 outfd
= open(outfile
, O_WRONLY
| O_CREAT
, 0666);
1155 bail_syserr(errno
, "failed to create output file `%s'", outfile
);
1158 off
= lseek(outfd
, 0, SEEK_END
);
1160 bail_syserr(errno
, "failed to seek to end of output file `%s'",
1162 put_event(EV_WRITE
, 0, off
/SECTORSZ
);
1163 } else if (!eventq
.n
&& !(f
&f_fixup
))
1164 put_event(EV_WRITE
, 0, 0);
1166 /* It's fast enough just to check everything. */
1168 for (i
= 1; i
< 100; i
++) {
1172 put_file(mkident(RAW
, 0, 0), 0, volsz
/SECTORSZ
);
1173 assert(filetab
.n
<= MAXFILES
);
1175 for (i
= 0, limit
= 0; i
< filetab
.n
; i
++)
1176 if (filetab
.v
[i
].end
> limit
) limit
= filetab
.v
[i
].end
;
1178 if (end
> limit
) end
= limit
;
1181 printf("\n;; files:\n");
1182 for (i
= 0; i
< filetab
.n
; i
++) {
1183 file
= &filetab
.v
[i
];
1184 store_filename(fn
, file
->id
);
1185 printf(";;\t%8"PRIuSEC
" %s\n", file
->start
, fn
);
1189 qsort(eventq
.v
, eventq
.n
, sizeof(struct event
), compare_event
);
1191 f
&= ~f_write
; start
= 0; n
= 0;
1192 for (i
= 0; i
< eventq
.n
; i
++) {
1197 bail("overlapping ranges: range from %"PRIuSEC
" still open at %"PRIuSEC
"",
1199 n
++; f
|= f_write
; start
= ev
->pos
;
1207 f
&= ~f_write
; start
= 0;
1208 for (i
= 0; i
< eventq
.n
; i
++) {
1211 case EV_WRITE
: start
= ev
->pos
; f
|= f_write
; break;
1213 nsectors
+= ev
->pos
- start
; f
&= ~f_write
; break;
1215 if (ev
->pos
>= limit
) break;
1216 if (f
&f_fixup
) start
= ev
->pos
;
1220 put_event(EV_WRITE
, 0, start
);
1224 nsectors
+= limit
- start
;
1225 put_event(EV_STOP
, 0, limit
);
1228 copy_progress
.render
= render_copy_progress
;
1229 progress_additem(&progress
, ©_progress
);
1230 if (nsectors
== limit
- start
)
1231 { ndone
= start
; nsectors
= limit
; }
1233 disc_progress
.render
= render_disc_progress
;
1234 progress_additem(&progress
, &disc_progress
);
1237 if (f
&f_stats
) gettimeofday(&tv0
, 0);
1240 printf("\n;; event sweep:\n");
1243 for (pos
= 0, i
= 0; i
< eventq
.n
; i
++) {
1245 if (ev
->pos
> pos
) {
1246 if (f
&f_write
) emit(pos
, ev
->pos
);
1249 progress_clear(&progress
);
1257 store_filename(fn
, filetab
.v
[ev
->file
].id
);
1258 progress_clear(&progress
);
1259 printf(";; %8"PRIuSEC
": begin `%s'\n", pos
, fn
);
1263 gettimeofday(&last_time
, 0); last_pos
= pos
;
1264 if (lseek(outfd
, (off_t
)ev
->pos
*SECTORSZ
, SEEK_SET
) < 0)
1266 "failed to seek to resume position "
1267 "(sector %"PRIuSEC
") in output file `%s'",
1270 progress_clear(&progress
);
1271 printf(";; %8"PRIuSEC
": begin write\n", pos
);
1278 progress_clear(&progress
);
1279 printf(";; %8"PRIuSEC
": end write\n", pos
);
1283 clear_live(ev
->file
);
1285 store_filename(fn
, filetab
.v
[ev
->file
].id
);
1286 progress_clear(&progress
);
1287 printf(";; %8"PRIuSEC
": end `%s'\n", pos
, fn
);
1294 progress_clear(&progress
);
1296 if (ftruncate(outfd
, (off_t
)limit
*SECTORSZ
) < 0)
1297 bail_syserr(errno
, "failed to set output file `%s' length", outfile
);
1300 gettimeofday(&tv1
, 0); t
= tvdiff(&tv0
, &tv1
);
1301 if (nsectors
== limit
- start
) { ndone
-= start
; nsectors
-= start
; }
1302 tot
= scale_bytes((double)nsectors
*SECTORSZ
, &totunit
);
1303 rate
= scale_bytes((double)nsectors
*SECTORSZ
/t
, &rateunit
);
1304 moan("all done: %.1f %sB in %s -- %.1f %sB/s",
1305 tot
, totunit
, fmttime(t
, timebuf
), rate
, rateunit
);
1308 if (dvd
) DVDClose(dvd
);
1309 if (dvdfd
>= 0) close(dvdfd
);
1310 if (outfd
>= 0) close(outfd
);
1311 carefully_fclose(mapfp
, "bad-sector region map");
1312 carefully_fclose(errfp
, "bad-sector error log");
1313 progress_free(&progress
);