MFC numerous features from HEAD.
[dragonfly.git] / usr.sbin / ckdist / ckdist.c
blobae09be77a57ed204b4bc04bc9835f7d174a286d2
1 /*
2 * Copyright (c) 1997 Robert Nordier
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in
12 * the documentation and/or other materials provided with the
13 * distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
16 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY
19 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
21 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
23 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
25 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 * $FreeBSD: src/usr.sbin/ckdist/ckdist.c,v 1.3.2.1 2000/07/01 10:37:21 ps Exp $
28 * $DragonFly: src/usr.sbin/ckdist/ckdist.c,v 1.3 2004/12/18 22:48:02 swildner Exp $
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <err.h>
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <fts.h>
37 #include <md5.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
43 extern int crc(int fd, u_long * cval, u_long * clen);
45 #define DISTMD5 1 /* MD5 format */
46 #define DISTINF 2 /* .inf format */
47 #define DISTTYPES 2 /* types supported */
49 #define E_UNKNOWN 1 /* Unknown format */
50 #define E_BADMD5 2 /* Invalid MD5 format */
51 #define E_BADINF 3 /* Invalid .inf format */
52 #define E_NAME 4 /* Can't derive component name */
53 #define E_LENGTH 5 /* Length mismatch */
54 #define E_CHKSUM 6 /* Checksum mismatch */
55 #define E_ERRNO 7 /* sys_errlist[errno] */
57 #define isfatal(err) ((err) && (err) <= E_NAME)
59 #define NAMESIZE 256 /* filename buffer size */
60 #define MDSUMLEN 32 /* length of MD5 message digest */
62 #define isstdin(path) ((path)[0] == '-' && !(path)[1])
64 static const char *opt_dir; /* where to look for components */
65 static const char *opt_name; /* name for accessing components */
66 static int opt_all; /* report on all components */
67 static int opt_ignore; /* ignore missing components */
68 static int opt_recurse; /* search directories recursively */
69 static int opt_silent; /* silent about inaccessible files */
70 static int opt_type; /* dist type: md5 or inf */
71 static int opt_exist; /* just verify existence */
73 static int ckdist(const char *path, int type);
74 static int chkmd5(FILE * fp, const char *path);
75 static int chkinf(FILE * fp, const char *path);
76 static int report(const char *path, const char *name, int error);
77 static const char *distname(const char *path, const char *name,
78 const char *ext);
79 static char *stripath(const char *path);
80 static int distfile(const char *path);
81 static int disttype(const char *name);
82 static int fail(const char *path, const char *msg);
83 static void usage(void);
85 int
86 main(int argc, char *argv[])
88 static char *arg[2];
89 struct stat sb;
90 FTS *ftsp;
91 FTSENT *f;
92 int rval, c, type;
94 while ((c = getopt(argc, argv, "ad:in:rst:x")) != -1)
95 switch (c) {
96 case 'a':
97 opt_all = 1;
98 break;
99 case 'd':
100 opt_dir = optarg;
101 break;
102 case 'i':
103 opt_ignore = 1;
104 break;
105 case 'n':
106 opt_name = optarg;
107 break;
108 case 'r':
109 opt_recurse = 1;
110 break;
111 case 's':
112 opt_silent = 1;
113 break;
114 case 't':
115 if ((opt_type = disttype(optarg)) == 0) {
116 warnx("illegal argument to -t option");
117 usage();
119 break;
120 case 'x':
121 opt_exist = 1;
122 break;
123 default:
124 usage();
126 argc -= optind;
127 argv += optind;
128 if (argc < 1)
129 usage();
130 if (opt_dir) {
131 if (stat(opt_dir, &sb))
132 err(2, "%s", opt_dir);
133 if (!S_ISDIR(sb.st_mode))
134 errx(2, "%s: not a directory", opt_dir);
136 rval = 0;
137 do {
138 if (isstdin(*argv))
139 rval |= ckdist(*argv, opt_type);
140 else if (stat(*argv, &sb))
141 rval |= fail(*argv, NULL);
142 else if (S_ISREG(sb.st_mode))
143 rval |= ckdist(*argv, opt_type);
144 else {
145 arg[0] = *argv;
146 if ((ftsp = fts_open(arg, FTS_LOGICAL, NULL)) == NULL)
147 err(2, "fts_open");
148 while ((f = fts_read(ftsp)) != NULL)
149 switch (f->fts_info) {
150 case FTS_DC:
151 rval = fail(f->fts_path, "Directory causes a cycle");
152 break;
153 case FTS_DNR:
154 case FTS_ERR:
155 case FTS_NS:
156 rval = fail(f->fts_path, sys_errlist[f->fts_errno]);
157 break;
158 case FTS_D:
159 if (!opt_recurse && f->fts_level > FTS_ROOTLEVEL &&
160 fts_set(ftsp, f, FTS_SKIP))
161 err(2, "fts_set");
162 break;
163 case FTS_F:
164 if ((type = distfile(f->fts_name)) != 0 &&
165 (!opt_type || type == opt_type))
166 rval |= ckdist(f->fts_path, type);
167 break;
168 default: ;
170 if (errno)
171 err(2, "fts_read");
172 if (fts_close(ftsp))
173 err(2, "fts_close");
175 } while (*++argv);
176 return rval;
179 static int
180 ckdist(const char *path, int type)
182 FILE *fp;
183 int rval, c;
185 if (isstdin(path)) {
186 path = "(stdin)";
187 fp = stdin;
188 } else if ((fp = fopen(path, "r")) == NULL)
189 return fail(path, NULL);
190 if (!type) {
191 if (fp != stdin)
192 type = distfile(path);
193 if (!type)
194 if ((c = fgetc(fp)) != EOF) {
195 type = c == 'M' ? DISTMD5 : c == 'P' ? DISTINF : 0;
196 ungetc(c, fp);
199 switch (type) {
200 case DISTMD5:
201 rval = chkmd5(fp, path);
202 break;
203 case DISTINF:
204 rval = chkinf(fp, path);
205 break;
206 default:
207 rval = report(path, NULL, E_UNKNOWN);
209 if (ferror(fp))
210 warn("%s", path);
211 if (fp != stdin && fclose(fp))
212 err(2, "%s", path);
213 return rval;
216 static int
217 chkmd5(FILE * fp, const char *path)
219 char buf[298]; /* "MD5 (NAMESIZE = MDSUMLEN" */
220 char name[NAMESIZE + 1];
221 char sum[MDSUMLEN + 1], chk[MDSUMLEN + 1];
222 const char *dname;
223 char *s;
224 int rval, error, c, fd;
225 char ch;
227 rval = 0;
228 while (fgets(buf, sizeof(buf), fp)) {
229 dname = NULL;
230 error = 0;
231 if (((c = sscanf(buf, "MD5 (%256s = %32s%c", name, sum,
232 &ch)) != 3 && (!feof(fp) || c != 2)) ||
233 (c == 3 && ch != '\n') ||
234 (s = strrchr(name, ')')) == NULL ||
235 strlen(sum) != MDSUMLEN)
236 error = E_BADMD5;
237 else {
238 *s = 0;
239 if ((dname = distname(path, name, NULL)) == NULL)
240 error = E_NAME;
241 else if (opt_exist) {
242 if ((fd = open(dname, O_RDONLY)) == -1)
243 error = E_ERRNO;
244 else if (close(fd))
245 err(2, "%s", dname);
246 } else if (!MD5File((char *)dname, chk))
247 error = E_ERRNO;
248 else if (strcmp(chk, sum))
249 error = E_CHKSUM;
251 if (opt_ignore && error == E_ERRNO && errno == ENOENT)
252 continue;
253 if (error || opt_all)
254 rval |= report(path, dname, error);
255 if (isfatal(error))
256 break;
258 return rval;
261 static int
262 chkinf(FILE * fp, const char *path)
264 char buf[30]; /* "cksum.2 = 10 6" */
265 char ext[3];
266 struct stat sb;
267 const char *dname;
268 u_long sum, len, chk;
269 int rval, error, c, pieces, cnt, fd;
270 char ch;
272 rval = 0;
273 for (cnt = -1; fgets(buf, sizeof(buf), fp); cnt++) {
274 fd = -1;
275 dname = NULL;
276 error = 0;
277 if (cnt == -1) {
278 if ((c = sscanf(buf, "Pieces = %d%c", &pieces, &ch)) != 2 ||
279 ch != '\n' || pieces < 1)
280 error = E_BADINF;
281 } else if (((c = sscanf(buf, "cksum.%2s = %lu %lu%c", ext, &sum,
282 &len, &ch)) != 4 &&
283 (!feof(fp) || c != 3)) || (c == 4 && ch != '\n') ||
284 ext[0] != 'a' + cnt / 26 || ext[1] != 'a' + cnt % 26)
285 error = E_BADINF;
286 else if ((dname = distname(fp == stdin ? NULL : path, NULL,
287 ext)) == NULL)
288 error = E_NAME;
289 else if ((fd = open(dname, O_RDONLY)) == -1)
290 error = E_ERRNO;
291 else if (fstat(fd, &sb))
292 error = E_ERRNO;
293 else if (sb.st_size != (off_t)len)
294 error = E_LENGTH;
295 else if (!opt_exist) {
296 if (crc(fd, &chk, &len))
297 error = E_ERRNO;
298 else if (chk != sum)
299 error = E_CHKSUM;
301 if (fd != -1 && close(fd))
302 err(2, "%s", dname);
303 if (opt_ignore && error == E_ERRNO && errno == ENOENT)
304 continue;
305 if (error || (opt_all && cnt >= 0))
306 rval |= report(path, dname, error);
307 if (isfatal(error))
308 break;
310 return rval;
313 static int
314 report(const char *path, const char *name, int error)
316 if (name)
317 name = stripath(name);
318 switch (error) {
319 case E_UNKNOWN:
320 printf("%s: Unknown format\n", path);
321 break;
322 case E_BADMD5:
323 printf("%s: Invalid MD5 format\n", path);
324 break;
325 case E_BADINF:
326 printf("%s: Invalid .inf format\n", path);
327 break;
328 case E_NAME:
329 printf("%s: Can't derive component name\n", path);
330 break;
331 case E_LENGTH:
332 printf("%s: %s: Size mismatch\n", path, name);
333 break;
334 case E_CHKSUM:
335 printf("%s: %s: Checksum mismatch\n", path, name);
336 break;
337 case E_ERRNO:
338 printf("%s: %s: %s\n", path, name, sys_errlist[errno]);
339 break;
340 default:
341 printf("%s: %s: OK\n", path, name);
343 return error != 0;
346 static const char *
347 distname(const char *path, const char *name, const char *ext)
349 static char buf[NAMESIZE];
350 size_t plen, nlen;
351 char *s;
353 if (opt_name)
354 name = opt_name;
355 else if (!name) {
356 if (!path)
357 return NULL;
358 name = stripath(path);
360 nlen = strlen(name);
361 if (ext && nlen > 4 && name[nlen - 4] == '.' &&
362 disttype(name + nlen - 3) == DISTINF)
363 nlen -= 4;
364 if (opt_dir) {
365 path = opt_dir;
366 plen = strlen(path);
367 } else
368 plen = path && (s = strrchr(path, '/')) != NULL ?
369 (size_t)(s - path) : 0;
370 if (plen + (plen > 0) + nlen + (ext ? 3 : 0) >= sizeof(buf))
371 return NULL;
372 s = buf;
373 if (plen) {
374 memcpy(s, path, plen);
375 s += plen;
376 *s++ = '/';
378 memcpy(s, name, nlen);
379 s += nlen;
380 if (ext) {
381 *s++ = '.';
382 memcpy(s, ext, 2);
383 s += 2;
385 *s = 0;
386 return buf;
389 static char *
390 stripath(const char *path)
392 const char *s;
394 return (char *)((s = strrchr(path, '/')) != NULL && s[1] ?
395 s + 1 : path);
398 static int
399 distfile(const char *path)
401 const char *s;
402 int type;
404 if ((type = disttype(path)) == DISTMD5 ||
405 ((s = strrchr(path, '.')) != NULL && s > path &&
406 (type = disttype(s + 1)) != 0))
407 return type;
408 return 0;
411 static int
412 disttype(const char *name)
414 static const char dname[DISTTYPES][4] = {"md5", "inf"};
415 int i;
417 for (i = 0; i < DISTTYPES; i++)
418 if (!strcmp(dname[i], name))
419 return 1 + i;
420 return 0;
423 static int
424 fail(const char *path, const char *msg)
426 if (opt_silent)
427 return 0;
428 warnx("%s: %s", path, msg ? msg : sys_errlist[errno]);
429 return 2;
432 static void
433 usage(void)
435 fprintf(stderr,
436 "usage: ckdist [-airsx] [-d dir] [-n name] [-t type] file ...\n");
437 exit(2);