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