2 #define _FILE_OFFSET_BITS 64
18 #include <sys/ioctl.h>
26 #include <dvdread/dvd_reader.h>
27 #include <dvdread/dvd_udf.h>
28 #include <dvdread/ifo_read.h>
29 #include <dvdread/ifo_types.h>
31 #define CTYPE_HACK(fn, ch) fn((unsigned char)(ch))
32 #define ISDIGIT(ch) CTYPE_HACK(isdigit, ch)
33 #define ISSPACE(ch) CTYPE_HACK(isspace, ch)
41 #define N(v) (sizeof(v)/sizeof((v)[0]))
44 #define SECTORS(n) (((n) + (SECTORSZ - 1))/SECTORSZ)
46 static const char *prog
= "<unset>";
47 static int status
= 0;
49 static void usage(FILE *fp
)
52 "usage: %s [-c] [-R MAP] [-b OUTMAP] [-r [START]-[END]]\n"
57 static void vmoan(const char *fmt
, va_list ap
)
58 { fprintf(stderr
, "%s: ", prog
); vfprintf(stderr
, fmt
, ap
); }
60 __attribute__((format(printf
, 1, 2)))
61 static void moan(const char *fmt
, ...)
65 va_start(ap
, fmt
); vmoan(fmt
, ap
); va_end(ap
);
69 __attribute__((noreturn
, format(printf
, 1, 2)))
70 static void bail(const char *fmt
, ...)
74 va_start(ap
, fmt
); vmoan(fmt
, ap
); va_end(ap
);
79 __attribute__((noreturn
, format(printf
, 2, 3)))
80 static void bail_syserr(int err
, const char *fmt
, ...)
84 va_start(ap
, fmt
); vmoan(fmt
, ap
); va_end(ap
);
85 if (err
) fprintf(stderr
, ": %s", strerror(errno
));
90 static void carefully_write(int fd
, const void *buf
, size_t sz
)
92 const unsigned char *p
= buf
;
99 if (errno
== EINTR
) continue;
100 bail_syserr(errno
, "failed to write to output file");
102 if (!n
) bail("unexpected short write to output file");
107 static void open_file_on_demand(const char *file
, FILE **fp_inout
,
113 fp
= fopen(file
, "w");
115 bail_syserr(errno
, "failed to open %s file `%s'", what
, file
);
116 fprintf(fp
, "## %s\n\n", what
);
121 static void check_write(FILE *fp
, const char *what
)
124 if (ferror(fp
)) bail_syserr(errno
, "error writing %s file", what
);
127 static void carefully_fclose(FILE *fp
, const char *what
)
129 if (fp
&& (ferror(fp
) || fclose(fp
)))
130 bail_syserr(errno
, "error writing %s file", what
);
133 #define DEFVEC(vtype, etype) \
134 typedef struct { etype *v; size_t n, sz; } vtype
135 #define VEC_INIT { 0, 0, 0 }
136 #define VEC_FREE(vv) do { \
137 free((vv)->v); (vv)->v 0; (vv)->n = (vv)->sz = 0; \
139 #define VEC_PUSH(p, vv) do { \
141 if ((vv)->n >= (vv)->sz) { \
142 (vv)->sz = (vv)->sz ? 2*(vv)->sz : 32; \
143 _want = (vv)->sz*sizeof(*(vv)->v); \
144 (vv)->v = realloc((vv)->v, _want); \
145 if (!(vv)->v) bail("out of memory allocating %zu bytes", _want); \
147 (p) = &(vv)->v[(vv)->n++]; \
150 enum { RAW
, IFO
, VOB
, BUP
};
151 typedef uint_least32_t ident
;
153 static inline ident
mkident(unsigned kind
, unsigned title
, unsigned part
)
154 { return (((ident
)kind
<< 0) | ((ident
)title
<< 8) | ((ident
)part
<< 16)); }
155 static inline unsigned id_kind(ident id
) { return ((id
>> 0)&0x0ff); }
156 static inline unsigned id_title(ident id
) { return ((id
>> 8)&0x0ff); }
157 static inline unsigned id_part(ident id
) { return ((id
>> 16)&0x0ff); }
159 #define MAXFNSZ (1 + 8 + 1 + 12 + 1)
161 static void store_filename(char *buf
, ident id
)
163 switch (id_kind(id
)) {
165 sprintf(buf
, "#<raw device>");
168 if (!id_title(id
)) sprintf(buf
, "/VIDEO_TS/VIDEO_TS.IFO");
169 else sprintf(buf
, "/VIDEO_TS/VTS_%02u_0.IFO", id_title(id
));
172 if (!id_title(id
)) sprintf(buf
, "/VIDEO_TS/VIDEO_TS.BUP");
173 else sprintf(buf
, "/VIDEO_TS/VTS_%02u_0.BUP", id_title(id
));
176 if (!id_title(id
)) sprintf(buf
, "/VIDEO_TS/VIDEO_TS.VOB");
178 sprintf(buf
, "/VIDEO_TS/VTS_%02u_%u.VOB", id_title(id
), id_part(id
));
185 typedef uint_least32_t secaddr
;
186 #define PRIuSEC PRIuLEAST32
187 #define SECLIMIT 0x00400000
189 #define MAXFILES (1 + 2*99 + 1)
194 DEFVEC(file_v
, struct file
);
195 static file_v filetab
= VEC_INIT
;
197 enum { EV_STOP
, EV_BEGIN
, EV_END
, EV_WRITE
};
199 unsigned char ev
, file
;
202 DEFVEC(event_v
, struct event
);
203 static event_v eventq
= VEC_INIT
;
205 static int compare_event(const void *a
, const void *b
)
207 const struct event
*eva
= a
, *evb
= b
;
209 if (eva
->pos
< evb
->pos
) return (-1);
210 else if (eva
->pos
> evb
->pos
) return (+1);
212 if (eva
->ev
< evb
->ev
) return (-1);
213 else if (eva
->ev
> evb
->ev
) return (+1);
215 if (eva
->file
< evb
->file
) return (-1);
216 else if (eva
->file
> evb
->file
) return (+1);
221 typedef uint_least32_t bits
;
222 static bits live
[(MAXFILES
+ 31)/32];
224 static inline int livep(unsigned i
)
225 { return (live
[i
/32]&((bits
)1 << (i
%32))); }
226 static inline void set_live(unsigned i
)
227 { live
[i
/32] |= (bits
)1 << (i
%32); }
228 static inline void clear_live(unsigned i
)
229 { live
[i
/32] &= ~((bits
)1 << (i
%32)); }
230 static inline int least_live(void)
232 unsigned i
, n
= (filetab
.n
+ 32)/32;
235 for (i
= 0; i
< n
; i
++) { b
= live
[i
]; if (b
) goto found
; }
239 if (!(b
&0x0000ffff)) { b
>>= 16; i
+= 16; }
240 if (!(b
&0x000000ff)) { b
>>= 8; i
+= 8; }
241 if (!(b
&0x0000000f)) { b
>>= 4; i
+= 4; }
242 if (!(b
&0x00000003)) { b
>>= 2; i
+= 2; }
243 if (!(b
&0x00000001)) { b
>>= 1; i
+= 1; }
248 static void put_event(unsigned evtype
, unsigned file
, secaddr pos
)
252 VEC_PUSH(ev
, &eventq
);
253 ev
->ev
= evtype
; ev
->file
= file
; ev
->pos
= pos
;
256 static void put_file(ident id
, secaddr start
, secaddr end
)
261 VEC_PUSH(f
, &filetab
); i
= f
- filetab
.v
;
262 f
->id
= id
; f
->start
= start
; f
->end
= end
;
263 put_event(EV_BEGIN
, i
, start
);
264 put_event(EV_END
, i
, end
);
267 static void put_menu(dvd_reader_t
*dvd
, unsigned title
)
269 ident id
= mkident(VOB
, title
, 0);
273 store_filename(fn
, id
);
274 start
= UDFFindFile(dvd
, fn
, &len
); if (!start
) return;
276 printf(";; %8"PRIuSEC
" .. %-8"PRIuSEC
": %s\n",
277 start
, start
+ SECTORS(len
), fn
);
279 put_file(id
, start
, start
+ SECTORS(len
));
282 static void put_title(dvd_reader_t
*dvd
, unsigned title
)
285 secaddr start
[9], len
[9];
288 for (i
= 0; i
< 9; i
++) {
289 store_filename(fn
, mkident(VOB
, title
, i
+ 1));
290 start
[i
] = UDFFindFile(dvd
, fn
, &len
[i
]); if (!start
[i
]) break;
292 npart
= i
; if (!npart
) return;
295 for (i
= 0; i
< npart
; i
++) {
296 store_filename(fn
, mkident(VOB
, title
, i
+ 1));
297 printf(";; %8"PRIuSEC
" .. %-8"PRIuSEC
": %s\n",
298 start
[i
], start
[i
] + SECTORS(len
[i
]), fn
);
303 for (i
= 0; i
< npart
- 1; i
++) {
305 bail("title %u part %u length = %"PRIuSEC
" not a multiple of %d",
306 title
, i
, len
[i
], SECTORSZ
);
307 if (start
[i
] + len
[i
]/SECTORSZ
!= start
[i
+ 1])
309 ("title %u part %u end = %"PRIuSEC
" /= part %u start = %"PRIuSEC
"",
310 title
, i
, start
[i
] + len
[i
]/SECTORSZ
, i
+ 1, start
[i
+ 1]);
313 put_file(mkident(VOB
, title
, 1),
314 start
[0], start
[npart
- 1] + SECTORS(len
[npart
- 1]));
317 static int progresslen
= 0;
319 static void clear_progress_internal(void)
321 while (progresslen
) { fputs("\b \b", stdout
); progresslen
--; }
324 static void clear_progress(void)
325 { clear_progress_internal(); fflush(stdout
); }
327 static void debug_clear_progress(void)
328 { if (progresslen
) { putchar('\n'); progresslen
= 0; } }
330 static void vappend_progress(const char *fmt
, va_list ap
)
331 { progresslen
+= vprintf(fmt
, ap
); }
332 __attribute__((format(printf
, 1, 2)))
333 static void append_progress(const char *fmt
, ...)
338 vappend_progress(fmt
, ap
);
341 __attribute__((format(printf
, 1, 2)))
342 static void print_progress(const char *fmt
, ...)
347 clear_progress_internal();
348 vappend_progress(fmt
, ap
);
353 # define F_ALLPROGRESS 1u
354 static secaddr last_pos
, limit
, nsectors
, ndone
;
355 static struct timeval last_time
;
356 static double wsum
, wcount
;
357 static struct file
*file
;
359 static void report_progress(secaddr pos
)
364 double percent
, t
, f
, g
, rate
;
368 #define BETA (1 - ALPHA)
370 gettimeofday(&now
, 0);
371 t
= (now
.tv_sec
- last_time
.tv_sec
) +
372 (now
.tv_usec
- last_time
.tv_usec
)/1000000.0;
375 g
= wcount ?
pow(BETA
, t
) : 0.0; f
= (1 - g
)/(1 - BETA
);
376 wsum
= f
*(pos
- last_pos
)/t
+ g
*wsum
;
377 wcount
= f
+ g
*wcount
;
378 ndone
+= pos
- last_pos
;
379 last_time
= now
; last_pos
= pos
;
382 if (!wsum
|| !wcount
)
383 { rate
= 0; strcpy(etastr
, "???"); }
386 eta
= (int)((nsectors
- ndone
)/rate
);
387 sprintf(etastr
, "%d:%02d:%02d", eta
/3600, (eta
/60)%60, eta
%60);
390 rate
*= SECTORSZ
; unit
= "";
391 if (rate
> 128) { rate
/= 1024; unit
= "k"; }
392 if (rate
> 128) { rate
/= 1024; unit
= "M"; }
393 if (rate
> 128) { rate
/= 1024; unit
= "G"; }
395 if (flags
&F_ALLPROGRESS
) percent
= pos
*100.0/limit
;
396 else percent
= ndone
*100.0/nsectors
;
398 ("copied %.1f%% (%"PRIuSEC
" of %"PRIuSEC
"; %.1f %sB/s, ETA %s)",
399 percent
, pos
, limit
, rate
, unit
, etastr
);
400 if (file
&& id_kind(file
->id
) == VOB
) {
401 append_progress(" -- %s %d %.1f%%",
402 id_part(file
->id
) ?
"title" : "menu",
404 (pos
- file
->start
)*100.0/
405 (file
->end
- file
->start
));
412 static dvd_reader_t
*dvd
;
413 static int dvdfd
= -1, outfd
= -1;
414 static dvd_file_t
*vob
;
415 static const char *mapfile
; static FILE *mapfp
;
416 static const char *errfile
; static FILE *errfp
;
418 struct badblock
{ secaddr start
, end
; };
419 DEFVEC(badblock_v
, struct badblock
);
420 static badblock_v badblocks
= VEC_INIT
;
422 static int compare_badblock(const void *a
, const void *b
)
424 const struct badblock
*ba
= a
, *bb
= b
;
426 if (ba
->start
< bb
->start
) return (-1);
427 else if (ba
->start
> bb
->start
) return (+1);
429 if (ba
->end
< bb
->end
) return (-1);
430 else if (ba
->end
> bb
->end
) return (+1);
435 static ssize_t
read_sectors(secaddr pos
, void *buf
, secaddr want
)
440 struct badblock
*bad
, *best
;
441 unsigned char *p
= buf
;
444 best
= 0; lo
= 0; hi
= badblocks
.n
;
446 debug_clear_progress();
447 printf(";; searching badblocks for %"PRIuSEC
" .. %"PRIuSEC
"\n",
451 mid
= lo
+ (hi
- lo
)/2; bad
= &badblocks
.v
[mid
];
453 printf(";; try %zu (%"PRIuSEC
" .. %"PRIuSEC
")... ",
454 mid
, bad
->start
, bad
->end
);
456 if (pos
< bad
->start
) { D( printf("high\n"); ) best
= bad
; hi
= mid
; }
457 else if (pos
>= bad
->end
) { D( printf("low\n"); ) lo
= mid
+ 1; }
458 else { D( printf("match!\n"); ) errno
= EIO
; return (-1); }
462 printf(";; next is %"PRIuSEC
" .. %"PRIuSEC
"\n",
463 best
->start
, best
->end
);
465 if (best
&& pos
+ want
> best
->start
)
466 { want
= best
->start
- pos
; fakeerr
= EIO
; }
471 { errno
= 0; n
= DVDReadBlocks(vob
, pos
- file
->start
, want
, p
); }
473 if (lseek(dvdfd
, (off_t
)pos
*SECTORSZ
, SEEK_SET
) < 0)
474 bail_syserr(errno
, "failed to seek to sector %"PRIuSEC
"", pos
);
475 errno
= 0; n
= read(dvdfd
, p
, want
*SECTORSZ
);
476 if (n
>= 0) n
/= SECTORSZ
;
478 memset(p
, 0, want
*SECTORSZ
);
482 if (n
> 0) { done
+= n
; pos
+= n
; p
+= n
*SECTORSZ
; want
-= n
; }
484 else if (errno
== EIO
&& errfile
) {
485 open_file_on_demand(errfile
, &errfp
, "bad-sector error log");
486 fprintf(errfp
, "%"PRIuSEC
" %"PRIuSEC
"\n", pos
, pos
+ 1);
487 check_write(errfp
, "bad-sector error log");
489 } else if (errno
!= EINTR
) break;
491 if (fakeerr
&& !errno
) errno
= fakeerr
;
492 return (!done
&& errno ?
-1 : done
);
495 static void report_bad_blocks_progress(secaddr lo
, secaddr hi
, int err
)
499 if (lo
== hi
) append_progress(": retrying bad sector");
501 append_progress(": %"PRIuSEC
" bad %s",
502 hi
- lo
, hi
== lo
+ 1 ?
"sector" : "sectors");
503 if (err
&& err
!= EIO
) append_progress(" (%s)", strerror(err
));
507 static void recovered(secaddr bad_lo
, secaddr bad_hi
)
510 moan("skipping %"PRIuSEC
" bad sectors (%"PRIuSEC
" .. %"PRIuSEC
")",
511 bad_hi
- bad_lo
, bad_lo
, bad_hi
);
513 open_file_on_demand(mapfile
, &mapfp
, "bad-sector region map");
514 fprintf(mapfp
, "%"PRIuSEC
" %"PRIuSEC
"", bad_lo
, bad_hi
);
516 fprintf(mapfp
, " # %s #%d %"PRIuSEC
"..%"PRIuSEC
" of %"PRIuSEC
" (%.1f%%)",
517 id_part(file
->id
) ?
"title" : "menu",
519 bad_lo
- file
->start
, bad_hi
- file
->start
,
520 file
->end
- file
->start
,
521 (bad_lo
- file
->start
)*100.0/(file
->end
- file
->start
));
523 check_write(mapfp
, "bad-sector region map");
525 if (lseek(outfd
, (off_t
)(bad_hi
- bad_lo
)*SECTORSZ
, SEEK_CUR
) < 0)
526 bail_syserr(errno
, "failed to seek past bad sectors");
532 secaddr sz
, pos
, start
, end
;
535 static void rearrange_sectors(struct recoverybuf
*r
,
536 secaddr dest
, secaddr src
, secaddr len
)
538 assert(dest
+ len
<= r
->sz
);
539 assert(src
+ len
<= r
->sz
);
540 memmove(r
->buf
+ dest
*SECTORSZ
, r
->buf
+ src
*SECTORSZ
, len
*SECTORSZ
);
544 __attribute__((format(printf
, 2, 3)))
545 static void show_recovery_buffer_map(const struct recoverybuf
*r
,
546 const char *what
, ...)
551 debug_clear_progress();
552 printf(";; recovery buffer (");
555 "(%"PRIuSEC
") ..%"PRIuSEC
".. "
556 "[%"PRIuSEC
" ..%"PRIuSEC
".. %"PRIuSEC
"] "
557 "..%"PRIuSEC
".. (%"PRIuSEC
")\n",
559 r
->pos
+ r
->start
, r
->end
- r
->start
, r
->pos
+ r
->end
,
560 r
->sz
- r
->end
, r
->pos
+ r
->sz
);
562 assert(r
->start
<= r
->end
);
563 assert(r
->end
<= r
->sz
);
567 static ssize_t
recovery_read_sectors(struct recoverybuf
*r
,
568 secaddr pos
, secaddr off
, secaddr want
)
572 assert(off
<= r
->sz
); assert(want
<= r
->sz
- off
);
573 n
= read_sectors(pos
, r
->buf
+ off
*SECTORSZ
, want
);
577 static ssize_t
recovery_read(struct recoverybuf
*r
,
578 secaddr pos
, secaddr want
)
580 secaddr diff
, pp
, nn
;
584 debug_clear_progress();
585 show_recovery_buffer_map(r
, "begin(%"PRIuSEC
", %"PRIuSEC
")", pos
, want
);
590 if (r
->start
+ diff
>= r
->sz
) {
591 r
->pos
= pos
; r
->start
= r
->end
= 0;
593 show_recovery_buffer_map(r
, "cleared; shift up by %"PRIuSEC
"", diff
);
596 if (r
->end
+ diff
> r
->sz
) r
->end
= r
->sz
- diff
;
597 rearrange_sectors(r
, r
->start
+ diff
, r
->start
, r
->end
- r
->start
);
598 r
->pos
-= diff
; r
->start
+= diff
; r
->end
+= diff
;
600 show_recovery_buffer_map(r
, "shifted up by %"PRIuSEC
"", diff
);
603 } else if (pos
> r
->pos
+ r
->end
) {
604 r
->pos
= pos
; r
->start
= r
->end
= 0;
606 show_recovery_buffer_map(r
, "cleared; beyond previous region");
608 } else if (pos
+ want
> r
->pos
+ r
->sz
) {
609 diff
= (pos
+ want
) - (r
->pos
+ r
->sz
);
610 if (r
->end
<= diff
) {
611 r
->pos
= pos
; r
->start
= r
->end
= 0;
613 show_recovery_buffer_map(r
, "cleared; shift down by %"PRIuSEC
"", diff
);
616 if (r
->start
< diff
) r
->start
= diff
;
617 rearrange_sectors(r
, r
->start
- diff
, r
->start
, r
->end
- r
->start
);
618 r
->pos
+= diff
; r
->start
-= diff
; r
->end
-= diff
;
620 show_recovery_buffer_map(r
, "shifted down by %"PRIuSEC
"", diff
);
625 if (pos
< r
->pos
+ r
->start
) {
626 pp
= pos
- r
->pos
; nn
= r
->start
- pp
;
628 printf(";; read low (%"PRIuSEC
"@%"PRIuSEC
", %"PRIuSEC
")", pos
, pp
, nn
);
631 n
= recovery_read_sectors(r
, pos
, pp
, nn
);
633 printf(" -> %zd\n", n
);
636 if (n
>= 0 && n
> want
) n
= want
;
641 show_recovery_buffer_map(r
, "joined new region");
645 if (pos
+ want
> r
->pos
+ r
->end
) {
646 pp
= r
->end
; nn
= (pos
+ want
) - (r
->pos
+ r
->end
);
648 printf(";; read high (%"PRIuSEC
"@%"PRIuSEC
", %"PRIuSEC
")",
649 r
->pos
+ pp
, pp
, nn
);
652 n
= recovery_read_sectors(r
, r
->pos
+ pp
, pp
, nn
);
654 printf(" -> %zd\n", n
);
659 show_recovery_buffer_map(r
, "joined new region");
664 n
= r
->pos
+ r
->end
- pos
;
665 if (!n
&& want
) n
= -1;
666 else if (n
> want
) n
= want
;
670 show_recovery_buffer_map(r
, "done; return %zd", n
);
675 static secaddr
run_length_wanted(secaddr pos
, secaddr badlen
,
676 secaddr sz
, secaddr end
)
682 if (want
> end
- pos
) want
= end
- pos
;
683 if (want
> sz
) want
= sz
;
687 static ssize_t
find_good_sector(secaddr
*pos_inout
, secaddr end
,
688 unsigned char *buf
, secaddr sz
)
691 secaddr pos
= *pos_inout
, bad_lo
, bad_hi
, good
, step
, want
;
692 struct recoverybuf r
;
695 r
.buf
= buf
; r
.sz
= sz
; r
.pos
= r
.start
= r
.end
= 0;
696 report_bad_blocks_progress(pos
, pos
, errno
);
698 want
= sz
; if (want
> end
- pos
) want
= end
- pos
;
699 for (i
= 0; i
< 4; i
++) {
700 n
= recovery_read(&r
, pos
, want
);
702 debug_clear_progress();
703 printf(";; [retry] try reading %"PRIuSEC
" .. %"PRIuSEC
" -> %zd\n",
708 moan("sector %"PRIuSEC
" read ok after retry", pos
);
713 bad_lo
= pos
; bad_hi
= pos
+ 1;
715 report_bad_blocks_progress(bad_lo
, bad_hi
, errno
);
717 debug_clear_progress();
718 printf(";; bounding bad-block region: "
719 "%"PRIuSEC
" ..%"PRIuSEC
".. %"PRIuSEC
"\n",
720 bad_lo
, bad_hi
- bad_lo
, bad_hi
);
724 moan("giving up on this extent");
725 recovered(bad_lo
, end
); *pos_inout
= end
; return (0);
727 step
= 2*(bad_hi
- bad_lo
); if (step
> end
- bad_lo
) step
= end
- bad_lo
;
728 pos
= bad_lo
+ step
- 1;
729 want
= run_length_wanted(pos
, step
, sz
, end
);
730 n
= recovery_read(&r
, pos
, want
);
732 printf(";; [bound] try reading %"PRIuSEC
" .. %"PRIuSEC
" -> %zd\n",
735 if (n
== want
) break;
737 bad_hi
= pos
+ n
+ 1;
741 while (good
> bad_hi
) {
742 report_bad_blocks_progress(bad_lo
, bad_hi
, errno
);
744 debug_clear_progress();
745 printf(";; limiting bad-block region: "
746 "%"PRIuSEC
" ..%"PRIuSEC
".. %"PRIuSEC
" ..%"PRIuSEC
".. %"PRIuSEC
"\n",
747 bad_lo
, bad_hi
- bad_lo
, bad_hi
, good
- bad_hi
, good
);
749 pos
= bad_hi
+ (good
- bad_hi
)/2;
751 want
= run_length_wanted(pos
, step
, sz
, end
);
752 n
= recovery_read(&r
, pos
, want
);
754 printf(";; [limit] try reading %"PRIuSEC
" .. %"PRIuSEC
" -> %zd\n",
758 if (n
== want
) good
= pos
;
759 else bad_hi
= pos
+ n
+ 1;
761 recovered(bad_lo
, bad_hi
); *pos_inout
= good
;
762 if (good
< r
.pos
+ r
.start
|| r
.pos
+ r
.end
<= good
)
765 n
= r
.pos
+ r
.end
- good
;
766 rearrange_sectors(&r
, 0, good
- r
.pos
, n
);
769 show_recovery_buffer_map(&r
, "returning %zd good sectors at %"PRIuSEC
"",
775 static void emit(secaddr start
, secaddr end
)
777 #define BUFSECTORS 512
780 unsigned char buf
[BUFSECTORS
*SECTORSZ
];
784 static int first_time
= 1;
792 least
= least_live();
795 printf(";; %8"PRIuSEC
" .. %"PRIuSEC
"\n", start
, end
);
797 for (i
= 0; i
< filetab
.n
; i
++) {
798 if (!livep(i
)) continue;
799 if (act
== -1) act
= i
;
800 f
= &filetab
.v
[i
]; store_filename(fn
, f
->id
);
801 printf(";;\t\t%8"PRIuSEC
" .. %-8"PRIuSEC
" %s\n",
802 start
- f
->start
, end
- f
->start
, fn
);
804 if (act
== -1) printf(";;\t\t#<no live source>\n");
805 assert(act
== least
);
809 { file
= 0; vob
= 0; }
811 file
= &filetab
.v
[least
];
812 switch (id_kind(file
->id
)) {
817 if (first_time
) { clear_progress(); first_time
= 0; }
818 vob
= DVDOpenFile(dvd
, id_title(file
->id
),
820 ? DVD_READ_TITLE_VOBS
821 : DVD_READ_MENU_VOBS
);
823 bail("failed to open %s %u",
824 id_part(file
->id
) ?
"title" : "menu",
834 want
= end
- pos
; if (want
> BUFSECTORS
) want
= BUFSECTORS
;
835 n
= read_sectors(pos
, buf
, want
);
837 if (n
<= 0) n
= find_good_sector(&pos
, end
, buf
, BUFSECTORS
);
838 if (n
> 0) { carefully_write(outfd
, buf
, n
*SECTORSZ
); pos
+= n
; }
839 report_progress(pos
); fflush(stdout
);
842 if (vob
) { DVDCloseFile(vob
); vob
= 0; }
848 static void logfn(void *p
, dvd_logger_level_t lev
,
849 const char *fmt
, va_list ap
)
852 case DVD_LOGGER_LEVEL_ERROR
:
853 fprintf("%s (libdvdread error): ", prog
);
855 case DVD_LOGGER_LEVEL_WARN
:
856 fprintf("%s (libdvdread warning): ", prog
);
861 vfprintf(stderr
, fmt
, ap
);
864 static const dvd_logger_cb logger
= { logfn
};
871 #define BUF_INIT { 0, 0, 0 }
872 #define BUF_REWIND(b) do { (b)->n = 0; } while (0)
873 #define BUF_FREE(b) do { \
875 free(_b->p); _b->p = 0; _b->n = _b->sz = 0; \
877 #define BUF_PUTC(b, ch) do { \
878 struct buf *_b = (b); \
879 if (_b->n >= _b->sz) { \
880 _b->sz = _b->sz ? 2*_b->sz : 32; \
881 _b->p = realloc(_b->p, _b->sz); \
882 if (!_b->p) bail("out of memory allocating %zu bytes", _b->sz); \
884 _b->p[_b->n] = (ch); \
887 static int read_line(FILE *fp
, struct buf
*b
)
894 else if (ch
!= '\n') do {
895 BUF_PUTC(b
, ch
); b
->n
++;
897 } while (ch
!= EOF
&& ch
!= '\n');
902 #define PRF_HYPHEN 1u
903 static int parse_range(const char *p
, unsigned f
,
904 secaddr
*start_out
, secaddr
*end_out
)
908 unsigned long start
, end
;
913 start
= strtoul(p
, &q
, 0);
914 if (errno
|| start
>= SECLIMIT
) { rc
= -1; goto end
; }
915 *start_out
= start
; p
= q
;
916 } else if (!(f
&PRF_HYPHEN
))
917 { rc
= -1; goto end
; }
922 if (*p
!= '-') { rc
= -1; goto end
; }
925 if (!ISSPACE(*p
)) { rc
= -1; goto end
; }
926 do p
++; while (ISSPACE(*p
));
930 end
= strtoul(p
, &q
, 0);
931 if (errno
|| end
> SECLIMIT
|| end
< start
) { rc
= -1; goto end
; }
932 *end_out
= end
; p
= q
;
933 } else if (!(f
&PRF_HYPHEN
))
934 { rc
= -1; goto end
; }
936 if (!(f
&PRF_HYPHEN
)) while (ISSPACE(*p
)) p
++;
937 if (*p
&& ((f
&PRF_HYPHEN
) || *p
!= '=')) { rc
= -1; goto end
; }
945 int main(int argc
, char *argv
[])
952 secaddr start
, end
, last
;
953 const struct event
*ev
;
954 const char *device
, *outfile
;
955 struct badblock
*bad
;
960 struct buf buf
= BUF_INIT
;
963 const struct file
*file
;
968 #define f_continue 2u
972 p
= strrchr(argv
[0], '/'); prog
= p ? p
+ 1 : argv
[0];
974 opt
= getopt(argc
, argv
, "hE:FR:X:b:cr:"); if (opt
< 0) break;
976 case 'h': usage(stderr
); exit(0);
977 case 'E': errfile
= optarg
; break;
978 case 'F': f
|= f_fixup
; break;
980 fp
= fopen(optarg
, "r");
982 bail_syserr(errno
, "failed to open ranges file `%s'", optarg
);
985 BUF_REWIND(&buf
); if (read_line(fp
, &buf
)) break;
987 while (ISSPACE(*p
)) p
++;
988 if (!*p
|| *p
== '#') continue;
989 if (parse_range(p
, 0, &start
, &end
) ||
990 (last
<= SECLIMIT
&& start
< last
))
991 bail("bad range `%s' at `%s' line %zu", buf
.p
, optarg
, i
);
994 eventq
.v
[eventq
.n
- 1].pos
= end
;
996 put_event(EV_WRITE
, 0, start
);
997 put_event(EV_STOP
, 0, end
);
1003 bail_syserr(errno
, "failed to read ranges file `%s'", optarg
);
1006 fp
= fopen(optarg
, "r");
1008 bail_syserr(errno
, "failed to open bad-blocks file `%s'", optarg
);
1011 BUF_REWIND(&buf
); if (read_line(fp
, &buf
)) break;
1013 while (ISSPACE(*p
)) p
++;
1014 if (!*p
|| *p
== '#') continue;
1015 if (parse_range(p
, 0, &start
, &end
) ||
1016 (last
<= SECLIMIT
&& start
< last
))
1017 bail("bad range `%s' at `%s' line %zu", buf
.p
, optarg
, i
);
1019 { VEC_PUSH(bad
, &badblocks
); bad
->start
= start
; bad
->end
= end
; }
1022 bail_syserr(errno
, "failed to read bad-blocks file `%s'", optarg
);
1025 if (mapfile
) bail("can't have multiple map files");
1028 case 'c': f
|= f_continue
; break;
1030 start
= 0; end
= -1;
1031 if (parse_range(optarg
, PRF_HYPHEN
, &start
, &end
))
1032 bail("bad range `%s'", optarg
);
1034 put_event(EV_WRITE
, 0, start
);
1035 if (end
<= SECLIMIT
) put_event(EV_STOP
, 0, end
);
1038 default: f
|= f_bogus
; break;
1041 if (argc
- optind
!= 2) f
|= f_bogus
;
1042 if (f
&f_bogus
) { usage(stderr
); exit(2); }
1044 device
= argv
[optind
]; outfile
= argv
[optind
+ 1];
1047 qsort(badblocks
.v
, badblocks
.n
, sizeof(struct badblock
),
1050 printf(";; fake bad blocks:\n");
1051 for (i
= 0; i
< badblocks
.n
; i
++)
1052 printf(";;\t%8"PRIuSEC
" .. %"PRIuSEC
"\n",
1053 badblocks
.v
[i
].start
, badblocks
.v
[i
].end
);
1057 dvdfd
= open(device
, O_RDONLY
);
1059 bail_syserr(errno
, "failed to open device `%s'", device
);
1060 if (fstat(dvdfd
, &st
))
1061 bail_syserr(errno
, "failed to stat device `%s'", device
);
1062 if (S_ISREG(st
.st_mode
)) {
1065 } else if (S_ISBLK(st
.st_mode
)) {
1066 if (ioctl(dvdfd
, BLKSSZGET
, &blksz
))
1067 bail_syserr(errno
, "failed to get block size for `%s'", device
);
1068 if (ioctl(dvdfd
, BLKGETSIZE64
, &volsz
))
1069 bail_syserr(errno
, "failed to get volume size for `%s'", device
);
1071 bail("can't use `%s' as source: expected file or block device", device
);
1073 if (blksz
!= SECTORSZ
)
1074 bail("device `%s' block size %d /= %d", device
, blksz
, SECTORSZ
);
1076 bail("device `%s' volume size %"PRIu64
" not a multiple of %d",
1077 device
, volsz
, SECTORSZ
);
1080 outfd
= open(outfile
, O_WRONLY
| O_CREAT
, 0666);
1082 bail_syserr(errno
, "failed to create output file `%s'", outfile
);
1086 off
= lseek(outfd
, 0, SEEK_END
);
1088 bail_syserr(errno
, "failed to seek to end of output file `%s'",
1090 put_event(EV_WRITE
, 0, off
/SECTORSZ
);
1091 } else if (!eventq
.n
&& !(f
&f_fixup
))
1092 put_event(EV_WRITE
, 0, 0);
1095 dvd
= DVDOpen2(0, &logger
, device
);
1097 dvd
= DVDOpen(device
);
1099 if (!dvd
) bail("failed to open DVD on `%s'", device
);
1101 /* It's fast enough just to check everything. */
1103 for (i
= 1; i
< 100; i
++) {
1107 put_file(mkident(RAW
, 0, 0), 0, volsz
/SECTORSZ
);
1108 assert(filetab
.n
<= MAXFILES
);
1110 for (i
= 0, limit
= 0; i
< filetab
.n
; i
++)
1111 if (filetab
.v
[i
].end
> limit
) limit
= filetab
.v
[i
].end
;
1113 if (end
> limit
) end
= limit
;
1116 printf("\n;; files:\n");
1117 for (i
= 0; i
< filetab
.n
; i
++) {
1118 file
= &filetab
.v
[i
];
1119 store_filename(fn
, file
->id
);
1120 printf(";;\t%8"PRIuSEC
" %s\n", file
->start
, fn
);
1124 qsort(eventq
.v
, eventq
.n
, sizeof(struct event
), compare_event
);
1126 f
&= ~f_write
; start
= 0; n
= 0;
1127 for (i
= 0; i
< eventq
.n
; i
++) {
1132 bail("overlapping ranges: range from %"PRIuSEC
" still open at %"PRIuSEC
"",
1134 n
++; f
|= f_write
; start
= ev
->pos
;
1142 f
&= ~f_write
; start
= 0;
1143 for (i
= 0; i
< eventq
.n
; i
++) {
1146 case EV_WRITE
: start
= ev
->pos
; f
|= f_write
; break;
1147 case EV_STOP
: nsectors
+= ev
->pos
- start
; f
&= ~f_write
; break;
1149 if (ev
->pos
>= limit
) break;
1150 if (f
&f_fixup
) start
= ev
->pos
;
1154 put_event(EV_WRITE
, 0, start
);
1158 nsectors
+= limit
- start
;
1159 put_event(EV_STOP
, 0, limit
);
1161 if (n
== 1 && (f
&f_write
)) flags
|= F_ALLPROGRESS
;
1165 printf("\n;; event sweep:\n");
1167 for (pos
= 0, i
= 0; i
< eventq
.n
; i
++) {
1169 if (ev
->pos
> pos
) {
1170 if (f
&f_write
) emit(pos
, ev
->pos
);
1173 debug_clear_progress();
1181 store_filename(fn
, filetab
.v
[ev
->file
].id
);
1182 debug_clear_progress();
1183 printf(";; %8"PRIuSEC
": begin `%s'\n", pos
, fn
);
1187 gettimeofday(&last_time
, 0); last_pos
= pos
;
1188 if (lseek(outfd
, (off_t
)ev
->pos
*SECTORSZ
, SEEK_SET
) < 0)
1190 "failed to seek to resume position "
1191 "(sector %"PRIuSEC
") in output file `%s'",
1194 debug_clear_progress();
1195 printf(";; %8"PRIuSEC
": begin write\n", pos
);
1202 debug_clear_progress();
1203 printf(";; %8"PRIuSEC
": end write\n", pos
);
1207 clear_live(ev
->file
);
1209 store_filename(fn
, filetab
.v
[ev
->file
].id
);
1210 debug_clear_progress();
1211 printf(";; %8"PRIuSEC
": end `%s'\n", pos
, fn
);
1218 if (progresslen
) putchar('\n');
1220 if (ftruncate(outfd
, (off_t
)limit
*SECTORSZ
) < 0)
1221 bail_syserr(errno
, "failed to set output file `%s' length", outfile
);
1223 if (dvd
) DVDClose(dvd
);
1224 if (dvdfd
>= 0) close(dvdfd
);
1225 if (outfd
>= 0) close(outfd
);
1226 carefully_fclose(mapfp
, "bad-sector region map");
1227 carefully_fclose(errfp
, "bad-sector error log");