@@@ dvdrip-upload: change settings while i'm stealing someone else's internet
[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
844bf71b
MW
7static void set_status(int st)
8 { if (st > status) status = st; }
0049c6b5 9static void vgripe_syserr(int st, int err, const char *fmt, va_list ap)
844bf71b 10 { vmoan_syserr(err, fmt, ap); set_status(st); }
0049c6b5
MW
11static void vgripe(int st, const char *fmt, va_list ap)
12 { vgripe_syserr(st, 0, fmt, ap); }
13PRINTF_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}
19PRINTF_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
22static 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
42typedef uint_least32_t u32;
43#define Pu32 PRIuLEAST32
44
45static 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
53static 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
89static 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
98static void check_img(const char *file)
99{
100 int fd = -1;
0049c6b5 101 unsigned char b[SECTORSZ], bb[SECTORSZ];
eac2ed1a
MW
102 off_t volsz;
103 int blksz;
0049c6b5
MW
104 secaddr end;
105 unsigned i, j;
106
d23998cb
MW
107 if (open_dvd(file, (flags&F_FIX) ? O_RDWR : O_RDONLY, &fd, 0))
108 { set_status(2); goto end; }
eac2ed1a
MW
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) {
0049c6b5
MW
113 gripe(2, "bad length for `%s' -- not whole number of sectors", file);
114 goto end;
115 }
eac2ed1a 116 end = volsz/SECTORSZ;
0049c6b5
MW
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: "
323c8d53 128 "couldn't find backup anchor descriptor in `%s'", file);
0049c6b5
MW
129 goto end;
130nonzero:
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
173end:
174 if (fd != -1) close(fd);
175}
176
177static void usage(FILE *fp)
178 { fprintf(fp, "usage: %s [-x] IMG ...\n", prog); }
179
180int 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); }
fb48dc9c
MW
197 setlocale(LC_ALL, "");
198 progress_init(&progress);
0049c6b5 199 for (i = optind; i < argc; i++) check_img(argv[i]);
fb48dc9c 200 progress_free(&progress);
0049c6b5
MW
201 return (status);
202}