chiark / gitweb /
Merge branch 'master' of git.distorted.org.uk:~mdw/publish/public-git/dvdrip
[dvdrip] / chkdvdimg.c
1 #include "lib.h"
2
3 static int status = 0;
4 static unsigned flags = 0;
5 #define F_FIX 1u
6
7 static void vgripe_syserr(int st, int err, const char *fmt, va_list ap)
8   { vmoan_syserr(err, fmt, ap); if (st > status) status = st; }
9 static void vgripe(int st, const char *fmt, va_list ap)
10   { vgripe_syserr(st, 0, fmt, ap); }
11 PRINTF_LIKE(3, 4)
12   static void gripe_syserr(int st, int err, const char *fmt, ...)
13 {
14   va_list ap;
15   va_start(ap, fmt); vgripe_syserr(st, err, fmt, ap); va_end(ap);
16 }
17 PRINTF_LIKE(2, 3) static void gripe(int st, const char *fmt, ...)
18   { va_list ap; va_start(ap, fmt); vgripe(st, fmt, ap); va_end(ap); }
19
20 static int carefully_read(int fd, off_t off,
21                           void *p, size_t sz, const char *file)
22 {
23   ssize_t n;
24   char *q = p;
25
26   if (lseek(fd, off, SEEK_SET) < 0)
27     { gripe_syserr(2, errno, "failed to seek in `%s'", file); return (-1); }
28
29   while (sz) {
30     n = read(fd, q, sz);
31     if (n < 0)
32       { gripe_syserr(2, errno, "failed to read `%s'", file); return (-1); }
33     else if (!n)
34       { gripe(2, "unexpected end-of-file reading `%s'", file); return (-1); }
35     q += n; sz -= n;
36   }
37   return (0);
38 }
39
40 typedef uint_least32_t u32;
41 #define Pu32 PRIuLEAST32
42
43 static u32 load32(const unsigned char *p)
44 {
45   return (((u32)p[0] <<  0) | ((u32)p[1] <<  8) |
46           ((u32)p[2] << 16) | ((u32)p[3] << 24));
47 }
48
49 #define CAHF_GRIPE 1u
50 #define CAHF_FULL 1u
51 static int check_anchor_header(const unsigned char  *b, secaddr addr,
52                                unsigned f, const char *file)
53 {
54   unsigned i, t;
55   secaddr n;
56
57   if (b[0] != 2) {
58     if (f&CAHF_GRIPE)
59       gripe(1, "anchor descriptor not found at `%s' sector %"PRIuSEC"",
60             file, addr);
61     return (-1);
62   }
63
64   for (i = 0, t = 0; i < 16; i++) if (i != 4) t += b[i];
65   t %= 256;
66   if (b[4] != t) {
67     if (f&CAHF_GRIPE)
68       gripe(1, "bad anchor descriptor header checksum (%u /= %u) "
69             "at `%s' sector %"PRIuSEC"", (unsigned)b[4], t, file, addr);
70     return (-1);
71   }
72
73   if (f&CAHF_FULL) {
74     n = load32(b + 12);
75     if (n != addr) {
76       if (f&CAHF_GRIPE)
77         gripe(1, "bad sector number in anchor descriptor "
78               "(%"PRIuSEC" /= %"PRIuSEC") in `%s'",
79               n, addr, file);
80       return (-1);
81     }
82   }
83
84   return (0);
85 }
86
87 static int all_zero_p(const unsigned char *p, size_t sz)
88 {
89   while (sz) {
90     if (*p) return (0);
91     p++; sz--;
92   }
93   return (1);
94 }
95
96 static void check_img(const char *file)
97 {
98   int fd = -1;
99   unsigned char b[SECTORSZ], bb[SECTORSZ];
100   off_t volsz;
101   int blksz;
102   secaddr end;
103   unsigned i, j;
104
105   open_dvd(file, (flags&F_FIX) ? O_RDWR : O_RDONLY, &fd, 0);
106   blksz = SECTORSZ; volsz = device_size(fd, file, &blksz);
107   if (SECTORSZ != 2048)
108     { gripe(2, "device sector size %d /= 2048", blksz); goto end; }
109   if (volsz%SECTORSZ) {
110     gripe(2, "bad length for `%s' -- not whole number of sectors", file);
111     goto end;
112   }
113   end = volsz/SECTORSZ;
114
115   if (carefully_read(fd, 256*SECTORSZ, b, SECTORSZ, file) ||
116       check_anchor_header(b, 256, CAHF_GRIPE | CAHF_FULL, file))
117     goto end;
118
119   for (i = 1; i < 32768; i++) {
120     if (carefully_read(fd, (off_t)(end - i)*SECTORSZ, bb, SECTORSZ, file))
121       goto end;
122     if (bb[0] || !all_zero_p(bb, SECTORSZ)) goto nonzero;
123   }
124   gripe(1, "too many trailing zero sectors: "
125         "couldn't find backup anchor descriptor in `%s'", file);
126   goto end;
127 nonzero:
128   j = i;
129   for (;;) {
130     if (bb[0] == 2 && !check_anchor_header(bb, end - j, 0, file))
131       break;
132     j++;
133     if (j > i + 256) {
134       gripe(1, "failed to find backup anchor descriptor in `%s'", file);
135       goto end;
136     }
137     if (carefully_read(fd, (off_t)(end - j)*SECTORSZ, bb, SECTORSZ, file))
138       goto end;
139   }
140   if (j > i) {
141     gripe(1, "found backup anchor descriptor at sector %"PRIuSEC" "
142           " = %"PRIuSEC" from end, = %"PRIuSEC" from trailing zeros",
143           end - j, j, j - i + 1);
144     i = j;
145   }
146
147   if (check_anchor_header(bb, end - i, CAHF_GRIPE | CAHF_FULL, file))
148     goto end;
149
150   if (MEMCMP(b + 16, != , bb + 16, SECTORSZ - 16)) {
151     gripe(1, "backup anchor descriptor in sector %"PRIuSEC" "
152           "doesn't match primary in sector 256 of `%s'",
153           end - i, file);
154     goto end;
155   }
156
157   if (i > 1) {
158     if (!(flags&F_FIX))
159       gripe(1, "%u trailing zero sectors in `%s'", i - 1, file);
160     else {
161       if (ftruncate(fd, (off_t)(end - i + 1)*SECTORSZ))
162         gripe_syserr(2, errno,
163                      "failed to truncate `%s' to %"PRIuSEC" sectors",
164                      file, end - i + 1);
165       else
166         moan("removed %u trailing zero sectors from `%s'", i - 1, file);
167     }
168   }
169
170 end:
171   if (fd != -1) close(fd);
172 }
173
174 static void usage(FILE *fp)
175   { fprintf(fp, "usage: %s [-x] IMG ...\n", prog); }
176
177 int main(int argc, char *argv[])
178 {
179   int i, opt;
180   unsigned f = 0;
181 #define f_bogus 1u
182
183   set_prog(argv[0]);
184   for (;;) {
185     opt = getopt(argc, argv, "hx"); if (opt < 0) break;
186     switch (opt) {
187       case 'h': usage(stdout); exit(0);
188       case 'x': flags |= F_FIX; break;
189       default: f |= f_bogus; break;
190     }
191   }
192   if (optind >= argc) f |= f_bogus;
193   if (f&f_bogus) { usage(stderr); exit(2); }
194   setlocale(LC_ALL, "");
195   progress_init(&progress);
196   for (i = optind; i < argc; i++) check_img(argv[i]);
197   progress_free(&progress);
198   return (status);
199 }