@@@ dvdrip-upload: change settings while i'm stealing someone else's internet
[dvdrip] / dvd-sector-copy.c
CommitLineData
b505435d
MW
1/* -*-c-*-
2 *
3 * Make an unscrambled copy of a DVD.
4 *
5 * (c) 2022 Mark Wooding
6 */
7
8/*----- Licensing notice --------------------------------------------------*
9 *
10 * This file is part of the DVD ripping toolset.
11 *
12 * DVDrip is free software: you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 3 of the License, or (at your
15 * option) any later version.
16 *
17 * DVDrip is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 * for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with DVDrip. If not, see <https://www.gnu.org/licenses/>.
24 */
25
26/*----- Header files ------------------------------------------------------*/
27
dc53ebfa 28#include "lib.h"
7fbe0fb9 29
4d69e943
MW
30#ifdef __linux__
31# include <linux/cdrom.h>
32#endif
33
b505435d
MW
34/*----- Program usage summary ---------------------------------------------*/
35
7fbe0fb9
MW
36static void usage(FILE *fp)
37{
38 fprintf(fp,
d6845ac3 39 "usage: %s [-ci] [-B PARAM=VALUE,...] [-R MAP]\n"
dc53ebfa 40 "\t[-b OUTMAP] [-r [START]-[END]] DEVICE OUTFILE\n",
7fbe0fb9
MW
41 prog);
42}
43
b505435d
MW
44/*----- Random utilities --------------------------------------------------*/
45
8cec8b64
MW
46#define PRF_HYPHEN 1u
47static int parse_range(const char *p, unsigned f,
48 secaddr *start_out, secaddr *end_out)
b505435d
MW
49 /* Parse a range of sectors from the string P. If successful, store
50 * the specified start sector address in *START_OUT and the end
51 * address in *END_OUT, and return zero. On failure, return -1;
52 * *START_OUT and/or *END_OUT are clobbered.
53 *
54 * The acceptable syntax depends on the flags.
55 *
56 * * The `PRF_HYPHEN' syntax is intended for use on the
57 * command-line. It accepts `[START]-[END]'; if the start and/or
58 * end addresses are omitted then *START_OUT and/or *END_OUT are
59 * left unchanged.
60 *
61 * * The default syntax matches what's written to the bad-sector
62 * output files. It accepts `START END [# COMMENT]'.
63 */
8cec8b64
MW
64{
65 char *q;
66 int err, rc;
67 unsigned long start, end;
68
b505435d 69 /* Save any existing error code. */
8cec8b64
MW
70 err = errno;
71
b505435d 72 /* Parse the start address. */
8cec8b64 73 if (ISDIGIT(*p)) {
b505435d
MW
74 /* We found a digit: this is a good start. Convert the integer, check
75 * that it's in range, save it.
76 */
77
8cec8b64
MW
78 start = strtoul(p, &q, 0);
79 if (errno || start >= SECLIMIT) { rc = -1; goto end; }
80 *start_out = start; p = q;
b505435d
MW
81 } else if (!(f&PRF_HYPHEN)) {
82 /* No digit. We're parsing the map-file syntax, so this is an error. */
83
84 rc = -1; goto end;
85 } else {
86 /* We're parsing the command-line syntax, so this is OK. Set our
87 * internal idea of the position for the range check later, but don't
88 * alter the caller's variables.
89 */
90
8cec8b64 91 start = 0;
b505435d 92 }
8cec8b64 93
b505435d 94 /* Parse the delimiter. */
8cec8b64
MW
95 if (f&PRF_HYPHEN) {
96 if (*p != '-') { rc = -1; goto end; }
97 p++;
98 } else {
99 if (!ISSPACE(*p)) { rc = -1; goto end; }
100 do p++; while (ISSPACE(*p));
101 }
102
b505435d 103 /* Parse the end address. */
8cec8b64 104 if (ISDIGIT(*p)) {
b505435d
MW
105 /* We found a digit. Parse the integer and check that it's strictly
106 * larger than the start address.
107 */
108
8cec8b64
MW
109 end = strtoul(p, &q, 0);
110 if (errno || end > SECLIMIT || end < start) { rc = -1; goto end; }
111 *end_out = end; p = q;
b505435d
MW
112 } else if (!(f&PRF_HYPHEN)) {
113 /* No digit. We're parsing the file syntax, so this is an error. */
114
115 rc = -1; goto end;
116 }
8cec8b64 117
b505435d 118 /* In the file syntax, we're now allowed whitespace, so skip past that. */
8cec8b64 119 if (!(f&PRF_HYPHEN)) while (ISSPACE(*p)) p++;
b505435d
MW
120
121 /* Check that there's nothing else. The file syntax allows a trailing
122 * comment here, but the command-line syntax doesn't.
123 */
8cec8b64
MW
124 if (*p && ((f&PRF_HYPHEN) || *p != '#')) { rc = -1; goto end; }
125
b505435d 126 /* All done! */
8cec8b64
MW
127 rc = 0;
128end:
129 errno = err;
130 return (rc);
131}
132
b505435d
MW
133/*----- A few words about the overall approach ----------------------------*
134 *
135 * The objective is to produce a working copy of the input (commercial,
136 * pressed) DVD disc, only with all of the scrambled video data unscrambled
137 * so that it can be read without the need for cracking CSS keys, which, in
138 * the absence of a cooperative drive with access to the key tables in the
139 * disc lead-in data -- which we /don't/ copy -- is often slow and prone to
140 * failure. Producing a sector-by-sector image preserves all of the menus
141 * and special features, and also any other bonus data stored in the
142 * filesystem for use by computers, such as PDF scripts. DVD images are
143 * large because DVD video is inefficiently compressed by modern standards,
144 * but disk space is cheap and the tradeoff seems worthwhile to me.
145 *
146 * The approach is, in essence, simple: start at the beginning of the disc,
147 * reading sectors into a buffer and writing them to the output file, and
148 * continue until we reach the end. But we must cope with scrambled video
149 * files. Fortunately, `libdvdread' knows how to deal with these, and will
150 * tell us where they are on the disc.
151 *
152 * Given this information, we build a table of `events', with the sector
153 * numbers at which they occur. An `event' might be something like `such-
154 * and-such a video file began' or `such-and-such a file ended'. Chunks of
155 * disc between events can be read using the same strategy -- either reading
156 * unscrambled sectors directly from the block device, or decrypting
157 * scrambled sectors through `libdvdread' -- while at sector boundaries we
158 * might need to change strategy.
159 *
160 * Note that files can /overlap/. The DVD spec says that this can't happen,
161 * and that the data for video titles is laid out with higher-numbered
162 * titlesets occupying higher-numbered sectors, but it does anyway. I think
163 * this is intended to frustrate copiers like `dvdbackup' which try to copy
164 * the DVD files into a directory on the filesystem. The result is that they
165 * copy the same sectors into multiple, very large files, and turn an 8 GB
166 * DVD image into a 60 GB directory. (The reused regions often also contain
167 * intentionally bad sectors, so you have to wait for the drive to fail the
168 * same sectors over and over again. This is no fun.) As far as I know,
169 * files are either disjoint or coincident, but more complex arrangements are
170 * possible in principle. Also, I guess it's possible that the same sector
171 * should be decrypted with different keys depending on which titleset we're
172 * considering it being part of, but (a) DVD CSS keys aren't long enough to
173 * do this very well, and (b) I'm not aware of this actually being a thing.
174 * (Indeed, `libdvdcss' indexes keys by start sector, so such a disc probably
175 * wouldn't play back properly through VLC or `mpv'.)
176 *
177 * There's an additional consideration. We want to be able to fill in an
178 * ouptut image file incrementally, in several runs. A run can be
179 * interrupted for lots of reasons (e.g., a faster drive might have become
180 * available; it might be beneficial to switch to a more forgiving drive; it
181 * might be necessary to stop and clean the disc; the output filesystem might
182 * have become full; ...). And discs don't always read perfectly: some discs
183 * are damaged and have areas which can't be read; some discs (I'm looking at
184 * you, Sony, Disney, Lionsgate, and E-One) have intentional bad sectors,
185 * presumably specifically to make my life annoying. So we have other events
186 * which say things like `start writing stuff to the output' or `stop writing
187 * things to the output'. And we have a rather elaborate algorithm for
188 * trying to skip past a region of bad blocks, because drives get /really/
189 * slow when reading bad sectors.
190 */
191
192/*----- The file and event tables -----------------------------------------*/
193
7fbe0fb9 194#define MAXFILES (1 + 2*99 + 1)
b505435d
MW
195 /* How many (interesting) files there can be. This counts the
196 * magical `raw' file which refers to direct disc access, the master
197 * menu file, and 99 possible menu and titleset pairs. (A titleset
198 * can be split into 9 parts in order to keep each file below a
199 * gigabyte in size, but the rules require that the parts together
200 * form a single contiguous chunk on the disc, in the right order, so
201 * we treat them as a single file. We check this in `put_title'
202 * below, just in case some disc somewhere tries to be awkward, but I
203 * don't have a disc like that in my collection, and I doubt it would
204 * work very well.)
205 */
206
7fbe0fb9 207struct file {
b505435d
MW
208 /* An interesting DVD file. It has a name, encoded as an `ident'
209 * (see `lib.h'), and start and end sectors. (The `end' here, as
210 * everywhere in this code, is /exclusive/, so that the file's length
211 * is simply end - start.)
212 */
213
214 ident id; /* file name */
215 secaddr start, end; /* start (inclusive) and end
216 * (exclusive) sector numbers */
217};
218DEFVEC(file_v, struct file); /* a vector of files */
219static file_v filetab = VEC_INIT; /* the file table */
220
221enum {
222 /* Event codes. The ordering of these is important, because we use
223 * them to tie-break comparisons of events happening at the same
224 * sector when we sort the event queue.
225 */
226
227 EV_STOP, /* stop copying stuff to output */
228 EV_BEGIN, /* a (maybe scrambled) file begins */
229 EV_END, /* a file ends */
230 EV_WRITE /* start copying stuff to output */
7fbe0fb9 231};
7fbe0fb9 232
7fbe0fb9 233struct event {
b505435d
MW
234 /* An event. */
235
236 unsigned char ev; /* event code (`EV_...') */
237 unsigned char file; /* the file (`EV_BEGIN', `EV_END');
238 * index into `filetab' */
239 secaddr pos; /* the sector at which it happens */
7fbe0fb9 240};
b505435d
MW
241DEFVEC(event_v, struct event); /* a vector of events */
242static event_v eventq = VEC_INIT; /* the event queue */
7fbe0fb9 243
72279434 244static int compare_event(const void *a, const void *b)
b505435d
MW
245 /* A `qsort' comparison function for events. Event A sorts earlier
246 * than event B iff A's sector number is smaller than B's, or A's
247 * event code is less than B's.
248 */
72279434
MW
249{
250 const struct event *eva = a, *evb = b;
251
b505435d 252 /* Primary ordering by position. */
72279434
MW
253 if (eva->pos < evb->pos) return (-1);
254 else if (eva->pos > evb->pos) return (+1);
255
b505435d 256 /* Secondary ordering by event code. */
72279434
MW
257 if (eva->ev < evb->ev) return (-1);
258 else if (eva->ev > evb->ev) return (+1);
259
b505435d
MW
260 /* We currently have a final tie-break on file numbers so that the ordering
261 * is deterministic, but this is an arbitrary choice that shouldn't be
262 * relied upon.
263 */
72279434
MW
264 if (eva->file < evb->file) return (-1);
265 else if (eva->file > evb->file) return (+1);
266
b505435d 267 /* These events are equal. */
72279434
MW
268 return (0);
269}
270
8cec8b64
MW
271#ifdef DEBUG
272static void dump_eventq(const char *what)
b505435d 273 /* Dump the event queue, labelling the output with WHAT. */
8cec8b64
MW
274{
275 unsigned i;
276 const struct event *ev;
277 char fn[MAXFNSZ];
278
279 printf("\n;; event dump (%s):\n", what);
280 for (i = 0; i < eventq.n; i++) {
281 ev = &eventq.v[i];
282 switch (ev->ev) {
283 case EV_BEGIN:
284 store_filename(fn, filetab.v[ev->file].id);
285 printf(";; %8"PRIuSEC": begin %s\n", ev->pos, fn);
286 break;
287 case EV_END:
288 store_filename(fn, filetab.v[ev->file].id);
289 printf(";; %8"PRIuSEC": end %s\n", ev->pos, fn);
290 break;
291 case EV_WRITE:
292 printf(";; %8"PRIuSEC": write\n", ev->pos);
293 break;
294 case EV_STOP:
295 printf(";; %8"PRIuSEC": stop\n", ev->pos);
296 break;
297 default:
298 printf(";; %8"PRIuSEC": ?%u\n", ev->pos, ev->ev);
299 break;
300 }
301 }
302}
303#endif
304
7fbe0fb9
MW
305typedef uint_least32_t bits;
306static bits live[(MAXFILES + 31)/32];
b505435d
MW
307 /* A bitmap which keeps track of which files are currently `active',
308 * i.e., that contain the sector we're currently thinking about. We
309 * set and clear these bits as we encounter `EV_BEGIN' and `EV_END'
310 * events.
311 */
7fbe0fb9
MW
312
313static inline int livep(unsigned i)
b505435d 314 /* Return whether file I is active. */
7fbe0fb9 315 { return (live[i/32]&((bits)1 << (i%32))); }
b505435d 316
7fbe0fb9 317static inline void set_live(unsigned i)
b505435d 318 /* Note that we've seen the start of file I. */
7fbe0fb9 319 { live[i/32] |= (bits)1 << (i%32); }
b505435d 320
7fbe0fb9 321static inline void clear_live(unsigned i)
b505435d 322 /* Note that we've seen the end of file I. */
7fbe0fb9 323 { live[i/32] &= ~((bits)1 << (i%32)); }
b505435d 324
7fbe0fb9 325static inline int least_live(void)
b505435d
MW
326 /* Return the smallest index for any active file. This is going to
327 * be the file that we ask `libdvdread' to unscramble for us. This
328 * is important: the imaginary `raw' file that represents the entire
329 * block device has the highest index, and we want any actual video
330 * file to be used in preference so that we unscramble the data.
331 */
7fbe0fb9
MW
332{
333 unsigned i, n = (filetab.n + 32)/32;
334 bits b;
335
b505435d 336 /* First part: find the first nonzero word in the `live' table. */
7fbe0fb9
MW
337 for (i = 0; i < n; i++) { b = live[i]; if (b) goto found; }
338 return (-1);
b505435d 339
7fbe0fb9 340found:
b505435d
MW
341 /* Second part: identify which bit in this word is nonzero. First, see if
342 * the bottom 16 bits are clear: if so, shift down and add 16 to the
343 * total. Now we know that the first set bit is indeed in the low 16
344 * bits, so see whether the low 8 bits are clear, and so on.
345 */
7fbe0fb9
MW
346 i *= 32;
347 if (!(b&0x0000ffff)) { b >>= 16; i += 16; }
348 if (!(b&0x000000ff)) { b >>= 8; i += 8; }
349 if (!(b&0x0000000f)) { b >>= 4; i += 4; }
350 if (!(b&0x00000003)) { b >>= 2; i += 2; }
351 if (!(b&0x00000001)) { b >>= 1; i += 1; }
352 assert(b&1);
b505435d
MW
353
354 /* Done. */
7fbe0fb9
MW
355 return (i);
356}
357
788fe88a 358static void put_event(unsigned evtype, unsigned file, secaddr pos)
b505435d
MW
359 /* Add an event to the queue, with type EVTYPE, for the given FILE,
360 * and at sector POS. You can add events in any order because we'll
361 * sort them later. For `EV_WRITE' and `EV_STOP' events, the FILE
362 * doesn't matter: use zero for concreteness.
363 */
7fbe0fb9
MW
364{
365 struct event *ev;
366
367 VEC_PUSH(ev, &eventq);
368 ev->ev = evtype; ev->file = file; ev->pos = pos;
369}
370
788fe88a 371static void put_file(ident id, secaddr start, secaddr end)
b505435d
MW
372 /* Add a (VOB) file to the file table and event queue, with ident ID,
373 * starting at sector START and ending just before sector END.
374 */
7fbe0fb9
MW
375{
376 struct file *f;
377 size_t i;
378
379 VEC_PUSH(f, &filetab); i = f - filetab.v;
380 f->id = id; f->start = start; f->end = end;
381 put_event(EV_BEGIN, i, start);
382 put_event(EV_END, i, end);
383}
384
385static void put_menu(dvd_reader_t *dvd, unsigned title)
b505435d
MW
386 /* Add the menu file for the given TITLE number to the file table and
387 * event queue; use the reader DVD to find out which sectors it
388 * occupies, if it even exists.
389 */
7fbe0fb9
MW
390{
391 ident id = mkident(VOB, title, 0);
392 char fn[MAXFNSZ];
788fe88a 393 secaddr start, len;
7fbe0fb9 394
b505435d 395 /* Find out where the file is. */
7fbe0fb9
MW
396 store_filename(fn, id);
397 start = UDFFindFile(dvd, fn, &len); if (!start) return;
b505435d 398
7fbe0fb9 399#ifdef DEBUG
b505435d 400 /* Print out what we've discovered. */
8ed763a5
MW
401 printf(";; %8"PRIuSEC" .. %-8"PRIuSEC": %s\n",
402 start, start + SECTORS(len), fn);
7fbe0fb9 403#endif
b505435d
MW
404
405 /* Register the file and boundary events. */
7fbe0fb9
MW
406 put_file(id, start, start + SECTORS(len));
407}
408
409static void put_title(dvd_reader_t *dvd, unsigned title)
b505435d
MW
410 /* Add the titleset file for the given TITLE number to the file table
411 * and event queue; use the reader DVD to find out which sectors it
412 * occupies, if it even exists.
413 */
7fbe0fb9
MW
414{
415 char fn[MAXFNSZ];
788fe88a 416 secaddr start[9], len[9];
7fbe0fb9
MW
417 unsigned i, npart;
418
b505435d
MW
419 /* First step: find out where all of the parts of the titleset are. I'm
420 * assuming that there aren't gaps in the numbering.
421 */
7fbe0fb9
MW
422 for (i = 0; i < 9; i++) {
423 store_filename(fn, mkident(VOB, title, i + 1));
424 start[i] = UDFFindFile(dvd, fn, &len[i]); if (!start[i]) break;
425 }
426 npart = i; if (!npart) return;
427
428#ifdef DEBUG
b505435d 429 /* Print out what we've discovered. */
7fbe0fb9
MW
430 for (i = 0; i < npart; i++) {
431 store_filename(fn, mkident(VOB, title, i + 1));
788fe88a 432 printf(";; %8"PRIuSEC" .. %-8"PRIuSEC": %s\n",
7fbe0fb9
MW
433 start[i], start[i] + SECTORS(len[i]), fn);
434 }
435#endif
436
b505435d
MW
437 /* Second step: check that the parts all butt up against each other in the
438 * correct order. For this to work, the lengths, which are expressed in
439 * /bytes/ by `UDFFindFile', of all but the last part must be a whole
440 * number of sectors.
441 */
7fbe0fb9
MW
442 if (npart > 1)
443 for (i = 0; i < npart - 1; i++) {
444 if (len[i]%SECTORSZ)
788fe88a 445 bail("title %u part %u length = %"PRIuSEC" not a multiple of %d",
7fbe0fb9
MW
446 title, i, len[i], SECTORSZ);
447 if (start[i] + len[i]/SECTORSZ != start[i + 1])
788fe88a
MW
448 bail
449 ("title %u part %u end = %"PRIuSEC" /= part %u start = %"PRIuSEC"",
450 title, i, start[i] + len[i]/SECTORSZ, i + 1, start[i + 1]);
7fbe0fb9
MW
451 }
452
b505435d 453 /* All good: register a single file and its boundary events. */
7fbe0fb9
MW
454 put_file(mkident(VOB, title, 1),
455 start[0], start[npart - 1] + SECTORS(len[npart - 1]));
456}
457
c16d1dab
MW
458/*----- Moving average machinery ------------------------------------------*
459 *
460 * We're using an exponential moving average with a weighting factor of α
461 * (`alpha', above); larger values are more sensitive to recent changes. If
462 * the old average was v_1, and the measurement in the current interval is x,
463 * then the new average after this interval is
464 *
465 * v = α x + (1 − α) v_1 .
466 *
467 * Write β = 1 − α; so
468 *
469 * v = α x + β v_1 .
470 *
471 * Let x_0 = x, let x_1 be the measurement from the previous interval, and,
472 * in general, let x_i be the measurement from i intervals ago. Then another
473 * way to write the above would be
474 *
475 * v = α (x_0 + β x_1 + ⋯ + β^i x_i + ⋯) .
476 *
477 * Alas, our time intervals are not regular. Suppose that we get our next
478 * measurement after a gap of t intervals, for some integer t. We can
479 * compensate approximately by pretending that all of the missed intervals --
480 * and our new one -- had the same mean rate. Then we'd have calculated
481 *
482 * v = α (x + β x + ⋯ + β^{t−1} x) + β^t v_1
483 *
484 * 1 − β^t
485 * = α x ------- + β^t v_1
486 * 1 − β
487 *
488 * = x (1 − β^t) + β^t v_1 (since α = 1 − β)
489 *
490 * = x + β^t (v_1 − x) .
491 *
492 * Does this work in general? It's clearly correct in the case t = 1.
493 *
494 * Suppose the old average was v_2, and that over a period of t intervals
495 * (where t is not necessarily an integer) we measured a mean rate of x, and
496 * then after u intervals we measured a mean rate of x /again/. Then we'd
497 * firstly determine
498 *
499 * v_1 = x + β^t (v_2 − x)
500 *
501 * and then
502 *
503 * v = x + β^u (v_1 − x)
504 *
505 * = x + β^u (x + β^t (v_2 − x) − x)
506 *
507 * = x + β^{t+u} (v_2 − x) ,
508 *
509 * which is exactly what we'd have done if we'd calculated the same mean rate
510 * over the combined span of t + u intervals.
511 *
512 * One final wrinkle, in case that wasn't enough. There's a problem with the
513 * initial setup of an exponential moving average. Apparently
514 * (https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average)
515 * explains that we can do this better by calculating the average after k
516 * intervals as
517 *
518 * x_0 + β x_1 + β^2 x_2 + ⋯ + β^{k−1} x_{k−1}
519 * v′ = ------------------------------------------- .
520 * 1 + β + β^2 + ⋯ + β^{k−1}
521 *
522 * The numerator is our existing v/α; the denominator is (1 − β^k)/α; the
523 * factors of α cancel, and we find that v′ = v/(1 − β^k). This still holds
524 * in our situation, where k may not be an integer.
525 *
526 * To apply all of this:
527 *
528 * * we maintain the moving average v in `avg';
529 *
530 * * we maintain the total β^k in `corr'; and
531 *
532 * * we compute v′ = v/(1 − β^k) on demand up in `render_perfstats'.
533 */
534
535struct avg {
536 double avg, corr;
537};
538#define AVG_INIT { 0.0, 1.0 }
539
540static double alpha = 0.1; /* weighting factor for average */
541
542static void update_avg(struct avg *a, double t, double n)
543{
544 double rate = n/t, beta_t = pow(1 - alpha, t);
545
546 a->avg = rate + beta_t*(a->avg - rate);
547 a->corr *= beta_t;
548}
549
550static inline double current_avg(const struct avg *a)
551 { return (a->avg/(1 - a->corr)); }
552
4d69e943
MW
553/*----- The nonlinear progress model --------------------------------------*/
554
555/* The recorded portion of a single-layer DVD (i.e., DVD-5) can hold 2298496
556 * sectors of user data. This is preceded by 0x30000 = 196608 sectors of
557 * lead-in information, for a totoal of 2495104 sectors.
558 *
559 * The readable portion of a disc is an annulus with respective internal and
560 * external diameters of 44 mm and 117 mm. This annulus has an area of
561 * 9230.8 mm^2, so DVD has a storage density of about 270.3 sectors/mm^2. If
562 * the interior of the annulus were used for data storage rather than leaving
563 * a hole for a spindle and a clamping area, then it would be 10751 mm^2 and
564 * could store 2906107 sectors. (That means that the portion of the disc
565 * that's actually used to make it spin could have stored an additional
566 * 411003 sectors.)
567 *
568 * Sectors aren't stored on the surface willy-nilly, but arranged into a
569 * single archimedean spiral; bits are stored along this spiral at a
570 * more-or-less constant pitch. We are therefore led into an investigation
571 * of the arc-length of archimedean spirals.
572 *
573 * It's best to start with the polar equation of the spiral, which is simply
574 *
575 * r = k θ
576 *
577 * for a given constant k. The arc length of a curve expressed using polar
578 * coordinates is given by
579 *
580 * s = ∫ √(r^2 + (dr/dθ)^2) dθ
581 *
582 * = ∫ √(k^2 θ^2 + k^2) dθ
583 *
584 * = k ∫ √(1 + θ^2) dθ
585 *
586 * k
587 * = - [ θ √(1 + θ^2) + log(θ + √(1 + θ^2)) ] - s_0.
588 * 2
589 *
590 * We're assuming that the sectors are spaced out at a constant linear
591 * density along the spiral. We don't know the units for s, but there's some
592 * constant L such that A = s/L; so
593 *
594 * k
595 * A = --- [ θ √(1 + θ^2) + log(θ + √(1 + θ^2)) ] - A_0
596 * 2 L
597 *
598 * for some suitable constant A_0.
599 *
600 * Finally, we're assuming that the disc is spinning with some approximately
601 * constant angular velocity ω, so θ = ω t, giving
602 *
603 * k
604 * A = --- [ ω t √(1 + ω^2 t^2) + log(ω + √(1 + ω^2 t^2)) ] + A_0 .
605 * 2 L
606 *
607 * We can calculate approximate values for k/(2 L) and A_0. As stated above,
608 * the track pitch is about 0.75 µm; our inside and outside diameters of
609 * 44 mm and 117 mm correspond to angles of 184306 and 490088 radians
610 * respectively. Feeding those into the above equation for s gives arc
611 * lengths of 16984492558 and 120093346360 respectively, in unknown units.
612 * The difference is 103108853802, which should correspond to 2495104
613 * sectors, giving us 41324 arc-length units per sector. As a cross-check,
614 * the arc length corresponding to the inside diameter yields 411003 sectors,
615 * which is the same as we calculated above. This will be our A_0
616 */
617
618#ifdef unusef
619static double archimedes_arclen(double t)
620 /* Given an angle T, return the arc length of the canonical
621 * archimedean spiral r = θ, from θ = 0 up to θ = T.
622 */
623{
624 double u;
625
626 u = sqrt(1 + t*t);
627 return (t*u + log(t + u))/2;
628}
629#endif
630
631static double inv_archimedes_arclen(double s)
632 /* Given an arc length S, return the angle T such that the arc length
633 * of the canonical archimedean spiral r = θ, from θ = 0 up to θ = T,
634 * is equal to S.
635 */
636{
637 /* There is no closed-form solution, so we're going to invert the arc-
638 * length formula above numerically, using the Newton--Raphson method.
639 *
640 * Given an incorrect guess x_0 of a zero of some function f, we refine the
641 * guess by approximating f by its tangent at the point (x_0, f(x_0)).
642 * This will be a line with an equation like
643 *
644 * y = f'(x_0) x + c .
645 *
646 * We know that y = f(x_0) when x = x_0, so we can calculate
647 *
648 * c = f(x_0) - f'(x_0) x_0 .
649 *
650 * This will be zero when
651 *
652 * y = f'(x_0) x + f(x_0) - f'(x_0) x_0 = 0
653 *
654 * hwnce
655 *
656 * f'(x_0) x_0) - f(x_0) f(x_0)
657 * x = --------------------- = x_0 - ------- .
658 * f'(x_0) f'(x_0)
659 */
660
661 double t, ss, u, e;
662
663 /* We need to choose an initial estimate. This seems to work well in
664 * practice.
665 */
666 t = 1.5*sqrt(s);
667
668 for (;;) {
669 /* Compute s' = f(t). We open-code this calculation because the
670 * intermediate value √(1 + t^2) is also the gradient.
671 */
672 u = sqrt(1 + t*t);
673 ss = (t*u + log(t + u))/2;
674
675 /* Determine the error in f(t). We don't actually need much precision
676 * here, but 2 ulp seems achievable in practice with minimal cost: the
677 * usually sequence converges after only five iterations.
678 */
679 e = fabs(s/ss - 1);
680 if (e <= 2*DBL_EPSILON) return (t);
681
682 /* Not good enough. Refine the guess and go around again. */
683 t -= (ss - s)/u;
684 }
685}
686
687static double sectors_to_angle(secaddr base, secaddr low, secaddr high)
688 /* Return the angle, in radians, subtended by the range LOW up to
689 * HIGH of user sector addresses, given the physical sector address
690 * BASE of the first user-data sectors.
691 */
692{
693#define A0 411003.262489
694#define K 41324.4713654
695
696 return (inv_archimedes_arclen(K*(A0 + base + high)) -
697 inv_archimedes_arclen(K*(A0 + base + low)));
698
699#undef A0
700#undef K
701}
702
4d69e943
MW
703enum {
704 FLAT, /* not actually a real DVD */
705 SINGLE, /* disc with only one layer */
706 PTP, /* two layers, parallel track path */
707 OTP /* two layers, opposite track path */
708};
709
710struct geometry {
711 unsigned shape; /* one of the four codes above */
712 secaddr start0, start1; /* initial physical sector */
713 secaddr midpoint; /* sector address of layer switch */
714};
715
716#define GF_BLKDEV 1u
717static void setup_geometry(struct geometry *g, int fd, unsigned f,
718 secaddr sz)
719 /* Initialize G with information about the disc structure. FD is a
720 * file descriptor for the device; SZ is the size of the disc in
721 * sectors. If `GF_BLKDEV' is clear in F then assume that FD refers
722 * to a regular file; G is populated with a `FLAT' performance model.
723 * If `GF_BLKDEV' is set, then FD refers to a block device, so try to
724 * retreive detailed structure information from the drive.
725 */
726{
727#ifdef __linux__
728 dvd_struct ds;
729 const struct dvd_layer *ly;
730#endif
731 secaddr t;
732
e05f519b
MW
733#define LAYER_LIMIT 2298496 /* maximum (user) sectors on layer */
734#define DVDROM_OFFSET 0x30000 /* usual initial physical sector */
735
4d69e943
MW
736 if (!(f&GF_BLKDEV)) {
737 /* We're reading from a regular file. Assume that progress will be
738 * linear.
739 */
740
741 g->shape = FLAT;
742 g->midpoint = SECLIMIT;
743 return;
744 }
745
746#ifdef __linux__
747 /* We have Linux and its DVD ioctl(2) calls. Interrogate the disc to
748 * discover its structure.
749 */
750
751 ds.type = DVD_STRUCT_PHYSICAL;
752 ds.physical.layer_num = 0;
753 if (ioctl(fd, DVD_READ_STRUCT, &ds)) {
754 moan_syserr(errno, "failed to read physical disc structure");
755 goto guess_structure;
756 }
757 ly = &ds.physical.layer[0];
758 switch (ly->nlayers) {
759 case 0:
760 g->shape = SINGLE;
761 g->start0 = g->start1 = 0;
762 g->midpoint = SECLIMIT;
763 break;
764 case 1:
765 g->start0 = ly->start_sector;
766 if (ly->track_path) {
767 g->shape = OTP;
768 g->start1 = 0;
769 g->midpoint = ly->end_sector_l0 - ly->start_sector + 1;
770 } else {
771 g->shape = PTP;
772 g->midpoint = ly->end_sector - ly->start_sector + 1;
773 ds.physical.layer_num = 1;
774 if (ioctl(fd, DVD_READ_STRUCT, &ds)) {
775 moan_syserr(errno, "failed to read layer 1 physical structure");
776 goto guess_structure;
777 }
778 g->start1 = ly->start_sector;
779 }
780 break;
781 default:
782 moan("unexpected layer count %d", ly->nlayers + 1);
783 goto guess_structure;
784 }
785 return;
786guess_structure:
787#endif
788
789 /* Either we don't have Linux, or we found something confusing. Let's try
790 * to guess at what's going on.
791 *
792 * If the volume size is small enough to fit on a single layer then assume
793 * that's what's happened; otherwise assume opposite track path with a cut
794 * at the midpoint, rounded up to an ECC block (16 sectors).
795 */
796 g->start0 = DVDROM_OFFSET; g->start1 = 0;
797 if (sz <= LAYER_LIMIT) {
798 g->shape = SINGLE;
799 g->midpoint = SECLIMIT;
800 } else {
801 g->shape = OTP;
802 t = (sz + DVDROM_OFFSET)/2;
803 t += 15; t &= -16;
804 t -= DVDROM_OFFSET;
805 g->midpoint = t;
806 }
e05f519b
MW
807
808#undef LAYER_LIMIT
809#undef DVDROM_OFFSET
4d69e943
MW
810}
811
812static double linear_progress(const struct geometry *g,
813 secaddr a0, secaddr a1)
814 /* Convert the sector range from A0 to A1 into a progress measurement
815 * which is, by intention, approximately linearly related to time,
816 * given a geometry description G.
817 */
818{
819 double theta = 0.0;
820
821 switch (g->shape) {
822 case FLAT:
823 theta = a1 - a0;
824 break;
825 case SINGLE:
826 theta = sectors_to_angle(g->start0, a0, a1);
827 break;
828 case PTP:
829 if (a0 < g->midpoint)
830 theta += sectors_to_angle(g->start0,
831 a0, a1 < g->midpoint ? a1 : g->midpoint);
832 if (a1 > g->midpoint)
833 theta += sectors_to_angle(g->start1,
834 a0 > g->midpoint ? a0 : g->midpoint, a1);
835 break;
836 case OTP:
837 if (a0 < g->midpoint)
838 theta += sectors_to_angle(g->start0,
839 a0, a1 < g->midpoint ? a1 : g->midpoint);
840 if (a1 > g->midpoint)
841 theta += sectors_to_angle(g->start0,
842 2*g->midpoint - a1,
843 a0 > g->midpoint ?
844 2*g->midpoint - a0 : g->midpoint);
845 break;
846 default:
847 abort();
848 }
849 return (theta);
850}
851
b505435d
MW
852/*----- Common variables used by the copying machinery --------------------*/
853
854/* General reading state. */
855static dvd_reader_t *dvd; /* `libdvdread' state for device */
856static int dvdfd = -1, outfd = -1; /* input device and output image */
857static struct file *file; /* currently active file */
858static dvd_file_t *vob; /* current `.VOB' file, or null */
859static const char *mapfile; static FILE *mapfp; /* skipped regions map */
860static const char *errfile; static FILE *errfp; /* bad-sector log */
861static secaddr limit; /* upper bound on sectors */
862
863static secaddr bad_start; /* start of current bad region */
864static unsigned retry, max_retries = 4; /* retry state */
865
866/*----- Progress reporting ------------------------------------------------*/
867
868static secaddr nsectors, ndone; /* number of sectors done/to do */
4d69e943 869static double total_linear, done_linear; /* linear progress tracking */
b505435d
MW
870static secaddr last_pos; /* position last time we updated */
871static struct timeval last_time; /* time last time we updated */
4d69e943
MW
872static struct geometry geom; /* disc geometry for progress */
873static struct avg avg_rate = AVG_INIT, avg_linear = AVG_INIT;
b505435d 874static int bad_err; /* most recent error code */
45b498cf 875static FILE *progressfp; /* file on which to trace progress data */
b505435d
MW
876
877static const char throbber[] = "|<-<|>->"; /* throbber pattern */
878static unsigned throbix = 0; /* current throbber index */
879
880static struct progress_item /* stock progress items */
8cec8b64
MW
881 copy_progress, disc_progress,
882 file_progress, badblock_progress;
883
dc53ebfa 884static double scale_bytes(double n, const char **unit_out)
b505435d
MW
885 /* Determine a human-readable representation for N bytes. Divide N
886 * by some power of 1024, and store in *UNIT_OUT a string
887 * representing the conventional unit-prefix for that power of 1024.
888 */
12db0342 889{
dc53ebfa
MW
890 const char *unit = "";
891
892 if (n > 1600) { n /= 1024; unit = "k"; }
893 if (n > 1600) { n /= 1024; unit = "M"; }
894 if (n > 1600) { n /= 1024; unit = "G"; }
895 if (n > 1600) { n /= 1024; unit = "T"; }
dc53ebfa 896 *unit_out = unit; return (n);
12db0342 897}
dc53ebfa 898
b505435d 899#define TIMESTRMAX 16 /* maximum length of a duration string */
dc53ebfa 900static char *fmttime(unsigned long t, char *buf)
b505435d
MW
901 /* Format a count T of seconds. Write a suitable string to BUF,
902 * which will be no longer than `TIMESTRMAX' bytes including the
903 * terminating zero. Return BUF.
904 */
7fbe0fb9 905{
dc53ebfa
MW
906 if (t < 60) sprintf(buf, "%ld s", t);
907 else if (t < 3600) sprintf(buf, "%ld:%02ld", t/60, t%60);
908 else sprintf(buf, "%ld:%02ld:%02ld", t/3600, (t/60)%60, t%60);
909 return (buf);
910}
7fbe0fb9 911
dc53ebfa 912static void render_perfstats(struct progress_render_state *render)
b505435d
MW
913 /* Add performance statistics to RENDER.
914 *
915 * Specifically: the average transfer rate, and the estimated time to
916 * completion. (See `update_progress' for how the average
917 * computation works.)
918 */
dc53ebfa
MW
919{
920 int eta;
921 char timebuf[TIMESTRMAX];
4d69e943 922 double rate, linrate;
dc53ebfa
MW
923 const char *unit;
924
b505435d 925 /* If there's no average computed yet, then use some placeholder values. */
4d69e943
MW
926 rate = current_avg(&avg_rate);
927 linrate = current_avg(&avg_linear);
928 eta = (int)((total_linear - done_linear)/linrate + 0.5);
dc53ebfa 929
b505435d 930 /* Write out the statistics. */
dc53ebfa 931 rate = scale_bytes(rate*SECTORSZ, &unit);
c16d1dab 932 progress_putright(render, "ETA %s ",
4d69e943 933 avg_linear.avg ? fmttime(eta, timebuf) : "???");
dc53ebfa 934 progress_putright(render, "%.1f %sB/s, ", rate, unit);
7fbe0fb9 935}
dc53ebfa
MW
936
937static void render_copy_progress(struct progress_item *item,
938 struct progress_render_state *render)
b505435d
MW
939 /* Render the progress for the copy, i.e., the number of sectors
940 * copied against the total number to be copied.
941 */
7fbe0fb9 942{
dc53ebfa 943 double frac = (double)ndone/nsectors;
7fbe0fb9 944
dc53ebfa
MW
945 progress_putleft(render, " %c copied %.1f%%",
946 throbber[throbix], 100.0*frac);
947 render_perfstats(render);
948 progress_putleft(render, " (%"PRIuSEC" of %"PRIuSEC")", ndone, nsectors);
949
950 progress_showbar(render, frac);
7fbe0fb9
MW
951}
952
dc53ebfa
MW
953static void render_disc_progress(struct progress_item *item,
954 struct progress_render_state *render)
b505435d
MW
955 /* Render the progress for the disc, i.e., the current position
956 * against the total number of sectors on the disc.
957 */
dc53ebfa
MW
958{
959 double frac = (double)last_pos/limit;
5cc1b8be 960
dc53ebfa
MW
961 progress_putleft(render, " disc %.1f%% (%"PRIuSEC" of %"PRIuSEC")",
962 100.0*frac, last_pos, limit);
963 progress_showbar(render, frac);
964}
a090d4b3 965
dc53ebfa
MW
966static void render_file_progress(struct progress_item *item,
967 struct progress_render_state *render)
b505435d
MW
968 /* Render the progress for the current file, i.e., the current
969 * position within the file against the file size.
970 */
dc53ebfa
MW
971{
972 secaddr off = last_pos - file->start, len = file->end - file->start;
973 char fn[MAXFNSZ];
974 double frac;
975
976 store_filename(fn, file->id);
977 frac = (double)off/len;
978 progress_putleft(render, " `%s' %.1f%% (%"PRIuSEC" of %"PRIuSEC")",
979 fn, 100.0*frac, off, len);
980 progress_showbar(render, frac);
981}
982
983static void render_badblock_progress(struct progress_item *item,
984 struct progress_render_state *render)
b505435d
MW
985 /* Render a notice about the progress through the current bad block
986 * region.
987 */
dc53ebfa
MW
988{
989 secaddr n = last_pos - bad_start;
990 int bg;
991
992 if (!n) {
993 progress_putleft(render, " Retrying bad sector %"PRIuSEC"", bad_start);
994 progress_putright(render, "attempt %u/%u ", retry + 1, max_retries);
995 bg = 4;
996 } else {
997 progress_putleft(render, " Found %"PRIuSEC" bad %s",
998 n, n == 1 ? "sector" : "sectors");
999 progress_putright(render, "%"PRIuSEC" .. %"PRIuSEC" ",
1000 bad_start, last_pos);
1001 bg = 1;
1002 }
1003 if (bad_err && bad_err != EIO)
1004 progress_putleft(render, " (%s)", strerror(bad_err));
1005 progress_shownotice(render, bg, 7);
1006}
1007
1008static void update_progress(secaddr pos)
b505435d
MW
1009 /* Recompute the data displayed by the progress renderer functions
1010 * above, based on the new current sector POS.
1011 */
7fbe0fb9 1012{
7fbe0fb9 1013 struct timeval now;
4d69e943 1014 double t, delta_r;
b505435d
MW
1015
1016 /* Find the current time and the delta since the last time we updated.
1017 * This will be the length of the current interval.
1018 */
a238b544 1019 gettimeofday(&now, 0); t = tvdiff(&last_time, &now);
b505435d
MW
1020
1021 /* If no time at all has passed (unlikely!) then skip the rate
1022 * calculation. (The moving average wouldn't be affected anyway.)
1023 */
7fbe0fb9 1024 if (t) {
b505435d
MW
1025 /* Update the moving average and the correction term, and start the next
1026 * interval.
1027 */
1028
4d69e943 1029 delta_r = linear_progress(&geom, last_pos, pos);
c16d1dab 1030 update_avg(&avg_rate, t, pos - last_pos);
4d69e943
MW
1031 update_avg(&avg_linear, t, delta_r);
1032 ndone += pos - last_pos; done_linear += delta_r;
1033 last_time = now; last_pos = pos;
7fbe0fb9 1034 }
b505435d 1035
45b498cf
MW
1036 /* Trace progress state if requested. */
1037 if (progressfp) {
4d69e943 1038 fprintf(progressfp, "%10ju.%06ld %"PRIuSEC" %f %f\n",
45b498cf 1039 (uintmax_t)now.tv_sec, now.tv_usec,
4d69e943
MW
1040 ndone, done_linear,
1041 (total_linear - done_linear)/current_avg(&avg_linear));
45b498cf
MW
1042 check_write(progressfp, "progress trace file");
1043 }
1044
b505435d 1045 /* Advance the throbber character. */
dc53ebfa 1046 throbix++; if (!throbber[throbix]) throbix = 0;
7fbe0fb9
MW
1047}
1048
dc53ebfa 1049static void report_progress(secaddr pos)
b505435d
MW
1050 /* Update the progress variables (as `update_progress') and redraw
1051 * the progress display.
1052 */
dc53ebfa
MW
1053 { update_progress(pos); progress_update(&progress); }
1054
b505435d
MW
1055/*----- Basic disc I/O ----------------------------------------------------*/
1056
513eba44
MW
1057struct badblock { secaddr start, end; };
1058DEFVEC(badblock_v, struct badblock);
1059static badblock_v badblocks = VEC_INIT;
b505435d
MW
1060 /* This is a list of /fake/ bad-block ranges, used to test the
1061 * recovery algorithm. It's a rule that the ranges in this table
1062 * mustn't overlap -- though it's OK if they abut.
1063 */
513eba44
MW
1064
1065static int compare_badblock(const void *a, const void *b)
b505435d
MW
1066 /* A `qsort' comparison function for the fake bad-blocks list.
1067 * Ranges which start earlier are sorted before rangers which start
1068 * later.
1069 */
513eba44
MW
1070{
1071 const struct badblock *ba = a, *bb = b;
1072
b505435d 1073 /* Order by start sector. */
513eba44
MW
1074 if (ba->start < bb->start) return (-1);
1075 else if (ba->start > bb->start) return (+1);
1076
b505435d 1077 /* Order by end sector as a tiebreak. This shouldn't be possible. */
513eba44
MW
1078 if (ba->end < bb->end) return (-1);
1079 else if (ba->end > bb->end) return (+1);
1080
b505435d 1081 /* They're equal. This shouldn't be possible either. */
513eba44
MW
1082 return (0);
1083}
1084
b505435d
MW
1085static double bad_block_delay = 0.0, good_block_delay = 0.0;
1086 /* delay parameters for performance testing */
dc53ebfa 1087
5cc1b8be 1088static ssize_t read_sectors(secaddr pos, void *buf, secaddr want)
b505435d
MW
1089 /* Try to read WANT sectors from the input, starting with sector POS,
1090 * and write the contents to BUF. Return the number of /whole
1091 * sectors/ read; this will be 0 at end-of-file (though that
1092 * shouldn't happen). The returned length will be smaller than WANT
1093 * only if end-of-file or a system error prevents reading further.
1094 * Returns -1 on a system error if that prevented us from reading
1095 * anything at all.
1096 *
1097 * This function is where the fake bad-blocks list is handled.
1098 */
7fbe0fb9 1099{
0f0ba33f 1100 ssize_t n, done;
513eba44 1101 size_t lo, mid, hi;
0f0ba33f 1102 int fakeerr = 0;
513eba44 1103 struct badblock *bad, *best;
0f0ba33f 1104 unsigned char *p = buf;
513eba44 1105
b505435d 1106 /* See whether the requested range intersects a bad-blocks range. */
6e7426fe 1107 if (badblocks.n) {
b505435d
MW
1108 /* Since the list is sorted, we use a binary search. We're looking for
1109 * the earliest-starting range which /ends after/ POS. If this starts
1110 * /at or before/ POS, then POS itself is a bad sector, and we should
1111 * pretend an I/O error; otherwise, if the bad range /starts/ somewhere
1112 * in the range we're trying to read then we must pretend a short read;
1113 * and otherwise there's nothing to do.
1114 */
1115
1116 /* Throughout, `best' points to the earliest-starting range we've found
1117 * which (starts and) finishes after POS. Ranges with indices below LO
1118 * end too early to be interesting; similarly, ranges with indices HI or
1119 * above start later than POS. If we find a range which actually covers
1120 * POS exactly then we'll stop early.
1121 */
6e7426fe 1122 best = 0; lo = 0; hi = badblocks.n;
513eba44 1123#ifdef DEBUG
cf556df2 1124 progress_clear(&progress);
6e7426fe
MW
1125 printf(";; searching badblocks for %"PRIuSEC" .. %"PRIuSEC"\n",
1126 pos, pos + want);
513eba44 1127#endif
6e7426fe 1128 while (lo < hi) {
b505435d
MW
1129 /* Standard binary-search loop: we continue until the pointers
1130 * converge.
1131 */
1132
1133 /* Try the midpoint between the two bounds. */
6e7426fe 1134 mid = lo + (hi - lo)/2; bad = &badblocks.v[mid];
513eba44 1135#ifdef DEBUG
6e7426fe
MW
1136 printf(";; try %zu (%"PRIuSEC" .. %"PRIuSEC")... ",
1137 mid, bad->start, bad->end);
513eba44 1138#endif
b505435d
MW
1139
1140 /* Follow our invariant. If the range starts strictly after POS, then
1141 * it's too late to overlap, so bring down HI to cover it; but it must
1142 * be closer than any previous block we've found, so remember it in
1143 * `best'. Similarly, if the range ends /at or before/ POS then it
1144 * stops too early, so bring up LO to cover it (but otherwise forget
1145 * about it because it can't affect what we're doing).
1146 *
1147 * If we get a match then we stop immediately and fake a bad block.
1148 */
6e7426fe
MW
1149 if (pos < bad->start) { D( printf("high\n"); ) best = bad; hi = mid; }
1150 else if (pos >= bad->end) { D( printf("low\n"); ) lo = mid + 1; }
12ade065
MW
1151 else {
1152 D( printf("match!\n"); )
1153 errno = EIO; sit(bad_block_delay); return (-1);
1154 }
6e7426fe 1155 }
b505435d
MW
1156
1157 /* We're done. Check to see whether the bad range starts early enough.
1158 * If so, remember that we're simulating an error, apply the delay, and
1159 * bamboozle the rest of the code into performing a short read.
1160 */
513eba44 1161#ifdef DEBUG
6e7426fe
MW
1162 if (best)
1163 printf(";; next is %"PRIuSEC" .. %"PRIuSEC"\n",
1164 best->start, best->end);
513eba44 1165#endif
6e7426fe 1166 if (best && pos + want > best->start)
dc53ebfa 1167 { want = best->start - pos; fakeerr = EIO; sit(bad_block_delay); }
6e7426fe 1168 }
1c67758f 1169
b505435d
MW
1170 /* Try to read stuff into the buffer until we find a reason why we can't
1171 * continue. Obviously we need to keep track of how much stuff we've read
1172 * on previous iterations.
1173 */
1c67758f 1174 done = 0; errno = 0;
0f0ba33f 1175 while (want) {
b505435d
MW
1176
1177 /* Read from the current file's input source. If that's a scrambled
1178 * video file, then use `libdvdread'; if it's the `raw' file, then go to
1179 * the block device; if it's nothing at all, then fill with zeros.
1180 * Always force a seek to the right place, in case things got messed up
1181 * by some previous error.
1182 */
0f0ba33f
MW
1183 if (vob)
1184 { errno = 0; n = DVDReadBlocks(vob, pos - file->start, want, p); }
1185 else if (file) {
1186 if (lseek(dvdfd, (off_t)pos*SECTORSZ, SEEK_SET) < 0)
1187 bail_syserr(errno, "failed to seek to sector %"PRIuSEC"", pos);
1188 errno = 0; n = read(dvdfd, p, want*SECTORSZ);
1189 if (n >= 0) n /= SECTORSZ;
1190 } else {
1191 memset(p, 0, want*SECTORSZ);
1192 n = want;
1193 }
7fbe0fb9 1194
b505435d
MW
1195 /* If we read some stuff then update the buffer pointer and lengths. If
1196 * we hit end-of-file then stop. If we hit a bad sector then maybe make
1197 * a note of it in the bad-sector log. On any other kind of error, just
1198 * stop.
1199 */
0f0ba33f 1200 if (n > 0) { done += n; pos += n; p += n*SECTORSZ; want -= n; }
af3973a1
MW
1201 else if (!n) break;
1202 else if (errno == EIO && errfile) {
1203 open_file_on_demand(errfile, &errfp, "bad-sector error log");
1204 fprintf(errfp, "%"PRIuSEC" %"PRIuSEC"\n", pos, pos + 1);
1205 check_write(errfp, "bad-sector error log");
1206 break;
1207 } else if (errno != EINTR) break;
0f0ba33f 1208 }
b505435d
MW
1209
1210 /* We made it. If we saved up a fake error, and there wasn't a real error
1211 * (which should obviously take priority) then present the fake error to
1212 * the caller. If there wasn't an error, then everything must have been
1213 * good so impose the good-block delay -- note that a bad-block delay will
1214 * already have been imposed above. Finally, return the accumulated count
1215 * of sectors successfully read, or report the end-of-file or error
1216 * condition as applicable.
1217 */
0f0ba33f 1218 if (fakeerr && !errno) errno = fakeerr;
dc53ebfa 1219 else if (done > 0 && good_block_delay) sit(done*good_block_delay);
0f0ba33f 1220 return (!done && errno ? -1 : done);
7fbe0fb9
MW
1221}
1222
b505435d
MW
1223/*----- Tracking machinery for the bad-sector algorithm -------------------*
1224 *
1225 * While we're probing around trying to find the end of the bad region, we'll
1226 * have read some good data. We want to try to keep as much good data as we
1227 * can, and avoid re-reading it because (a) it's pointless I/O work, but more
1228 * importantly (b) it might not work the second time. The machinery here
1229 * is for making this work properly.
1230 *
1231 * There are two parts to this which don't really intersect, but for
1232 * convenience the tracking information for them is kept in the same
1233 * `recoverybuf' structure.
1234 *
1235 * * The `short-range' machinery keeps track of a contiguous region of good
1236 * data stored in the caller's buffer.
1237 *
1238 * * The `long-range' machinery keeps track of a contiguous region of good
1239 * data that's beyond the range of the buffer.
1240 */
1241
f0e437bc 1242struct recoverybuf {
b505435d
MW
1243 /* Information used to keep track of where good and bad sectors are
1244 * while we're trying to find the end of a region of bad sectors.
1245 */
1246
1247 /* Short-range buffer tracking. */
1248 unsigned char *buf; /* pointer to the actual buffer */
1249 secaddr sz; /* size of the buffer in sectors */
1250 secaddr pos; /* sector address corresponding to
1251 * the start of the buffer */
1252 secaddr start, end; /* bounds of the live region within
1253 * the buffer, as offsets in
1254 * sectors from the buffer start */
1255
1256 /* Long-range tracking. */
1257 secaddr good_lo, good_hi; /* known-good region, as absolute
1258 * sector addresses */
f0e437bc
MW
1259};
1260
1261static void rearrange_sectors(struct recoverybuf *r,
1262 secaddr dest, secaddr src, secaddr len)
b505435d
MW
1263 /* Shuffle data about in R's buffer. Specifically, move LEN sectors
1264 * starting SRC sectors from the start of the buffer to a new
1265 * position DEST sectors from the start.
1266 *
1267 * Unsurprisingly, this is a trivial wrapper around `memmove', with
1268 * some range checking thrown in; it's only used by `recovery_read_-
1269 * buffer' and `find_good_sector' below.
1270 */
f0e437bc 1271{
8cec8b64 1272 assert(dest + len <= r->sz); assert(src + len <= r->sz);
f0e437bc
MW
1273 memmove(r->buf + dest*SECTORSZ, r->buf + src*SECTORSZ, len*SECTORSZ);
1274}
1275
1276#ifdef DEBUG
dc53ebfa
MW
1277static PRINTF_LIKE(2, 3)
1278 void show_recovery_buffer_map(const struct recoverybuf *r,
1279 const char *what, ...)
b505435d 1280 /* Dump a simple visualization of the short-range tracking state. */
f0e437bc
MW
1281{
1282 va_list ap;
1283
1284 va_start(ap, what);
cf556df2 1285 progress_clear(&progress);
f0e437bc
MW
1286 printf(";; recovery buffer (");
1287 vprintf(what, ap);
1288 printf("): "
1289 "(%"PRIuSEC") ..%"PRIuSEC".. "
1290 "[%"PRIuSEC" ..%"PRIuSEC".. %"PRIuSEC"] "
1291 "..%"PRIuSEC".. (%"PRIuSEC")\n",
1292 r->pos, r->start,
1293 r->pos + r->start, r->end - r->start, r->pos + r->end,
1294 r->sz - r->end, r->pos + r->sz);
1295 va_end(ap);
1296 assert(r->start <= r->end);
1297 assert(r->end <= r->sz);
1298}
1299#endif
1300
1301static ssize_t recovery_read_sectors(struct recoverybuf *r,
1302 secaddr pos, secaddr off, secaddr want)
b505435d
MW
1303 /* Try to read WANT sectors starting at sector address POS from the
1304 * current file into R's buffer, at offset OFF sectors from the start
1305 * of the buffer. Return the number of sectors read, zero if at end
1306 * of file, or -1 in the event of a system error.
1307 *
1308 * This is a trivial wrapper around `read_sectors' with some
1309 * additional range checking, used only by `recovery_read_buffer'
1310 * below.
1311 */
f0e437bc
MW
1312{
1313 ssize_t n;
1314
9c8ab410 1315 assert(off <= r->sz); assert(want <= r->sz - off);
06b3c6a0 1316 assert(pos == r->pos + off);
f0e437bc
MW
1317 n = read_sectors(pos, r->buf + off*SECTORSZ, want);
1318 return (n);
1319}
1320
dc53ebfa
MW
1321static ssize_t recovery_read_buffer(struct recoverybuf *r,
1322 secaddr pos, secaddr want)
b505435d
MW
1323 /* Try to read WANT sectors, starting at sector address POS, from the
1324 * current file into the buffer R, returning a count of the number of
1325 * sectors read, or 0 if at end of file, or -1 in the case of a
1326 * system error, as for `read_sectors'. The data will end up
1327 * /somewhere/ in the buffer, but not necessarily at the start.
1328 */
f0e437bc
MW
1329{
1330 secaddr diff, pp, nn;
1331 ssize_t n;
1332
b505435d
MW
1333 /* This is the main piece of the short-range tracking machinery. It's
1334 * rather complicated, so hold on tight. (It's much simpler -- and less
1335 * broken -- than earlier versions were, though.)
1336 */
1337
f0e437bc 1338#ifdef DEBUG
cf556df2 1339 progress_clear(&progress);
f0e437bc
MW
1340 show_recovery_buffer_map(r, "begin(%"PRIuSEC", %"PRIuSEC")", pos, want);
1341#endif
1342
b505435d
MW
1343 /* The first order of business is to make space in the buffer for this new
1344 * data. We therefore start with a case analysis.
1345 */
f0e437bc 1346 if (pos < r->pos) {
b505435d
MW
1347 /* The new position is before the current start of the buffer, so we have
1348 * no choice but to decrease the buffer position, which will involve
1349 * shifting the existing material upwards.
1350 */
1351
1352 /* Determine how far up we'll need to shift. */
f0e437bc 1353 diff = r->pos - pos;
b505435d 1354
f0e437bc 1355 if (r->start + diff >= r->sz) {
b505435d
MW
1356 /* The material that's currently in the buffer would be completely
1357 * shifted off the end, so we have no choice but to discard it
1358 * completely.
1359 */
1360
f0e437bc
MW
1361 r->pos = pos; r->start = r->end = 0;
1362#ifdef DEBUG
1363 show_recovery_buffer_map(r, "cleared; shift up by %"PRIuSEC"", diff);
1364#endif
1365 } else {
b505435d
MW
1366 /* Some of the material in the buffer will still be there. We might
1367 * lose some stuff off the end: start by throwing that away, and then
1368 * whatever's left can be moved easily.
1369 */
1370
f0e437bc
MW
1371 if (r->end + diff > r->sz) r->end = r->sz - diff;
1372 rearrange_sectors(r, r->start + diff, r->start, r->end - r->start);
ea027dd6 1373 r->pos -= diff; r->start += diff; r->end += diff;
f0e437bc
MW
1374#ifdef DEBUG
1375 show_recovery_buffer_map(r, "shifted up by %"PRIuSEC"", diff);
1376#endif
1377 }
1378 } else if (pos > r->pos + r->end) {
b505435d
MW
1379 /* The new position is strictly beyond the old region. We /could/ maybe
1380 * keep this material, but it turns out to be better not to. To keep it,
1381 * we'd have to also read the stuff that's in between the end of the old
1382 * region and the start of the new one, and that might contain bad
1383 * sectors which the caller is specifically trying to skip. We just
1384 * discard the entire region here so as not to subvert the caller's
1385 * optimizations.
1386 */
1387
8ed763a5 1388 r->pos = pos; r->start = r->end = 0;
f0e437bc 1389#ifdef DEBUG
8ed763a5 1390 show_recovery_buffer_map(r, "cleared; beyond previous region");
f0e437bc
MW
1391#endif
1392 } else if (pos + want > r->pos + r->sz) {
b505435d
MW
1393 /* The requested range of sectors extends beyond the region currently
1394 * covered by the buffer. We must therefore increase the buffer position
1395 * which will involve shifting the existing material downwards.
1396 */
1397
1398 /* Determine how far down we'll need to shift. */
f0e437bc 1399 diff = (pos + want) - (r->pos + r->sz);
b505435d 1400
f0e437bc 1401 if (r->end <= diff) {
b505435d
MW
1402 /* The material that's currently in the buffer would be completely
1403 * shifted off the beginning, so we have no choice but to discard it
1404 * completely.
1405 */
1406
f0e437bc
MW
1407 r->pos = pos; r->start = r->end = 0;
1408#ifdef DEBUG
1409 show_recovery_buffer_map(r, "cleared; shift down by %"PRIuSEC"", diff);
1410#endif
1411 } else {
b505435d
MW
1412 /* Some of the material in the buffer will still be there. We might
1413 * lose some stuff off the beginning: start by throwing that away, and
1414 * then whatever's left can be moved easily.
1415 */
1416
f0e437bc
MW
1417 if (r->start < diff) r->start = diff;
1418 rearrange_sectors(r, r->start - diff, r->start, r->end - r->start);
ea027dd6 1419 r->pos += diff; r->start -= diff; r->end -= diff;
f0e437bc
MW
1420#ifdef DEBUG
1421 show_recovery_buffer_map(r, "shifted down by %"PRIuSEC"", diff);
1422#endif
1423 }
1424 }
1425
b505435d
MW
1426 /* We now have space in the buffer in which to put the new material.
1427 * However, the buffer already contains some stuff. We may need to read
1428 * some data from the input file into an area before the existing
1429 * material, or into an area following the existing stuff, or both, or
1430 * (possibly) neither.
1431 */
1432
f0e437bc 1433 if (pos < r->pos + r->start) {
b505435d
MW
1434 /* The requested position is before the current good material, so we'll
1435 * need to read some stuff there.
1436 */
1437
1438 /* Determine the place in the buffer where this data will be placed, and
1439 * how long it will need to be. Try to extend it all the way to the
1440 * existing region even if this is more than the caller wants, because it
1441 * will mean that we can join it onto the existing region rather than
1442 * having to decide which of two disconnected parts to throw away.
1443 */
f0e437bc 1444 pp = pos - r->pos; nn = r->start - pp;
b505435d
MW
1445
1446 /* Read the data. */
f0e437bc
MW
1447#ifdef DEBUG
1448 printf(";; read low (%"PRIuSEC"@%"PRIuSEC", %"PRIuSEC")", pos, pp, nn);
1449 fflush(stdout);
1450#endif
1451 n = recovery_read_sectors(r, pos, pp, nn);
1452#ifdef DEBUG
c0bff521 1453 printf(" -> %zd\n", n);
f0e437bc 1454#endif
b505435d
MW
1455
1456 /* See whether it worked. */
f0e437bc 1457 if (n != nn) {
b505435d
MW
1458 /* We didn't get everything we wanted. */
1459
1460 /* If we got more than the caller asked for then technically this is
1461 * good; but there must be some problem lurking up ahead, and the
1462 * caller will want to skip past that. So we don't update the tracking
1463 * information to reflect our new data; even though this /looks/ like a
1464 * success, it isn't really.
1465 */
d935b481 1466 if (n >= 0 && n > want) n = want;
b505435d
MW
1467
1468 /* We're done. */
f0e437bc
MW
1469 goto end;
1470 }
b505435d
MW
1471
1472 /* Extend the region to include the new piece. */
f0e437bc
MW
1473 r->start = pp;
1474#ifdef DEBUG
1475 show_recovery_buffer_map(r, "joined new region");
1476#endif
1477 }
1478
1479 if (pos + want > r->pos + r->end) {
b505435d
MW
1480 /* The requested region extends beyond the current region, so we'll need
1481 * to read some stuff there.
1482 */
1483
1484 /* Determine the place in the buffer where this data will be placed, and
1485 * how long it will need to be. Note that pos <= r->pos + r->end, so
1486 * there won't be a gap between the old good region and the material
1487 * we're trying to read.
1488 */
f0e437bc 1489 pp = r->end; nn = (pos + want) - (r->pos + r->end);
b505435d
MW
1490
1491 /* Read the data. */
f0e437bc
MW
1492#ifdef DEBUG
1493 printf(";; read high (%"PRIuSEC"@%"PRIuSEC", %"PRIuSEC")",
1494 r->pos + pp, pp, nn);
62fc1b88 1495 fflush(stdout);
f0e437bc 1496#endif
69d1ae9d 1497 n = recovery_read_sectors(r, r->pos + pp, pp, nn);
f0e437bc 1498#ifdef DEBUG
c0bff521 1499 printf(" -> %zd\n", n);
f0e437bc 1500#endif
b505435d
MW
1501
1502 /* See whether it worked. */
f0e437bc 1503 if (n > 0) {
b505435d
MW
1504 /* We read something, so add it onto the existing region. */
1505
f0e437bc
MW
1506 r->end += n;
1507#ifdef DEBUG
1508 show_recovery_buffer_map(r, "joined new region");
1509#endif
1510 }
1511 }
1512
b505435d
MW
1513 /* Work out the return value to pass back to the caller. The newly read
1514 * material has been merged with the existing region (the case where we
1515 * didn't manage to join the two together has been handled already), so we
1516 * can easily work out how much stuff is available by looking at the
1517 * tracking information. It only remains to bound the region size by the
1518 * requested length.
1519 */
f0e437bc
MW
1520 n = r->pos + r->end - pos;
1521 if (!n && want) n = -1;
d1105ba7 1522 else if (n > want) n = want;
f0e437bc
MW
1523
1524end:
b505435d 1525 /* Done. */
f0e437bc 1526#ifdef DEBUG
c0bff521 1527 show_recovery_buffer_map(r, "done; return %zd", n);
f0e437bc
MW
1528#endif
1529 return (n);
1530}
1531
dc53ebfa
MW
1532static ssize_t recovery_read_multiple(struct recoverybuf *r,
1533 secaddr pos, secaddr want)
b505435d
MW
1534 /* Try to read WANT sectors, starting at sector address POS, from the
1535 * current file, returning a count of the number of sectors read, or
1536 * 0 if at end of file, or -1 in the case of a system error, as for
1537 * `read_sectors'. Some data might end up in R's buffer, but if WANT
1538 * is larger than R->sz then a lot will be just thrown away.
1539 *
1540 * This is only used by `recovery_read' below.
1541 */
dc53ebfa
MW
1542{
1543 ssize_t n;
1544 secaddr skip, want0 = want;
1545
b505435d
MW
1546 /* If the request is larger than the buffer, then we start at the /end/ and
1547 * work backwards. If we encounter a bad sector while we're doing this,
1548 * then we report a short read as far as the bad sector: the idea is to
1549 * find the /latest/ bad sector we can. The caller will want to skip past
1550 * the bad sector, so the fact that we implicitly lied about the earlier
1551 * data as being `good' won't matter.
1552 */
1553
dc53ebfa 1554 while (want > r->sz) {
b505435d
MW
1555 /* There's (strictly!) more than a buffer's worth. Fill the buffer with
1556 * stuff and reduce the requested size.
1557 */
1558
dc53ebfa
MW
1559 skip = want - r->sz;
1560 n = recovery_read_buffer(r, pos + skip, r->sz);
b505435d
MW
1561
1562 /* If it failed, then we always return a positive result, because we're
1563 * pretending we managed to read all of the (nonempty) preceding
1564 * material.
1565 */
dc53ebfa 1566 if (n < r->sz) return (skip + (n >= 0 ? n : 0));
b505435d
MW
1567
1568 /* Cross off a buffer's worth and go around again. */
dc53ebfa
MW
1569 want -= r->sz;
1570 }
b505435d
MW
1571
1572 /* Read the last piece. If it fails or comes up short, then we don't need
1573 * to mess with the return code this time.
1574 */
dc53ebfa
MW
1575 n = recovery_read_buffer(r, pos, want);
1576 if (n < 0 || n < want) return (n);
b505435d
MW
1577
1578 /* It all worked. Return the full original amount requested. */
dc53ebfa
MW
1579 return (want0);
1580}
1581
1582static ssize_t recovery_read(struct recoverybuf *r,
1583 secaddr pos, secaddr want)
b505435d
MW
1584 /* Try to read WANT sectors, starting at sector address POS, from the
1585 * current file, returning a count of the number of
1586 * sectors read, or 0 if at end of file, or -1 in the case of a
1587 * system error, as for `read_sectors'. Some data might end up in
1588 * R's buffer, but if WANT is larger than R->sz then a lot will be
1589 * just thrown away.
1590 */
dc53ebfa 1591{
b505435d 1592 secaddr lo = pos, hi = pos + want, span; /* calculate the request bounds */
dc53ebfa
MW
1593 ssize_t n;
1594
b505435d
MW
1595 /* This is the main piece of the long-range tracking machinery.
1596 * Fortunately, it's much simpler than the short-range stuff that we've
1597 * just dealt with.
1598 */
1599
dc53ebfa 1600 if (hi < r->good_lo || lo > r->good_hi) {
b505435d
MW
1601 /* The requested region doesn't abut or overlap with the existing good
1602 * region, so it's no good to us. Just read the requested region; if it
1603 * worked at all, then replace the current known-good region with the
1604 * region that was successfully read.
1605 */
1606
dc53ebfa
MW
1607 n = recovery_read_multiple(r, lo, hi - lo);
1608 if (n > 0) { r->good_lo = lo; r->good_hi = lo + n; }
1609 return (n);
1610 }
1611
1612 if (hi > r->good_hi) {
b505435d
MW
1613 /* The requested region ends later than the current known-good region.
1614 * Read the missing piece. We're doing this first so that we find later
1615 * bad sectors.
1616 */
1617
dc53ebfa
MW
1618 span = hi - r->good_hi;
1619 n = recovery_read_multiple(r, r->good_hi, span);
b505435d
MW
1620
1621 /* If we read anything at all, then extend the known-good region. */
dc53ebfa 1622 if (n > 0) r->good_hi += n;
b505435d
MW
1623
1624 /* If we didn't read everything we wanted, then report this as a short
1625 * read (so including some nonempty portion of the known-good region).
1626 */
dc53ebfa
MW
1627 if (n < 0 || n < span) return (r->good_hi - lo);
1628 }
1629
1630 if (lo < r->good_lo) {
b505435d
MW
1631 /* The requested region begins earlier than the known-good region. */
1632
dc53ebfa
MW
1633 span = r->good_lo - lo;
1634 n = recovery_read_multiple(r, lo, span);
b505435d
MW
1635
1636 /* If we read everything we wanted, then extend the known-good region.
1637 * Otherwise, we're better off keeping the stuff after the bad block.
1638 */
dc53ebfa
MW
1639 if (n == span) r->good_lo = lo;
1640 else return (n);
1641 }
1642
b505435d
MW
1643 /* Everything read OK, and we've extended the known-good region to cover
1644 * the requested region. So return an appropriate code by consulting the
1645 * new known-good region.
1646 */
dc53ebfa
MW
1647 n = r->good_hi - pos; if (n > want) n = want;
1648 if (!n) { errno = EIO; n = -1; }
1649 return (n);
1650}
1651
b505435d
MW
1652/*----- Skipping past regions of bad sectors ------------------------------*/
1653
1654static double clear_factor = 0.5; /* proportion of clear sectors needed */
1655static secaddr clear_min = 1, clear_max = SECLIMIT; /* absolute bounds */
1656static double step_factor = 2.0; /* factor for how far to look ahead */
1657static secaddr step_min = 1, step_max = 0; /* and absolute bounds */
dc53ebfa 1658
8cec8b64 1659static void recovered(secaddr bad_lo, secaddr bad_hi)
b505435d
MW
1660 /* Do all of the things that are necessary when a region of bad
1661 * sectors has been found between BAD_LO (inclusive) and BAD_HI
1662 * (exclusive).
1663 */
8cec8b64
MW
1664{
1665 char fn[MAXFNSZ];
1666
b505435d 1667 /* Remove the progress display temporarily. */
8cec8b64
MW
1668 progress_clear(&progress);
1669
b505435d 1670 /* Print a message into the permanent output log. */
8cec8b64
MW
1671 if (!file || id_kind(file->id) == RAW)
1672 moan("skipping %"PRIuSEC" bad sectors (%"PRIuSEC" .. %"PRIuSEC")",
1673 bad_hi - bad_lo, bad_lo, bad_hi);
1674 else {
1675 store_filename(fn, file->id);
1676 moan("skipping %"PRIuSEC" bad sectors (%"PRIuSEC" .. %"PRIuSEC"; "
1677 "`%s' %"PRIuSEC" .. %"PRIuSEC" of %"PRIuSEC")",
1678 bad_hi - bad_lo, bad_lo, bad_hi,
1679 fn, bad_lo - file->start, bad_hi - file->start,
1680 file->end - file->start);
1681 }
1682
1683 if (mapfile) {
b505435d
MW
1684 /* The user requested a map of the skipped regions, so write an entry. */
1685
1686 /* Open the file, if it's not open already. */
8cec8b64 1687 open_file_on_demand(mapfile, &mapfp, "bad-sector region map");
b505435d
MW
1688
1689 /* Write the sector range. */
8cec8b64
MW
1690 fprintf(mapfp, "%"PRIuSEC" %"PRIuSEC" # %"PRIuSEC" sectors",
1691 bad_lo, bad_hi, bad_hi - bad_lo);
1692
b505435d
MW
1693 /* If we're currently reading from a file then note down the position in
1694 * the file in the comment. (Intentional bad sectors are frequently at
1695 * the start and end of titles, so this helps a reader to decide how
1696 * concerned to be.)
1697 */
8cec8b64
MW
1698 if (file && id_kind(file->id) != RAW)
1699 fprintf(mapfp, "; `%s' %"PRIuSEC" .. %"PRIuSEC" of %"PRIuSEC"",
1700 fn, bad_lo - file->start, bad_hi - file->start,
1701 file->end - file->start);
1702
b505435d
MW
1703 /* Done. Flush the output to the file so that we don't lose it if we
1704 * crash!
1705 */
8cec8b64
MW
1706 fputc('\n', mapfp);
1707 check_write(mapfp, "bad-sector region map");
1708 }
1709
b505435d
MW
1710 /* Adjust the position in our output file to skip past the bad region.
1711 * (This avoids overwriting anything that was there already, which is
1712 * almost certainly less wrong than anything we could come up with here.)
1713 */
8cec8b64
MW
1714 if (lseek(outfd, (off_t)(bad_hi - bad_lo)*SECTORSZ, SEEK_CUR) < 0)
1715 bail_syserr(errno, "failed to seek past bad sectors");
1716
b505435d
MW
1717 /* Remove our notice now that we're no longer messing about with bad
1718 * sectors, and reinstate the progress display.
1719 */
8cec8b64
MW
1720 progress_removeitem(&progress, &badblock_progress);
1721 progress_update(&progress);
1722}
1723
dc53ebfa 1724static secaddr run_length_wanted(secaddr pos, secaddr badlen, secaddr end)
b505435d
MW
1725 /* Return the number of good sectors that we want to see before
1726 * we're happy, given that we're about to try to read sector POS,
1727 * which is BADLEN sectors beyond where we found the first bad
1728 * sector, and the current region ends at sector END (i.e., this is
1729 * where the next event occurs).
1730 */
f0e437bc
MW
1731{
1732 secaddr want;
1733
b505435d 1734 /* Apply the factor to BADLEN to get an initial length. */
7c100803 1735 want = ceil(clear_factor*badlen);
b505435d
MW
1736
1737 /* Apply the user-configurable lower bound. */
dc53ebfa 1738 if (want < clear_min) want = clear_min;
b505435d
MW
1739
1740 /* Cap this with the end of the region. */
f0e437bc 1741 if (want > end - pos) want = end - pos;
b505435d
MW
1742
1743 /* And apply the user-configurable upper bound. */
dc53ebfa 1744 if (clear_max && want > clear_max) want = clear_max;
b505435d
MW
1745
1746 /* We're done. */
f0e437bc
MW
1747 return (want);
1748}
1749
dc53ebfa 1750static void report_bad_blocks_progress(secaddr bad_hi, int err)
b505435d
MW
1751 /* Report progress while we're trying to work past a region of bad
1752 * sectors. We're about to investigate BAD_HI, and the most recent
1753 * error was ERR.
1754 */
dc53ebfa
MW
1755 { bad_err = err; report_progress(bad_hi); }
1756
654a81af
MW
1757static ssize_t find_good_sector(secaddr *pos_inout, secaddr end,
1758 unsigned char *buf, secaddr sz)
b505435d
MW
1759 /* Work out a place to resume after finding a bad sector. The
1760 * current position, where we found a problem, is in *POS_INOUT. The
1761 * current input region goes up up sector END (i.e., this is where
1762 * the next event occurs). The caller's buffer is at BUF, and can
1763 * hold SZ sectors. On exit, update *POS_INOUT to be the start of a
1764 * region of /good/ sector that we decided was worth exploring, and
1765 * return the number of sectors we've already read at that position
1766 * and left at the start of the buffer. (This number may be zero,
1767 * depending on how things work out. That doesn't mean that we hit
1768 * end-of-file.)
1769 *
1770 * Altough the return value is `ssize_t', this is only to fit in with
1771 * other read functions; a negative return is not actually possible.
1772 */
654a81af 1773{
f0e437bc
MW
1774 secaddr pos = *pos_inout, bad_lo, bad_hi, good, step, want;
1775 struct recoverybuf r;
654a81af
MW
1776 ssize_t n;
1777
b505435d
MW
1778 /* Initial setup. Save the initial state and establish the bad-blocks
1779 * progress notice.
1780 */
dc53ebfa
MW
1781 bad_start = pos; bad_err = errno;
1782 badblock_progress.render = render_badblock_progress;
1783 progress_additem(&progress, &badblock_progress);
1784
b505435d
MW
1785 /* First, retry the `bad' sector a few times. Sometimes, with damaged
1786 * discs, this actually works. We'll try to read a full buffer, but we're
1787 * not expecting much.
1788 */
f0e437bc 1789 want = sz; if (want > end - pos) want = end - pos;
dc53ebfa 1790 for (retry = 0; retry < max_retries; retry++) {
b505435d
MW
1791
1792 /* Show the progress report. */
dc53ebfa 1793 report_bad_blocks_progress(pos, errno);
b505435d
MW
1794
1795 /* Try reading stuff. */
5d42ee1b 1796 n = read_sectors(pos, buf, want);
f0e437bc 1797#ifdef DEBUG
dc53ebfa 1798 progress_clear(&progress);
c0bff521 1799 printf(";; [retry] try reading %"PRIuSEC" .. %"PRIuSEC" -> %zd\n",
f0e437bc
MW
1800 pos, pos + want, n);
1801#endif
b505435d 1802
654a81af 1803 if (n > 0) {
b505435d
MW
1804 /* We won! Remove the progress display, and leave a permanent message
1805 * to inform the user what happened.
1806 */
dc53ebfa 1807 progress_clear(&progress);
654a81af 1808 moan("sector %"PRIuSEC" read ok after retry", pos);
dc53ebfa
MW
1809 progress_removeitem(&progress, &badblock_progress);
1810 progress_update(&progress);
2ac5af03 1811 return (n);
654a81af
MW
1812 }
1813 }
1814
b505435d 1815 /* We're going to have to be more creative. Set up the tracking state. */
5d42ee1b
MW
1816 r.buf = buf; r.sz = sz; r.pos = r.start = r.end = 0;
1817 r.good_lo = r.good_hi = 0;
1818
b505435d
MW
1819 /* Set up the region bound. We know the bad area starts at POS, and that
1820 * it covers at least one sector.
1821 */
f0e437bc 1822 bad_lo = pos; bad_hi = pos + 1;
b505435d
MW
1823
1824 /* Second major step: try to find somewhere on the other side of the bad
1825 * region.
1826 */
654a81af 1827 for (;;) {
ed689e74 1828#ifdef DEBUG
dc53ebfa 1829 progress_clear(&progress);
ed689e74
MW
1830 printf(";; bounding bad-block region: "
1831 "%"PRIuSEC" ..%"PRIuSEC".. %"PRIuSEC"\n",
1832 bad_lo, bad_hi - bad_lo, bad_hi);
1833#endif
b505435d
MW
1834
1835 /* If our upper bound has reached all the way to the end of the input
1836 * region then there's nowhere to recover to. Set the next position to
1837 * the end of the region and return.
1838 */
654a81af 1839 if (bad_hi >= end) {
dc53ebfa 1840 progress_clear(&progress);
654a81af 1841 moan("giving up on this extent");
dc53ebfa
MW
1842 recovered(bad_lo, end); *pos_inout = end;
1843 return (0);
654a81af 1844 }
b505435d
MW
1845
1846 /* Give a progress update. */
4239a143 1847 report_bad_blocks_progress(bad_hi, errno);
b505435d
MW
1848
1849 /* Choose a new place to look. Apply the step factor to the size of the
1850 * current gap between the start and end of the bad region, and then
1851 * bound by the user bounds and the input-region end.
1852 *
1853 * We make progress because `step' is at least 1: `step_min' is at least
1854 * 1, and bad_hi < end or we'd have already bailed.
1855 */
88693140 1856 step = (step_factor - 1)*(bad_hi - bad_lo);
dc53ebfa
MW
1857 if (step < step_min) step = step_min;
1858 if (step_max && step > step_max) step = step_max;
27c10313
MW
1859 step += bad_hi - bad_lo;
1860 if (step > end - bad_lo) step = end - bad_lo;
b505435d
MW
1861
1862 /* Now we look at the last sector of the new interval we've just marked
1863 * out.
1864 */
1865 pos = bad_lo + step - 1;
dc53ebfa 1866 want = run_length_wanted(pos, step, end);
f0e437bc
MW
1867 n = recovery_read(&r, pos, want);
1868#ifdef DEBUG
c0bff521 1869 printf(";; [bound] try reading %"PRIuSEC" .. %"PRIuSEC" -> %zd\n",
f0e437bc
MW
1870 pos, pos + want, n);
1871#endif
b505435d
MW
1872
1873 /* If everything went OK then we're done with this phase. */
f0e437bc 1874 if (n == want) break;
b505435d
MW
1875
1876 /* If it failed then extend the bad region to cover (the end of) the bad
1877 * sector which terminated the run, and go around again.
1878 */
f0e437bc
MW
1879 if (n < 0) n = 0;
1880 bad_hi = pos + n + 1;
654a81af
MW
1881 }
1882
b505435d
MW
1883 /* Third major step: identify exactly where the bad region ends. This is
1884 * a binary search.
1885 */
654a81af
MW
1886 good = pos;
1887 while (good > bad_hi) {
ed689e74 1888#ifdef DEBUG
dc53ebfa 1889 progress_clear(&progress);
ed689e74
MW
1890 printf(";; limiting bad-block region: "
1891 "%"PRIuSEC" ..%"PRIuSEC".. %"PRIuSEC" ..%"PRIuSEC".. %"PRIuSEC"\n",
1892 bad_lo, bad_hi - bad_lo, bad_hi, good - bad_hi, good);
1893#endif
b505435d
MW
1894
1895 /* Update the progress report. */
4239a143 1896 report_bad_blocks_progress(bad_hi, errno);
b505435d
MW
1897
1898 /* Pick a new place to try. */
1899 pos = bad_hi + (good - bad_hi)/2; step = pos - bad_lo;
dc53ebfa 1900 want = run_length_wanted(pos, step, end);
b505435d
MW
1901
1902 /* Try reading. */
f0e437bc
MW
1903 n = recovery_read(&r, pos, want);
1904#ifdef DEBUG
c0bff521 1905 printf(";; [limit] try reading %"PRIuSEC" .. %"PRIuSEC" -> %zd\n",
f0e437bc
MW
1906 pos, pos + want, n);
1907#endif
b505435d
MW
1908
1909 /* If that worked -- i.e., we got all the data we wanted -- then bring
1910 * down the `good' bound. If it failed, then bring up `bad_hi' to cover
1911 * the bad sector which terminated our read attempt.
1912 */
f0e437bc
MW
1913 if (n < 0) n = 0;
1914 if (n == want) good = pos;
1915 else bad_hi = pos + n + 1;
654a81af 1916 }
b505435d
MW
1917
1918 /* We're done. It's time to tidy up.
1919 *
1920 * One subtle point: it's possible that, as a result of retrying previous
1921 * bad blocks, that we ended up with bad_hi > good, so it's important that
1922 * we make a consistent choice between the two. I've gone with `good'
1923 * because (a) this gives us more of the original data from the disc and
1924 * (b) hopefully any marginal sectors are now in our buffer
1925 */
1315f9be 1926 recovered(bad_lo, good); *pos_inout = good;
b505435d
MW
1927
1928 /* Figure out how much data we can return to the caller from our buffer. */
1929 if (good < r.pos + r.start || r.pos + r.end <= good) {
1930 /* Our new position is outside of the region covered by the short-range
1931 * tracking, so there's nothing to return.
1932 */
1933
f0e437bc 1934 n = 0;
b505435d
MW
1935 } else {
1936 /* The new position is covered, so shuffle the data to the start of the
1937 * buffer and return as much as we can.
1938 */
1939
1315f9be
MW
1940 n = r.pos + r.end - good;
1941 rearrange_sectors(&r, 0, good - r.pos, n);
a8625592 1942 }
b505435d
MW
1943
1944 /* We're done. */
9dd5dba0
MW
1945#ifdef DEBUG
1946 show_recovery_buffer_map(&r, "returning %zd good sectors at %"PRIuSEC"",
1315f9be 1947 n, good);
9dd5dba0 1948#endif
654a81af
MW
1949 return (n);
1950}
1951
b505435d
MW
1952/*----- Copying data from a single input file -----------------------------*/
1953
9ac85bf5 1954static void emit(secaddr start, secaddr end)
b505435d
MW
1955 /* Copy sectors with absolute addresses from START (inclusive) to END
1956 * (exclusive) to the output. The entire input region comes from the
1957 * same source, already established as `file'.
1958 */
7fbe0fb9 1959{
b505435d 1960#define BUFSECTORS 512 /* this is a megabyte */
7fbe0fb9 1961
e98a0749 1962 int least;
7fbe0fb9 1963 unsigned char buf[BUFSECTORS*SECTORSZ];
788fe88a 1964 secaddr pos;
7fbe0fb9
MW
1965 size_t want;
1966 ssize_t n;
1967 static int first_time = 1;
1968#ifdef DEBUG
1969 struct file *f;
1970 char fn[MAXFNSZ];
1971 int act = -1;
e98a0749 1972 int i;
7fbe0fb9
MW
1973#endif
1974
b505435d
MW
1975 /* Choose an active file through which to read the source contents. We're
1976 * guaranteed that this file will do for the entire input region. We
1977 * choose the active file with the smallest index. The virtual `raw' file
1978 * which represents the underlying block device has the largest index, so
1979 * we'll always use a `.VOB' file if one is available. Looking at the
1980 * protocol suggests that the host and drive identify the per-title CSS key
1981 * by the start sector address of the `.VOB' file, so coincident files must
1982 * all use the same key. I've not encountered properly overlapping files
1983 * in the wild.
1984 */
7fbe0fb9 1985 least = least_live();
7fbe0fb9 1986#ifdef DEBUG
788fe88a 1987 printf(";; %8"PRIuSEC" .. %"PRIuSEC"\n", start, end);
7fbe0fb9
MW
1988 for (i = 0; i < filetab.n; i++) {
1989 if (!livep(i)) continue;
1990 if (act == -1) act = i;
1991 f = &filetab.v[i]; store_filename(fn, f->id);
788fe88a 1992 printf(";;\t\t%8"PRIuSEC" .. %-8"PRIuSEC" %s\n",
7fbe0fb9
MW
1993 start - f->start, end - f->start, fn);
1994 }
1995 if (act == -1) printf(";;\t\t#<no live source>\n");
1996 assert(act == least);
1997#endif
1998
b505435d
MW
1999 /* Set the global variables up for reading from the file we decided on.
2000 * These will be primarily used by `read_sectors' and `update_progress'.
2001 */
2002 if (least == -1) {
2003 /* There's nothing at all. This can happen because the kernel reported
2004 * the wrong block-device size for some reason but the filesystem has
2005 * identified files which start beyond the reported size, leaving a gap.
2006 */
2007
2008 file = 0; vob = 0;
2009 } else {
2010 /* There's a (possibly) virtual file. */
2011
5cc1b8be
MW
2012 file = &filetab.v[least];
2013 switch (id_kind(file->id)) {
b505435d 2014
7fbe0fb9 2015 case RAW:
b505435d
MW
2016 /* It's the raw device. Clear `vob' to prompt `read_sectors' to read
2017 * directly from `dvdfd'.
2018 */
2019
5cc1b8be 2020 vob = 0;
7fbe0fb9 2021 break;
b505435d 2022
7fbe0fb9 2023 case VOB:
b505435d
MW
2024 /* It's a `.VOB' file. We read these through `libdvdread', which
2025 * handles CSS unscrambling for us.
2026 */
2027
2028 /* The first time we open a `.VOB' file, `libdvdread' wants to spray
2029 * a bunch of information about how it's getting on cracking the
2030 * title keys. This will interfere with the progress display, so
2031 * preemptively hide the display.
2032 */
dc53ebfa 2033 if (first_time) { progress_clear(&progress); first_time = 0; }
b505435d
MW
2034
2035 /* Open the `.VOB' file. */
5cc1b8be
MW
2036 vob = DVDOpenFile(dvd, id_title(file->id),
2037 id_part(file->id)
2038 ? DVD_READ_TITLE_VOBS
2039 : DVD_READ_MENU_VOBS);
2040 if (!vob)
7fbe0fb9 2041 bail("failed to open %s %u",
5cc1b8be
MW
2042 id_part(file->id) ? "title" : "menu",
2043 id_title(file->id));
7fbe0fb9 2044 break;
b505435d 2045
7fbe0fb9 2046 default:
b505435d
MW
2047 /* Some other kind of thing; but there shouldn't be anything else in
2048 * the file table, so there's a bug.
2049 */
7fbe0fb9 2050 abort();
b505435d 2051
7fbe0fb9
MW
2052 }
2053 }
2054
b505435d
MW
2055 /* If we're not reading from the raw device then add an additional progress
2056 * bar for the current file. This isn't completely pointless: having a
2057 * ready visualization for whereabouts we are in a file is valuable when we
2058 * encounter bad blocks, because regions of intentional bad blocks near the
2059 * starts and and ends of VOBs are common on discs from annoying studios.
2060 */
dc53ebfa
MW
2061 if (file && id_kind(file->id) != RAW) {
2062 file_progress.render = render_file_progress;
2063 progress_additem(&progress, &file_progress);
2064 }
2065
b505435d
MW
2066 /* Put the progress display back, if we took it away, and show the file
2067 * progress bar if we added one.
2068 */
5ce0ca2a 2069 update_progress(start);
b505435d
MW
2070
2071 /* Read the input region and copy it to the disc. */
7fbe0fb9
MW
2072 pos = start;
2073 while (pos < end) {
b505435d
MW
2074
2075 /* Decide how much we want. Fill the buffer, unless there's not enough
2076 * input left.
2077 */
7fbe0fb9 2078 want = end - pos; if (want > BUFSECTORS) want = BUFSECTORS;
b505435d
MW
2079
2080 /* Try to read the input. */
5cc1b8be 2081 n = read_sectors(pos, buf, want);
7fbe0fb9 2082
b505435d
MW
2083 if (n <= 0) {
2084 /* It didn't work. Time to deploy the skipping-past-bad-blocks
2085 * machinery we worked so hard on. This will fill the buffer with
2086 * stuff and return a new count of how much it read.
2087 */
2088
2089 n = find_good_sector(&pos, end, buf, BUFSECTORS);
2090 }
2091 if (n > 0) {
2092 /* We made some progress. Write the stuff that we read to the output
2093 * file and update the position.
2094 */
2095
2096 carefully_write(outfd, buf, n*SECTORSZ); pos += n;
2097 }
2098
2099 /* Report our new progress. */
dc53ebfa 2100 report_progress(pos);
7fbe0fb9
MW
2101 }
2102
b505435d 2103 /* Close the `libdvdread' file, if we opened one. */
5cc1b8be 2104 if (vob) { DVDCloseFile(vob); vob = 0; }
7fbe0fb9 2105
b505435d 2106 /* If we added a per-file progress bar, then take it away again. */
dc53ebfa
MW
2107 if (file && id_kind(file->id) != RAW)
2108 progress_removeitem(&progress, &file_progress);
b505435d
MW
2109
2110 /* Update the progress display to report our glorious success. */
dc53ebfa 2111 progress_update(&progress);
7fbe0fb9 2112
dc53ebfa 2113#undef BUFSECTORS
7fbe0fb9 2114}
7fbe0fb9 2115
b505435d
MW
2116/*----- Main program ------------------------------------------------------*/
2117
7fbe0fb9
MW
2118int main(int argc, char *argv[])
2119{
2120 unsigned f = 0;
dc53ebfa 2121 const char *p;
4bd4876f 2122 off_t volsz;
788fe88a 2123 secaddr pos;
7fbe0fb9 2124 off_t off;
d79c7c9a 2125 secaddr start, end, last;
7fbe0fb9 2126 const struct event *ev;
69f3ec37 2127 const char *device, *outfile;
513eba44 2128 struct badblock *bad;
9ac85bf5 2129 int opt, blksz;
7fbe0fb9
MW
2130 size_t i;
2131 FILE *fp;
2132 struct buf buf = BUF_INIT;
dc53ebfa
MW
2133 struct timeval tv0, tv1;
2134 double t, rate, tot;
2135 const char *rateunit, *totunit;
d6845ac3
MW
2136 char timebuf[TIMESTRMAX], id_in[MAXIDSZ], id_out[MAXIDSZ];
2137 dvd_reader_t *dvd_out;
7fbe0fb9
MW
2138#ifdef DEBUG
2139 const struct file *file;
2140 char fn[MAXFNSZ];
2141#endif
2142
2143#define f_bogus 1u
2144#define f_continue 2u
2145#define f_fixup 4u
dc53ebfa 2146#define f_stats 8u
d6845ac3 2147#define f_checkid 16u
1315a4f7 2148#define f_retry 32u
7fbe0fb9 2149#define f_write 256u
4d69e943 2150#define f_file 512u
7fbe0fb9 2151
dc53ebfa 2152 set_prog(argv[0]);
b505435d
MW
2153
2154 /* First up, handle the command-line options. */
7fbe0fb9 2155 for (;;) {
45b498cf 2156 opt = getopt(argc, argv, "hB:E:FP:R:X:b:cir:s"); if (opt < 0) break;
7fbe0fb9 2157 switch (opt) {
b505435d
MW
2158
2159 /* `-h': Help. */
7fbe0fb9 2160 case 'h': usage(stderr); exit(0);
b505435d
MW
2161
2162 /* `-B PARAM=VALUE[,...]': Setting internal parameters. */
dc53ebfa 2163 case 'B':
b505435d
MW
2164
2165 /* Set up a cursor into the parameter string. */
dc53ebfa 2166 p = optarg;
b505435d 2167
dc53ebfa
MW
2168#define SKIP_PREFIX(s) \
2169 (STRNCMP(p, ==, s "=", sizeof(s)) && (p += sizeof(s), 1))
b505435d
MW
2170 /* If the text at P matches `S=' then advance P past that and
2171 * evaluate nonzero; otherwise evaluate zero.
2172 */
2173
dc53ebfa 2174 for (;;) {
b505435d 2175
dc53ebfa 2176 if (SKIP_PREFIX("cf"))
f82e4cd7
MW
2177 clear_factor = parse_float(&p, PNF_JUNK, 0, DBL_MAX,
2178 "clear factor");
b505435d 2179
dc53ebfa 2180 else if (SKIP_PREFIX("cmin"))
f82e4cd7
MW
2181 clear_min = parse_int(&p, PNF_JUNK, 1, SECLIMIT,
2182 "clear minimum");
b505435d 2183
dc53ebfa 2184 else if (SKIP_PREFIX("cmax"))
f82e4cd7
MW
2185 clear_max = parse_int(&p, PNF_JUNK, 1, SECLIMIT,
2186 "clear maximum");
b505435d 2187
dc53ebfa 2188 else if (SKIP_PREFIX("sf"))
f82e4cd7
MW
2189 step_factor = parse_float(&p, PNF_JUNK, 0, DBL_MAX,
2190 "step factor");
b505435d 2191
dc53ebfa 2192 else if (SKIP_PREFIX("smin"))
f82e4cd7
MW
2193 step_min = parse_int(&p, PNF_JUNK, 1, SECLIMIT - 1,
2194 "step minimum");
b505435d 2195
dc53ebfa 2196 else if (SKIP_PREFIX("smax"))
f82e4cd7
MW
2197 step_max = parse_int(&p, PNF_JUNK, 1, SECLIMIT - 1,
2198 "step maximum");
b505435d 2199
dc53ebfa 2200 else if (SKIP_PREFIX("retry"))
f82e4cd7 2201 max_retries = parse_int(&p, PNF_JUNK, 0, INT_MAX, "retries");
b505435d 2202
c1d37fc8 2203 else if (SKIP_PREFIX("alpha"))
f82e4cd7 2204 alpha = parse_float(&p, PNF_JUNK, 0, 1, "average decay factor");
b505435d 2205
dc53ebfa 2206 else if (SKIP_PREFIX("_badwait"))
f82e4cd7
MW
2207 bad_block_delay = parse_float(&p, PNF_JUNK, 0, DBL_MAX,
2208 "bad-block delay");
b505435d 2209
dc53ebfa 2210 else if (SKIP_PREFIX("_blkwait"))
f82e4cd7
MW
2211 good_block_delay = parse_float(&p, PNF_JUNK, 0, DBL_MAX,
2212 "good block delay");
b505435d 2213
dc53ebfa
MW
2214 else
2215 bail("unknown bad blocks parameter `%s'", p);
b505435d
MW
2216
2217 /* If we're now at the end of the string then we're done. */
dc53ebfa 2218 if (!*p) break;
b505435d
MW
2219
2220 /* We're not done yet, so there should now be a comma and another
2221 * parameter setting.
2222 */
ffe6038c 2223 if (*p != ',') bail("unexpected junk in parameters");
dc53ebfa
MW
2224 p++;
2225 }
b505435d 2226
dc53ebfa
MW
2227#undef SKIP_PREFIX
2228 break;
b505435d
MW
2229
2230 /* `-E FILE' (undocumented): Log the bad sectors we encountered to
2231 * FILE.
2232 */
af3973a1 2233 case 'E': errfile = optarg; break;
b505435d
MW
2234
2235 /* `-F' (undocumented): Hack for fixing up images that were broken by
2236 * an old early-stop bug.
2237 */
7fbe0fb9 2238 case 'F': f |= f_fixup; break;
b505435d 2239
45b498cf
MW
2240 /* `-P FILE' (undocumented): trace progress state to FILE. */
2241 case 'P':
2242 if (progressfp) bail("progress trace file already set");
2243 progressfp = fopen(optarg, "w");
2244 if (!progressfp)
2245 bail_syserr(errno, "failed to open progress trace file `%s'",
2246 optarg);
2247 break;
2248
b505435d
MW
2249 /* `-R FILE': Read ranges to retry from FILE. Retry ranges are
2250 * converted into `EV_WRITE' and `EV_STOP' events.
2251 */
7fbe0fb9
MW
2252 case 'R':
2253 fp = fopen(optarg, "r");
2254 if (!fp)
2255 bail_syserr(errno, "failed to open ranges file `%s'", optarg);
b505435d
MW
2256
2257 /* We're going to try to coalesce adjacent ranges from the file.
2258 * When we found a region to skip, we'd have stopped at the a file
2259 * boundary, and possibly restarted again immediately afterwards,
2260 * resulting in two adjacent regions in the file. To do that, and
2261 * also to police the restriction that ranges occur in ascending
2262 * order, we keep track of the upper bound for the most recent range
2263 * -- but there isn't one yet, so we use a sentinel value.
2264 */
d79c7c9a 2265 i = 0; last = -1;
7fbe0fb9 2266 for (;;) {
b505435d
MW
2267
2268 /* Read a line from the buffer. If there's nothing left then we're
2269 * done.
2270 */
c62cd11a 2271 buf_rewind(&buf); if (read_line(fp, &buf)) break;
b505435d
MW
2272
2273 /* Increment the line counter and establish a cursor. */
6f82ecff 2274 i++; p = buf.p;
b505435d
MW
2275
2276 /* Skip initial whitespace. */
7fbe0fb9 2277 while (ISSPACE(*p)) p++;
b505435d
MW
2278
2279 /* If this is a comment then ignore it and go round again. */
7fbe0fb9 2280 if (!*p || *p == '#') continue;
b505435d
MW
2281
2282 /* Parse the range. Check that the ranges are coming out in
2283 * ascending order.
2284 */
87a145d2
MW
2285 if (parse_range(p, 0, &start, &end) ||
2286 (last <= SECLIMIT && start < last))
2287 bail("bad range `%s' at `%s' line %zu", buf.p, optarg, i);
b505435d
MW
2288
2289 /* Ignore empty ranges: this is important (see below where we sort
2290 * the event queue). If this abuts the previous range then just
2291 * overwrite the previous end position. Otherwise, write a new
2292 * pair of events.
2293 */
7fbe0fb9 2294 if (start < end) {
d79c7c9a
MW
2295 if (start == last)
2296 eventq.v[eventq.n - 1].pos = end;
2297 else {
2298 put_event(EV_WRITE, 0, start);
2299 put_event(EV_STOP, 0, end);
2300 }
2301 last = end;
7fbe0fb9
MW
2302 }
2303 }
b505435d
MW
2304
2305 /* Check for read errors. */
7fbe0fb9
MW
2306 if (ferror(fp))
2307 bail_syserr(errno, "failed to read ranges file `%s'", optarg);
1315a4f7 2308 f |= f_retry;
7fbe0fb9 2309 break;
b505435d
MW
2310
2311 /* `-X FILE' (undocumented): Read ranges of bad-blocks from FILE to
2312 * establish fake bad blocks: see `read_sectors' above for the details.
2313 *
2314 * This is very similar to the `-R' option above, except that it
2315 * doesn't do the range coalescing thing.
2316 */
513eba44
MW
2317 case 'X':
2318 fp = fopen(optarg, "r");
2319 if (!fp)
2320 bail_syserr(errno, "failed to open bad-blocks file `%s'", optarg);
2321 i = 0; last = -1;
2322 for (;;) {
c62cd11a 2323 buf_rewind(&buf); if (read_line(fp, &buf)) break;
513eba44
MW
2324 p = buf.p; i++;
2325 while (ISSPACE(*p)) p++;
2326 if (!*p || *p == '#') continue;
2327 if (parse_range(p, 0, &start, &end) ||
2328 (last <= SECLIMIT && start < last))
2329 bail("bad range `%s' at `%s' line %zu", buf.p, optarg, i);
357a3945
MW
2330 if (start < end) {
2331 VEC_PUSH(bad, &badblocks);
2332 bad->start = start; bad->end = end;
2333 }
513eba44
MW
2334 }
2335 if (ferror(fp))
2336 bail_syserr(errno, "failed to read bad-blocks file `%s'", optarg);
2337 break;
b505435d
MW
2338
2339 /* Log regions skipped because of bad blocks to a file. */
7fbe0fb9 2340 case 'b':
5cc1b8be
MW
2341 if (mapfile) bail("can't have multiple map files");
2342 mapfile = optarg;
7fbe0fb9 2343 break;
b505435d
MW
2344
2345 /* `-c': Continue copying where we left off last time. */
7fbe0fb9 2346 case 'c': f |= f_continue; break;
b505435d
MW
2347
2348 /* `-i': Check that we're copying from the right disc. */
040a38df 2349 case 'i': f |= f_checkid; break;
b505435d
MW
2350
2351 /* `-r [START]-[END]': Manually provide a range of sectors to retry. */
7fbe0fb9 2352 case 'r':
1315a4f7 2353 start = 0; end = -1; f |= f_retry;
87a145d2
MW
2354 if (parse_range(optarg, PRF_HYPHEN, &start, &end))
2355 bail("bad range `%s'", optarg);
2356 if (start < end) {
b505435d 2357 /* Again, ignore empty ranges. */
7fbe0fb9 2358 put_event(EV_WRITE, 0, start);
87a145d2 2359 if (end <= SECLIMIT) put_event(EV_STOP, 0, end);
7fbe0fb9 2360 }
7fbe0fb9 2361 break;
b505435d
MW
2362
2363 /* `-s': Print statistics at the end. */
dc53ebfa 2364 case 's': f |= f_stats; break;
b505435d
MW
2365
2366 /* Anything else is an error. */
7fbe0fb9
MW
2367 default: f |= f_bogus; break;
2368 }
2369 }
b505435d
MW
2370
2371 /* We expect two arguments. Check this. Complain about bad usage if we
2372 * have bad arguments or options.
2373 */
69f3ec37 2374 if (argc - optind != 2) f |= f_bogus;
7fbe0fb9 2375 if (f&f_bogus) { usage(stderr); exit(2); }
69f3ec37
MW
2376 device = argv[optind]; outfile = argv[optind + 1];
2377
b505435d
MW
2378 /* If there are fake bad blocks (the `-X' option) then sort the list
2379 * because `read_sectors' wants to use a binary search.
2380 */
513eba44
MW
2381 if (badblocks.n) {
2382 qsort(badblocks.v, badblocks.n, sizeof(struct badblock),
2383 compare_badblock);
2384#ifdef DEBUG
2385 printf(";; fake bad blocks:\n");
2386 for (i = 0; i < badblocks.n; i++)
2387 printf(";;\t%8"PRIuSEC" .. %"PRIuSEC"\n",
2388 badblocks.v[i].start, badblocks.v[i].end);
2389#endif
2390 }
2391
b505435d 2392 /* Prepare to display progress information. */
64229dd6
MW
2393 setlocale(LC_ALL, "");
2394 progress_init(&progress);
b505435d
MW
2395
2396 /* Open the input device. (This may pop up a notice if there's nothing in
2397 * the drive.)
2398 */
d23998cb 2399 if (open_dvd(device, O_RDONLY, &dvdfd, &dvd)) exit(2);
7fbe0fb9 2400
b505435d 2401 /* Determine the size of the input device and check the sector size. */
4d69e943
MW
2402 blksz = -1; volsz = device_size(dvdfd, device, &blksz);
2403 if (blksz == -1)
2404 { blksz = SECTORSZ; f |= f_file; }
2405 else if (blksz != SECTORSZ)
7fbe0fb9
MW
2406 bail("device `%s' block size %d /= %d", device, blksz, SECTORSZ);
2407 if (volsz%SECTORSZ)
2408 bail("device `%s' volume size %"PRIu64" not a multiple of %d",
2409 device, volsz, SECTORSZ);
2410
4d69e943
MW
2411 setup_geometry(&geom, dvdfd, f&f_file ? 0 : GF_BLKDEV, volsz/blksz);
2412
2413 if (progressfp) {
2414 switch (geom.shape) {
2415 case FLAT:
2416 fprintf(progressfp, ":model flat-model\n");
2417 break;
2418 case SINGLE:
2419 fprintf(progressfp, ":model single-layer-model :start %"PRIuSEC"\n",
2420 geom.start0);
2421 break;
2422 case PTP:
2423 fprintf(progressfp,
2424 ":model parallel-track-path-model "
2425 ":start0 %"PRIuSEC" :start1 %"PRIuSEC" "
2426 ":midpoint %"PRIuSEC"\n",
2427 geom.start0, geom.start1, geom.midpoint);
2428 break;
2429 case OTP:
2430 fprintf(progressfp,
2431 ":model opposite-track-path-model "
2432 ":start %"PRIuSEC" :midpoint %"PRIuSEC"\n",
2433 geom.start0, geom.midpoint);
2434 break;
2435 default:
2436 abort();
2437 }
2438 }
2439
b505435d
MW
2440 /* Maybe check that we're copying from the right disc. This is intended to
2441 * help avoid image corruption by from the wrong disc, but it obviously
2442 * only works if the output file is mostly there.
2443 */
d6845ac3 2444 if (f&f_checkid) {
d23998cb 2445 if (open_dvd(outfile, O_RDONLY, 0, &dvd_out)) exit(2);
d6845ac3
MW
2446 if (dvd_id(id_in, dvd, DIF_MUSTIFOHASH, device) ||
2447 dvd_id(id_out, dvd_out, DIF_MUSTIFOHASH, device))
2448 exit(2);
2449 if (STRCMP(id_in, !=, id_out))
2450 bail("DVD id mismatch: input `%s' is `%s'; output `%s' is `%s'",
2451 device, id_in, outfile, id_out);
2452 }
2453
b505435d 2454 /* Open the output file. */
dc53ebfa
MW
2455 outfd = open(outfile, O_WRONLY | O_CREAT, 0666);
2456 if (outfd < 0)
2457 bail_syserr(errno, "failed to create output file `%s'", outfile);
7fbe0fb9
MW
2458
2459 if (f&f_continue) {
b505435d
MW
2460 /* If we're continuing from where we left off, then find out where that
2461 * was and make a note to copy from there to the end of the disc. Note
2462 * that we're not relying on this position: in particular, it might not
2463 * even be sector-aligned (in which case we'll ignore the final partial
2464 * sector). We'll seek to the right place again when we start writing.
2465 */
2466
7fbe0fb9
MW
2467 off = lseek(outfd, 0, SEEK_END);
2468 if (off < 0)
2469 bail_syserr(errno, "failed to seek to end of output file `%s'",
2470 outfile);
1315a4f7
MW
2471 put_event(EV_WRITE, 0, off/SECTORSZ); f |= f_retry;
2472 }
b505435d
MW
2473
2474 if (!(f&(f_retry | f_fixup))) {
2475 /* If there are no ranges to retry and we're not fixing an ancient early-
2476 * stop bug, then there's no range to retry and we should just copy
2477 * everything.
2478 */
2479
7fbe0fb9 2480 put_event(EV_WRITE, 0, 0);
b505435d 2481 }
7fbe0fb9 2482
b505435d
MW
2483 /* Now it's time to figure out what the input looks like. Work through the
2484 * titlesets in order, mapping out where the video-object files are. We
2485 * could figure out how many there are properly, but it's fast enough just
2486 * to try everything. That's the menu only for the special titleset 0, and
2487 * menu and titles for the remaining titlesets 1 up to 99.
2488 */
5cc1b8be 2489 put_menu(dvd, 0);
7fbe0fb9 2490 for (i = 1; i < 100; i++) {
5cc1b8be
MW
2491 put_menu(dvd, i);
2492 put_title(dvd, i);
7fbe0fb9 2493 }
b505435d
MW
2494
2495 /* Make a final virtual file for the raw device. (See `emit', which
2496 * assumes that this is the last entry in the file table.) Check that we
2497 * don't have more files than we expect, because the bitmap table has fixed
2498 * size.
2499 */
7fbe0fb9
MW
2500 put_file(mkident(RAW, 0, 0), 0, volsz/SECTORSZ);
2501 assert(filetab.n <= MAXFILES);
2502
b505435d
MW
2503 /* Find an upper limit for what we're supposed to copy. Since the `RAW'
2504 * entry covers the reported size of the input device, this ought to cover
2505 * all of our bases.
2506 */
5cc1b8be
MW
2507 for (i = 0, limit = 0; i < filetab.n; i++)
2508 if (filetab.v[i].end > limit) limit = filetab.v[i].end;
7fbe0fb9
MW
2509#ifdef DEBUG
2510 printf("\n;; files:\n");
2511 for (i = 0; i < filetab.n; i++) {
2512 file = &filetab.v[i];
2513 store_filename(fn, file->id);
50c72655
MW
2514 printf(";;\t%8"PRIuSEC" .. %-8"PRIuSEC" %s\n",
2515 file->start, file->end, fn);
7fbe0fb9
MW
2516 }
2517#endif
2518
b505435d
MW
2519 /* Sort the event list.
2520 *
2521 * The event-code ordering is important here.
2522 *
2523 * * `EV_STOP' sorts /before/ `EV_WRITE'. If we have two abutting ranges
2524 * to retry, then we should stop at the end of the first, and then
2525 * immediately start again. If empty ranges were permitted then we'd
2526 * stop writing and /then/ start, continuing forever, which is clearly
2527 * wrong.
2528 *
2529 * * `EV_BEGIN' sorts before `EV_END'. If we have empty files then we
2530 * should set the bit that indicates that it's started, and then clear
2531 * it, in that order. If we have abutting files, then we'll just both
2532 * bits for an instant, but that's not a problem.
2533 */
7fbe0fb9
MW
2534 qsort(eventq.v, eventq.n, sizeof(struct event), compare_event);
2535
b505435d
MW
2536 /* Check that the event list is well-formed. We start out at the
2537 * beginning, not writing anything.
2538 */
27e60e7a 2539 for (i = 0, f &= ~f_write, start = 0; i < eventq.n; i++) {
7fbe0fb9
MW
2540 ev = &eventq.v[i];
2541 switch (ev->ev) {
b505435d 2542
7fbe0fb9 2543 case EV_WRITE:
b505435d
MW
2544 /* Start writing. We shouldn't be writing yet! */
2545
7fbe0fb9 2546 if (f&f_write)
50232384
MW
2547 bail("overlapping ranges: range from %"PRIuSEC" "
2548 "still open at %"PRIuSEC"",
7fbe0fb9 2549 start, ev->pos);
7763378f 2550 f |= f_write; start = ev->pos;
7fbe0fb9 2551 break;
b505435d 2552
7fbe0fb9 2553 case EV_STOP:
b505435d
MW
2554 /* Stop writing. Make a note that we've done this. */
2555
7fbe0fb9
MW
2556 f &= ~f_write;
2557 break;
2558 }
2559 }
d366dab7
MW
2560#ifdef DEBUG
2561 dump_eventq("initial");
2562#endif
b505435d
MW
2563
2564 /* Now we make a second pass over the event queue to fix it up. Also
2565 * count up how much work we'll be doing so that we can report progress.
2566 */
0a9199a1 2567 for (i = 0, f &= ~f_write, start = last = 0; i < eventq.n; i++) {
7fbe0fb9 2568 ev = &eventq.v[i];
b505435d
MW
2569
2570 /* If we're supposed to start writing then make a note of the start
2571 * position. We'll want this to count up how much work we're doing. The
2572 * start position of the final range is also used by the logic below that
2573 * determines the progress display.
2574 */
41034102 2575 if (ev->ev == EV_WRITE) { start = ev->pos; f |= f_write; }
b505435d
MW
2576
2577 /* If this event position is past our final limit then stop. Nothing
2578 * beyond here can possibly be interesting. (Since `EV_WRITE' sorts
2579 * before other events, we will notice an `EV_WRITE' exactly at the limit
2580 * sector, but not any other kind of event.)
2581 */
5cc1b8be 2582 if (ev->pos >= limit) break;
b505435d
MW
2583
2584 /* If we're supposed to stop writing here, then add the size of the
2585 * most recent range onto our running total.
2586 */
4d69e943
MW
2587 if (ev->ev == EV_STOP) {
2588 nsectors += ev->pos - start;
2589 total_linear += linear_progress(&geom, start, ev->pos);
2590 f &= ~f_write;
2591 }
b505435d
MW
2592
2593 /* If we're fixing up images affected by the old early-stop bug, then
2594 * remember this position.
2595 */
0a9199a1 2596 if (f&f_fixup) last = ev->pos;
7fbe0fb9 2597 }
b505435d
MW
2598
2599 /* Truncate the event queue at the point we reached the sector limit. */
7fbe0fb9 2600 eventq.n = i;
d366dab7
MW
2601#ifdef DEBUG
2602 dump_eventq("trimmed");
2603#endif
b505435d
MW
2604
2605 /* Finally, the early-stop bug fix.
2606 *
2607 * The bug was caused by a broken version of the event-queue truncation
2608 * logic: it trimmed the event queue, but didn't add a final event at the
2609 * file limit. The effect was that the interval between the last event --
2610 * likely `EV_END' for a VOB file -- and the overall end of the disc didn't
2611 * get copied. We address this by starting to write at the position of
2612 * this last event.
2613 */
7fbe0fb9 2614 if (f&f_fixup) {
0a9199a1 2615 put_event(EV_WRITE, 0, last);
7763378f 2616 f |= f_write;
7fbe0fb9 2617 }
b505435d
MW
2618
2619 /* If we're still writing then avoid the early-end bug by adding an
2620 * `EV_STOP' event at the limit position. Include this range in the sector
2621 * count.
2622 */
7fbe0fb9 2623 if (f&f_write) {
5cc1b8be 2624 nsectors += limit - start;
4d69e943 2625 total_linear += linear_progress(&geom, start, limit);
5cc1b8be 2626 put_event(EV_STOP, 0, limit);
7fbe0fb9 2627 }
d366dab7
MW
2628#ifdef DEBUG
2629 dump_eventq("final");
2630#endif
dc53ebfa 2631
b505435d
MW
2632 /* Set up the main progress display.
2633 *
2634 * If we're copying a single region from somewhere to the end of the disc
2635 * then it seems more sensible to use a single progress bar for both. If
2636 * we're reading multiple ranges, maybe because we're retrying bad blocks,
2637 * then it's better to have separate bars for how much actual copying we've
2638 * done, and which part of the disc we're currently working on.
2639 */
dc53ebfa
MW
2640 copy_progress.render = render_copy_progress;
2641 progress_additem(&progress, &copy_progress);
4d69e943
MW
2642 if (nsectors == limit - start) {
2643 ndone = start; nsectors = limit;
2644 done_linear = linear_progress(&geom, 0, start);
2645 total_linear += done_linear;
2646 }
e0c3a82d 2647 else {
dc53ebfa
MW
2648 disc_progress.render = render_disc_progress;
2649 progress_additem(&progress, &disc_progress);
2650 }
2651
b505435d
MW
2652 /* If we're producing overall statistics then make a note of the current
2653 * time.
2654 */
dc53ebfa 2655 if (f&f_stats) gettimeofday(&tv0, 0);
7fbe0fb9 2656
b505435d 2657 /* We're now ready to start our sweep through the disc. */
7fbe0fb9
MW
2658#ifdef DEBUG
2659 printf("\n;; event sweep:\n");
2660#endif
b505435d
MW
2661
2662 /* We start at the beginning of the disc, and the start of the event queue,
2663 * not writing. We'll advance through the events one by one.
2664 */
27e60e7a 2665 for (pos = 0, i = 0, f &= ~f_write; i < eventq.n; i++) {
b505435d
MW
2666
2667 /* Get the next event. */
7fbe0fb9 2668 ev = &eventq.v[i];
b505435d
MW
2669
2670 /* If there's a nonempty range between here and the previous event then
2671 * we need to process this.
2672 */
7fbe0fb9 2673 if (ev->pos > pos) {
b505435d
MW
2674
2675 /* If we're writing then copy the interval from the previous event to
2676 * here to the output.
2677 */
9ac85bf5 2678 if (f&f_write) emit(pos, ev->pos);
b505435d
MW
2679
2680 /* Advance the current position now that the output is up-to-date. */
7fbe0fb9 2681 pos = ev->pos;
b505435d 2682
7fbe0fb9 2683#ifdef DEBUG
dc53ebfa 2684 progress_clear(&progress);
7fbe0fb9
MW
2685 printf(";;\n");
2686#endif
2687 }
b505435d
MW
2688
2689 /* Decide what to action to take in response to the event. */
7fbe0fb9 2690 switch (ev->ev) {
b505435d 2691
7fbe0fb9 2692 case EV_BEGIN:
b505435d
MW
2693 /* A file has started. Set the appropriate bit in the active-files
2694 * map.
2695 */
7fbe0fb9
MW
2696 set_live(ev->file);
2697#ifdef DEBUG
2698 store_filename(fn, filetab.v[ev->file].id);
dc53ebfa 2699 progress_clear(&progress);
788fe88a 2700 printf(";; %8"PRIuSEC": begin `%s'\n", pos, fn);
7fbe0fb9
MW
2701#endif
2702 break;
b505435d 2703
7fbe0fb9 2704 case EV_WRITE:
b505435d
MW
2705 /* We're supposed to start writing. */
2706
2707 /* Note the current time and position for the progress display. */
5cc1b8be 2708 gettimeofday(&last_time, 0); last_pos = pos;
b505435d
MW
2709
2710 /* Seek to the right place in the output file. */
69f3ec37 2711 if (lseek(outfd, (off_t)ev->pos*SECTORSZ, SEEK_SET) < 0)
7fbe0fb9
MW
2712 bail_syserr(errno,
2713 "failed to seek to resume position "
788fe88a 2714 "(sector %"PRIuSEC") in output file `%s'",
7fbe0fb9 2715 ev->pos, outfile);
b505435d
MW
2716
2717 /* Engage the write head. */
812ff552 2718 f |= f_write;
b505435d 2719
7fbe0fb9 2720#ifdef DEBUG
dc53ebfa 2721 progress_clear(&progress);
788fe88a 2722 printf(";; %8"PRIuSEC": begin write\n", pos);
7fbe0fb9 2723#endif
7fbe0fb9 2724 break;
b505435d 2725
7fbe0fb9 2726 case EV_STOP:
b505435d
MW
2727 /* We're supposed to stop writing. Disengage the write head. */
2728
7fbe0fb9
MW
2729 f &= ~f_write;
2730#ifdef DEBUG
dc53ebfa 2731 progress_clear(&progress);
788fe88a 2732 printf(";; %8"PRIuSEC": end write\n", pos);
7fbe0fb9
MW
2733#endif
2734 break;
b505435d 2735
7fbe0fb9 2736 case EV_END:
b505435d
MW
2737 /* We've found the end of a file. Clear its bit in the table. */
2738
7fbe0fb9
MW
2739 clear_live(ev->file);
2740#ifdef DEBUG
2741 store_filename(fn, filetab.v[ev->file].id);
dc53ebfa 2742 progress_clear(&progress);
788fe88a 2743 printf(";; %8"PRIuSEC": end `%s'\n", pos, fn);
7fbe0fb9
MW
2744#endif
2745 break;
b505435d
MW
2746
2747 /* Something else. Clearly a bug. */
7fbe0fb9
MW
2748 default: abort();
2749 }
2750 }
2751
b505435d 2752 /* Take down the progress display because we're done. */
dc53ebfa 2753 progress_clear(&progress);
7fbe0fb9 2754
b505435d 2755 /* Set the output file length correctly. */
69f3ec37 2756 if (ftruncate(outfd, (off_t)limit*SECTORSZ) < 0)
7fbe0fb9
MW
2757 bail_syserr(errno, "failed to set output file `%s' length", outfile);
2758
b505435d 2759 /* Report overall statistics. */
dc53ebfa
MW
2760 if (f&f_stats) {
2761 gettimeofday(&tv1, 0); t = tvdiff(&tv0, &tv1);
627fa6be 2762 if (nsectors == limit) { ndone -= start; nsectors -= start; }
dc53ebfa
MW
2763 tot = scale_bytes((double)nsectors*SECTORSZ, &totunit);
2764 rate = scale_bytes((double)nsectors*SECTORSZ/t, &rateunit);
2765 moan("all done: %.1f %sB in %s -- %.1f %sB/s",
2766 tot, totunit, fmttime(t, timebuf), rate, rateunit);
2767 }
2768
b505435d 2769 /* Close files. */
5cc1b8be
MW
2770 if (dvd) DVDClose(dvd);
2771 if (dvdfd >= 0) close(dvdfd);
7fbe0fb9 2772 if (outfd >= 0) close(outfd);
813c2ce8 2773 carefully_fclose(mapfp, "bad-sector region map");
af3973a1 2774 carefully_fclose(errfp, "bad-sector error log");
45b498cf 2775 carefully_fclose(progressfp, "progress trace file");
dc53ebfa 2776 progress_free(&progress);
7fbe0fb9 2777
b505435d 2778 /* We're done! */
760e321c
MW
2779 return (0);
2780
7fbe0fb9
MW
2781#undef f_bogus
2782#undef f_continue
2783#undef f_fixup
dc53ebfa 2784#undef f_stats
7fbe0fb9 2785#undef f_write
7fbe0fb9 2786}
b505435d
MW
2787
2788/*----- That's all, folks -------------------------------------------------*/