chkdvdimg.c: Name the file when reporting too many trailing zeros.
[dvdrip] / chkdvdimg.c
CommitLineData
0049c6b5
MW
1#include "lib.h"
2
3static int status = 0;
4static unsigned flags = 0;
5#define F_FIX 1u
6
7static void vgripe_syserr(int st, int err, const char *fmt, va_list ap)
8 { vmoan_syserr(err, fmt, ap); if (st > status) status = st; }
9static void vgripe(int st, const char *fmt, va_list ap)
10 { vgripe_syserr(st, 0, fmt, ap); }
11PRINTF_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}
17PRINTF_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
20static 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
40typedef uint_least32_t u32;
41#define Pu32 PRIuLEAST32
42
43static 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
51static 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
87static 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
96static void check_img(const char *file)
97{
98 int fd = -1;
0049c6b5 99 unsigned char b[SECTORSZ], bb[SECTORSZ];
eac2ed1a
MW
100 off_t volsz;
101 int blksz;
0049c6b5
MW
102 secaddr end;
103 unsigned i, j;
104
105 fd = open(file, (flags&F_FIX) ? O_RDWR : O_RDONLY);
106 if (fd < 0)
107 { gripe_syserr(2, errno, "failed to open `%s'", file); goto end; }
eac2ed1a
MW
108 blksz = SECTORSZ; volsz = device_size(fd, file, &blksz);
109 if (SECTORSZ != 2048)
110 { gripe(2, "device sector size %d /= 2048", blksz); goto end; }
111 if (volsz%SECTORSZ) {
0049c6b5
MW
112 gripe(2, "bad length for `%s' -- not whole number of sectors", file);
113 goto end;
114 }
eac2ed1a 115 end = volsz/SECTORSZ;
0049c6b5
MW
116
117 if (carefully_read(fd, 256*SECTORSZ, b, SECTORSZ, file) ||
118 check_anchor_header(b, 256, CAHF_GRIPE | CAHF_FULL, file))
119 goto end;
120
121 for (i = 1; i < 32768; i++) {
122 if (carefully_read(fd, (off_t)(end - i)*SECTORSZ, bb, SECTORSZ, file))
123 goto end;
124 if (bb[0] || !all_zero_p(bb, SECTORSZ)) goto nonzero;
125 }
126 gripe(1, "too many trailing zero sectors: "
323c8d53 127 "couldn't find backup anchor descriptor in `%s'", file);
0049c6b5
MW
128 goto end;
129nonzero:
130 j = i;
131 for (;;) {
132 if (bb[0] == 2 && !check_anchor_header(bb, end - j, 0, file))
133 break;
134 j++;
135 if (j > i + 256) {
136 gripe(1, "failed to find backup anchor descriptor in `%s'", file);
137 goto end;
138 }
139 if (carefully_read(fd, (off_t)(end - j)*SECTORSZ, bb, SECTORSZ, file))
140 goto end;
141 }
142 if (j > i) {
143 gripe(1, "found backup anchor descriptor at sector %"PRIuSEC" "
144 " = %"PRIuSEC" from end, = %"PRIuSEC" from trailing zeros",
145 end - j, j, j - i + 1);
146 i = j;
147 }
148
149 if (check_anchor_header(bb, end - i, CAHF_GRIPE | CAHF_FULL, file))
150 goto end;
151
152 if (MEMCMP(b + 16, != , bb + 16, SECTORSZ - 16)) {
153 gripe(1, "backup anchor descriptor in sector %"PRIuSEC" "
154 "doesn't match primary in sector 256 of `%s'",
155 end - i, file);
156 goto end;
157 }
158
159 if (i > 1) {
160 if (!(flags&F_FIX))
161 gripe(1, "%u trailing zero sectors in `%s'", i - 1, file);
162 else {
163 if (ftruncate(fd, (off_t)(end - i + 1)*SECTORSZ))
164 gripe_syserr(2, errno,
165 "failed to truncate `%s' to %"PRIuSEC" sectors",
166 file, end - i + 1);
167 else
168 moan("removed %u trailing zero sectors from `%s'", i - 1, file);
169 }
170 }
171
172end:
173 if (fd != -1) close(fd);
174}
175
176static void usage(FILE *fp)
177 { fprintf(fp, "usage: %s [-x] IMG ...\n", prog); }
178
179int main(int argc, char *argv[])
180{
181 int i, opt;
182 unsigned f = 0;
183#define f_bogus 1u
184
185 set_prog(argv[0]);
186 for (;;) {
187 opt = getopt(argc, argv, "hx"); if (opt < 0) break;
188 switch (opt) {
189 case 'h': usage(stdout); exit(0);
190 case 'x': flags |= F_FIX; break;
191 default: f |= f_bogus; break;
192 }
193 }
194 if (optind >= argc) f |= f_bogus;
195 if (f&f_bogus) { usage(stderr); exit(2); }
196
197 for (i = optind; i < argc; i++) check_img(argv[i]);
198 return (status);
199}