#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
+#include <sys/stat.h>
#include <sys/time.h>
#include <getopt.h>
#include <dvdread/ifo_read.h>
#include <dvdread/ifo_types.h>
-#define SECTORSZ 2048
-#define SECTORS(n) (((n) + (SECTORSZ - 1))/SECTORSZ)
-
#define CTYPE_HACK(fn, ch) fn((unsigned char)(ch))
#define ISDIGIT(ch) CTYPE_HACK(isdigit, ch)
#define ISSPACE(ch) CTYPE_HACK(isspace, ch)
#define N(v) (sizeof(v)/sizeof((v)[0]))
-enum { RAW, IFO, VOB, BUP };
-typedef uint_least32_t ident;
-
-static inline ident mkident(unsigned kind, unsigned title, unsigned part)
- { return (((ident)kind << 0) | ((ident)title << 8) | ((ident)part << 16)); }
-static inline unsigned id_kind(ident id) { return ((id >> 0)&0x0ff); }
-static inline unsigned id_title(ident id) { return ((id >> 8)&0x0ff); }
-static inline unsigned id_part(ident id) { return ((id >> 16)&0x0ff); }
+#define SECTORSZ 2048
+#define SECTORS(n) (((n) + (SECTORSZ - 1))/SECTORSZ)
static const char *prog = "<unset>";
static int status = 0;
static void vmoan(const char *fmt, va_list ap)
{ fprintf(stderr, "%s: ", prog); vfprintf(stderr, fmt, ap); }
+
+__attribute__((format(printf, 1, 2)))
+static void moan(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt); vmoan(fmt, ap); va_end(ap);
+ fputc('\n', stderr);
+}
+
__attribute__((noreturn, format(printf, 1, 2)))
static void bail(const char *fmt, ...)
{
fputc('\n', stderr);
exit(2);
}
+
__attribute__((noreturn, format(printf, 2, 3)))
static void bail_syserr(int err, const char *fmt, ...)
{
exit(2);
}
+#define DEFVEC(vtype, etype) \
+ typedef struct { etype *v; size_t n, sz; } vtype
+#define VEC_INIT { 0, 0, 0 }
+#define VEC_FREE(vv) do { \
+ free((vv)->v); (vv)->v 0; (vv)->n = (vv)->sz = 0; \
+} while (0)
+#define VEC_PUSH(p, vv) do { \
+ size_t _want; \
+ if ((vv)->n >= (vv)->sz) { \
+ (vv)->sz = (vv)->sz ? 2*(vv)->sz : 32; \
+ _want = (vv)->sz*sizeof(*(vv)->v); \
+ (vv)->v = realloc((vv)->v, _want); \
+ if (!(vv)->v) bail("out of memory allocating %zu bytes", _want); \
+ } \
+ (p) = &(vv)->v[(vv)->n++]; \
+} while (0)
+
+enum { RAW, IFO, VOB, BUP };
+typedef uint_least32_t ident;
+
+static inline ident mkident(unsigned kind, unsigned title, unsigned part)
+ { return (((ident)kind << 0) | ((ident)title << 8) | ((ident)part << 16)); }
+static inline unsigned id_kind(ident id) { return ((id >> 0)&0x0ff); }
+static inline unsigned id_title(ident id) { return ((id >> 8)&0x0ff); }
+static inline unsigned id_part(ident id) { return ((id >> 16)&0x0ff); }
+
#define MAXFNSZ (1 + 8 + 1 + 12 + 1)
static void store_filename(char *buf, ident id)
}
}
-#define DEFVEC(vtype, etype) \
- typedef struct { etype *v; size_t n, sz; } vtype
-#define VEC_INIT { 0, 0, 0 }
-#define VEC_FREE(vv) do { \
- free((vv)->v); (vv)->v 0; (vv)->n = (vv)->sz = 0; \
-} while (0)
-#define VEC_PUSH(p, vv) do { \
- size_t _want; \
- if ((vv)->n >= (vv)->sz) { \
- (vv)->sz = (vv)->sz ? 2*(vv)->sz : 32; \
- _want = (vv)->sz*sizeof(*(vv)->v); \
- (vv)->v = realloc((vv)->v, _want); \
- if (!(vv)->v) bail("out of memory allocating %zu bytes", _want); \
- } \
- (p) = &(vv)->v[(vv)->n++]; \
-} while (0)
+typedef uint_least32_t secaddr;
+#define PRIuSEC PRIuLEAST32
#define MAXFILES (1 + 2*99 + 1)
struct file {
ident id;
- uint32_t start, end;
+ secaddr start, end;
};
DEFVEC(file_v, struct file);
static file_v filetab = VEC_INIT;
-enum { EV_WRITE, EV_BEGIN, EV_END, EV_STOP };
+enum { EV_STOP, EV_BEGIN, EV_END, EV_WRITE };
struct event {
unsigned char ev, file;
- uint32_t pos;
+ secaddr pos;
};
DEFVEC(event_v, struct event);
static event_v eventq = VEC_INIT;
+static int compare_event(const void *a, const void *b)
+{
+ const struct event *eva = a, *evb = b;
+
+ if (eva->pos < evb->pos) return (-1);
+ else if (eva->pos > evb->pos) return (+1);
+
+ if (eva->ev < evb->ev) return (-1);
+ else if (eva->ev > evb->ev) return (+1);
+
+ if (eva->file < evb->file) return (-1);
+ else if (eva->file > evb->file) return (+1);
+
+ return (0);
+}
+
typedef uint_least32_t bits;
static bits live[(MAXFILES + 31)/32];
return (i);
}
-static void put_event(unsigned evtype, unsigned file, uint32_t pos)
+static void put_event(unsigned evtype, unsigned file, secaddr pos)
{
struct event *ev;
ev->ev = evtype; ev->file = file; ev->pos = pos;
}
-static void put_file(ident id, uint32_t start, uint32_t end)
+static void put_file(ident id, secaddr start, secaddr end)
{
struct file *f;
size_t i;
{
ident id = mkident(VOB, title, 0);
char fn[MAXFNSZ];
- uint32_t start, len;
+ secaddr start, len;
store_filename(fn, id);
start = UDFFindFile(dvd, fn, &len); if (!start) return;
#ifdef DEBUG
- printf(";; %8"PRIu32" .. %-8"PRIu32": %s\n",
+ printf(";; %8"PRIuSEC" .. %-8"PRIuSEC": %s\n",
start, start + SECTORS(len), fn);
#endif
put_file(id, start, start + SECTORS(len));
static void put_title(dvd_reader_t *dvd, unsigned title)
{
char fn[MAXFNSZ];
- uint32_t start[9], len[9];
+ secaddr start[9], len[9];
unsigned i, npart;
for (i = 0; i < 9; i++) {
#ifdef DEBUG
for (i = 0; i < npart; i++) {
store_filename(fn, mkident(VOB, title, i + 1));
- printf(";; %8"PRIu32" .. %-8"PRIu32": %s\n",
+ printf(";; %8"PRIuSEC" .. %-8"PRIuSEC": %s\n",
start[i], start[i] + SECTORS(len[i]), fn);
}
#endif
if (npart > 1)
for (i = 0; i < npart - 1; i++) {
if (len[i]%SECTORSZ)
- bail("title %u part %u length = %"PRIu32" not a multiple of %d",
+ bail("title %u part %u length = %"PRIuSEC" not a multiple of %d",
title, i, len[i], SECTORSZ);
if (start[i] + len[i]/SECTORSZ != start[i + 1])
- bail("title %u part %u end = %"PRIu32" /= part %u start = %"PRIu32"",
- title, i, start[i] + len[i]/SECTORSZ, i + 1, start[i + 1]);
+ bail
+ ("title %u part %u end = %"PRIuSEC" /= part %u start = %"PRIuSEC"",
+ title, i, start[i] + len[i]/SECTORSZ, i + 1, start[i + 1]);
}
put_file(mkident(VOB, title, 1),
start[0], start[npart - 1] + SECTORS(len[npart - 1]));
}
-static int compare_event(const void *a, const void *b)
-{
- const struct event *eva = a, *evb = b;
-
- if (eva->pos < evb->pos) return (-1);
- else if (eva->pos > evb->pos) return (+1);
-
- if (eva->ev < evb->ev) return (-1);
- else if (eva->ev > evb->ev) return (+1);
-
- if (eva->file < evb->file) return (-1);
- else if (eva->file > evb->file) return (+1);
-
- return (0);
-}
-
static int progresslen = 0;
static void clear_progress_internal(void)
unsigned f;
#define SRCF_ALLPROGRESS 1u
- uint32_t last_pos, limit, nsectors, ndone;
+ secaddr last_pos, limit, nsectors, ndone;
struct timeval last_time;
double wsum, wcount;
const char *mapfile; FILE *mapfp;
};
#define SOURCE_INIT { 0, -1, 0, 0, 0, 0, 0, 0, 0, { 0, 0 }, 0.0, 0.0, 0, 0 }
-static void report_progress(struct source *src, uint32_t pos)
+static void report_progress(struct source *src, secaddr pos)
{
char etastr[32];
struct timeval now;
if (src->f&SRCF_ALLPROGRESS) percent = pos*100.0/src->limit;
else percent = src->ndone*100.0/src->nsectors;
- print_progress("copied %.1f%% (%"PRIu32" of %"PRIu32"; %.1f %sB/s, ETA %s)",
- percent, pos, src->limit,
- rate, unit, etastr);
+ print_progress
+ ("copied %.1f%% (%"PRIuSEC" of %"PRIuSEC"; %.1f %sB/s, ETA %s)",
+ percent, pos, src->limit, rate, unit, etastr);
if (src->file && id_kind(src->file->id) == VOB) {
append_progress(" -- %s %d %3.1f%%",
id_part(src->file->id) ? "title" : "menu",
}
static void report_bad_blocks_progress(struct source *src,
- uint32_t lo, uint32_t hi, int err)
+ secaddr lo, secaddr hi, int err)
{
report_progress(src, hi);
if (lo == hi) append_progress(": retrying bad sector");
else
- append_progress(": %"PRIu32" bad %s",
+ append_progress(": %"PRIuSEC" bad %s",
hi - lo, hi == lo + 1 ? "sector" : "sectors");
if (err != EIO) append_progress(" (%s)", strerror(err));
fflush(stdout);
}
-static ssize_t read_sectors(struct source *src, uint32_t pos,
- void *buf, uint32_t want)
+static ssize_t read_sectors(struct source *src, secaddr pos,
+ void *buf, secaddr want)
{
ssize_t n;
n = DVDReadBlocks(src->vob, pos - src->file->start, want, buf);
else if (src->file) {
if (lseek(src->dvdfd, (off_t)pos*SECTORSZ, SEEK_SET) < 0)
- bail_syserr(errno, "failed to seek to sector %"PRIu32"", pos);
+ bail_syserr(errno, "failed to seek to sector %"PRIuSEC"", pos);
n = read(src->dvdfd, buf, want*SECTORSZ);
if (n >= 0) n /= SECTORSZ;
} else {
}
}
-static void emit(struct source *src, int outfd, uint32_t start, uint32_t end)
+static void emit(struct source *src, int outfd, secaddr start, secaddr end)
{
#define BUFSECTORS 512
int least, i;
unsigned char buf[BUFSECTORS*SECTORSZ];
- uint32_t pos;
- uint32_t bad_lo, bad_hi, good, step;
+ secaddr pos;
+ secaddr bad_lo, bad_hi, good, step;
size_t want;
ssize_t n;
static int first_time = 1;
least = least_live();
#ifdef DEBUG
- printf(";; %8"PRIu32" .. %"PRIu32"\n", start, end);
+ printf(";; %8"PRIuSEC" .. %"PRIuSEC"\n", start, end);
for (i = 0; i < filetab.n; i++) {
if (!livep(i)) continue;
if (act == -1) act = i;
f = &filetab.v[i]; store_filename(fn, f->id);
- printf(";;\t\t%8"PRIu32" .. %-8"PRIu32" %s\n",
+ printf(";;\t\t%8"PRIuSEC" .. %-8"PRIuSEC" %s\n",
start - f->start, end - f->start, fn);
}
if (act == -1) printf(";;\t\t#<no live source>\n");
n = read_sectors(src, pos, buf, 1);
if (n > 0) {
clear_progress();
- fprintf(stderr, "%s: sector %"PRIu32" read ok after retry\n",
- prog, pos);
+ moan("sector %"PRIuSEC" read ok after retry", pos);
bad_lo = bad_hi = pos;
goto recovered;
}
report_bad_blocks_progress(src, bad_lo, bad_hi, errno);
if (bad_hi >= end) {
clear_progress();
- fprintf(stderr, "%s: giving up on this extent\n", prog);
+ moan("giving up on this extent");
n = 0; goto recovered;
}
step *= 2;
recovered:
if (bad_hi > bad_lo) {
clear_progress();
- fprintf(stderr, "%s: skipping %"PRIu32" bad sectors "
- "(%"PRIu32" .. %"PRIu32")\n",
- prog, bad_hi - bad_lo, bad_lo, bad_hi);
+ moan("skipping %"PRIuSEC" bad sectors (%"PRIuSEC" .. %"PRIuSEC")",
+ bad_hi - bad_lo, bad_lo, bad_hi);
if (src->mapfile) {
if (!src->mapfp) {
src->mapfp = fopen(src->mapfile, "w");
optarg);
fprintf(src->mapfp, "## bad sector map\n\n");
}
- fprintf(src->mapfp, "%"PRIu32" %"PRIu32"\n", bad_lo, bad_hi);
+ fprintf(src->mapfp, "%"PRIuSEC" %"PRIuSEC"\n", bad_lo, bad_hi);
fflush(src->mapfp);
if (ferror(src->mapfp))
bail_syserr(errno, "error writing bad-sector map file");
unsigned f = 0;
char *p;
uint64_t volsz;
- uint32_t pos;
+ secaddr pos;
off_t off;
struct source src = SOURCE_INIT;
unsigned long start, end;
size_t i;
FILE *fp;
struct buf buf = BUF_INIT;
+ struct stat st;
#ifdef DEBUG
const struct file *file;
char fn[MAXFNSZ];
if (f&f_bogus) { usage(stderr); exit(2); }
src.dvdfd = open(device, O_RDONLY);
- if (src.dvdfd < 0) bail_syserr(errno, "failed to open device `%s'", device);
- if (ioctl(src.dvdfd, BLKSSZGET, &blksz))
- bail_syserr(errno, "failed to get block size for `%s'", device);
- if (ioctl(src.dvdfd, BLKGETSIZE64, &volsz))
- bail_syserr(errno, "failed to get volume size for `%s'", device);
+ if (src.dvdfd < 0)
+ bail_syserr(errno, "failed to open device `%s'", device);
+ if (fstat(src.dvdfd, &st))
+ bail_syserr(errno, "failed to stat device `%s'", device);
+ if (S_ISREG(st.st_mode)) {
+ blksz = SECTORSZ;
+ volsz = st.st_size;
+ } else if (S_ISBLK(st.st_mode)) {
+ if (ioctl(src.dvdfd, BLKSSZGET, &blksz))
+ bail_syserr(errno, "failed to get block size for `%s'", device);
+ if (ioctl(src.dvdfd, BLKGETSIZE64, &volsz))
+ bail_syserr(errno, "failed to get volume size for `%s'", device);
+ } else
+ bail("can't use `%s' as source: expected file or block device", device);
if (blksz != SECTORSZ)
bail("device `%s' block size %d /= %d", device, blksz, SECTORSZ);
for (i = 0; i < filetab.n; i++) {
file = &filetab.v[i];
store_filename(fn, file->id);
- printf(";;\t%8"PRIu32" %s\n", file->start, fn);
+ printf(";;\t%8"PRIuSEC" %s\n", file->start, fn);
}
#endif
switch (ev->ev) {
case EV_WRITE:
if (f&f_write)
- bail("overlapping ranges: range from %lu still open at %"PRIu32"",
+ bail("overlapping ranges: range from %lu still open at %"PRIuSEC"",
start, ev->pos);
n++; f |= f_write; start = ev->pos;
break;
#ifdef DEBUG
store_filename(fn, filetab.v[ev->file].id);
clear_progress();
- printf(";; %8"PRIu32": begin `%s'\n", pos, fn);
+ printf(";; %8"PRIuSEC": begin `%s'\n", pos, fn);
#endif
break;
case EV_WRITE:
lseek(outfd, (off_t)ev->pos*SECTORSZ, SEEK_SET) < 0)
bail_syserr(errno,
"failed to seek to resume position "
- "(sector %"PRIu32") in output file `%s'",
+ "(sector %"PRIuSEC") in output file `%s'",
ev->pos, outfile);
#ifdef DEBUG
clear_progress();
- printf(";; %8"PRIu32": begin write\n", pos);
+ printf(";; %8"PRIuSEC": begin write\n", pos);
#endif
f |= f_write;
break;
f &= ~f_write;
#ifdef DEBUG
clear_progress();
- printf(";; %8"PRIu32": end write\n", pos);
+ printf(";; %8"PRIuSEC": end write\n", pos);
#endif
break;
case EV_END:
#ifdef DEBUG
store_filename(fn, filetab.v[ev->file].id);
clear_progress();
- printf(";; %8"PRIu32": end `%s'\n", pos, fn);
+ printf(";; %8"PRIuSEC": end `%s'\n", pos, fn);
#endif
break;
default: abort();