]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - usr.sbin/ckdist/ckdist.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / usr.sbin / ckdist / ckdist.c
1 /*
2  * Copyright (c) 1997 Robert Nordier
3  * All rights reserved.
4  *
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.
14  *
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.
26  */
27
28 #ifndef lint
29 static const char rcsid[] =
30   "$FreeBSD$";
31 #endif /* not lint */
32
33 #include <sys/types.h>
34 #include <sys/stat.h>
35
36 #include <err.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <fts.h>
40 #include <md5.h>
41 #include <stdio.h>
42 #include <stdint.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46
47 extern int crc(int fd, uint32_t *cval, off_t *clen);
48
49 #define DISTMD5     1           /* MD5 format */
50 #define DISTINF     2           /* .inf format */
51 #define DISTTYPES   2           /* types supported */
52
53 #define E_UNKNOWN   1           /* Unknown format */
54 #define E_BADMD5    2           /* Invalid MD5 format */
55 #define E_BADINF    3           /* Invalid .inf format */
56 #define E_NAME      4           /* Can't derive component name */
57 #define E_LENGTH    5           /* Length mismatch */
58 #define E_CHKSUM    6           /* Checksum mismatch */
59 #define E_ERRNO     7           /* sys_errlist[errno] */
60
61 #define isfatal(err)   ((err) && (err) <= E_NAME)
62
63 #define NAMESIZE  256           /* filename buffer size */
64 #define MDSUMLEN   32           /* length of MD5 message digest */
65
66 #define isstdin(path)  ((path)[0] == '-' && !(path)[1])
67
68 static const char *opt_dir;     /* where to look for components */
69 static const char *opt_name;    /* name for accessing components */
70 static int opt_all;             /* report on all components */
71 static int opt_ignore;          /* ignore missing components */
72 static int opt_recurse;         /* search directories recursively */
73 static int opt_silent;          /* silent about inaccessible files */
74 static int opt_type;            /* dist type: md5 or inf */
75 static int opt_exist;           /* just verify existence */
76
77 static int ckdist(const char *path, int type);
78 static int chkmd5(FILE * fp, const char *path);
79 static int chkinf(FILE * fp, const char *path);
80 static int report(const char *path, const char *name, int error);
81 static const char *distname(const char *path, const char *name,
82                             const char *ext);
83 static const char *stripath(const char *path);
84 static int distfile(const char *path);
85 static int disttype(const char *name);
86 static int fail(const char *path, const char *msg);
87 static void usage(void);
88
89 int
90 main(int argc, char *argv[])
91 {
92     static char *arg[2];
93     struct stat sb;
94     FTS *ftsp;
95     FTSENT *f;
96     int rval, c, type;
97
98     while ((c = getopt(argc, argv, "ad:in:rst:x")) != -1)
99         switch (c) {
100         case 'a':
101             opt_all = 1;
102             break;
103         case 'd':
104             opt_dir = optarg;
105             break;
106         case 'i':
107             opt_ignore = 1;
108             break;
109         case 'n':
110             opt_name = optarg;
111             break;
112         case 'r':
113             opt_recurse = 1;
114             break;
115         case 's':
116             opt_silent = 1;
117             break;
118         case 't':
119             if ((opt_type = disttype(optarg)) == 0) {
120                 warnx("illegal argument to -t option");
121                 usage();
122             }
123             break;
124         case 'x':
125             opt_exist = 1;
126             break;
127         default:
128             usage();
129         }
130     argc -= optind;
131     argv += optind;
132     if (argc < 1)
133         usage();
134     if (opt_dir) {
135         if (stat(opt_dir, &sb))
136             err(2, "%s", opt_dir);
137         if (!S_ISDIR(sb.st_mode))
138             errx(2, "%s: not a directory", opt_dir);
139     }
140     rval = 0;
141     do {
142         if (isstdin(*argv))
143             rval |= ckdist(*argv, opt_type);
144         else if (stat(*argv, &sb))
145             rval |= fail(*argv, NULL);
146         else if (S_ISREG(sb.st_mode))
147             rval |= ckdist(*argv, opt_type);
148         else {
149             arg[0] = *argv;
150             if ((ftsp = fts_open(arg, FTS_LOGICAL, NULL)) == NULL)
151                 err(2, "fts_open");
152             while ((f = fts_read(ftsp)) != NULL)
153                 switch (f->fts_info) {
154                 case FTS_DC:
155                     rval = fail(f->fts_path, "Directory causes a cycle");
156                     break;
157                 case FTS_DNR:
158                 case FTS_ERR:
159                 case FTS_NS:
160                     rval = fail(f->fts_path, sys_errlist[f->fts_errno]);
161                     break;
162                 case FTS_D:
163                     if (!opt_recurse && f->fts_level > FTS_ROOTLEVEL &&
164                         fts_set(ftsp, f, FTS_SKIP))
165                         err(2, "fts_set");
166                     break;
167                 case FTS_F:
168                     if ((type = distfile(f->fts_name)) != 0 &&
169                         (!opt_type || type == opt_type))
170                         rval |= ckdist(f->fts_path, type);
171                     break;
172                 default: ;
173                 }
174             if (errno)
175                 err(2, "fts_read");
176             if (fts_close(ftsp))
177                 err(2, "fts_close");
178         }
179     } while (*++argv);
180     return rval;
181 }
182
183 static int
184 ckdist(const char *path, int type)
185 {
186     FILE *fp;
187     int rval, c;
188
189     if (isstdin(path)) {
190         path = "(stdin)";
191         fp = stdin;
192     } else if ((fp = fopen(path, "r")) == NULL)
193         return fail(path, NULL);
194     if (!type) {
195         if (fp != stdin)
196             type = distfile(path);
197         if (!type)
198             if ((c = fgetc(fp)) != EOF) {
199                 type = c == 'M' ? DISTMD5 : c == 'P' ? DISTINF : 0;
200                 (void)ungetc(c, fp);
201             }
202     }
203     switch (type) {
204     case DISTMD5:
205         rval = chkmd5(fp, path);
206         break;
207     case DISTINF:
208         rval = chkinf(fp, path);
209         break;
210     default:
211         rval = report(path, NULL, E_UNKNOWN);
212     }
213     if (ferror(fp))
214         warn("%s", path);
215     if (fp != stdin && fclose(fp))
216         err(2, "%s", path);
217     return rval;
218 }
219
220 static int
221 chkmd5(FILE * fp, const char *path)
222 {
223     char buf[298];              /* "MD5 (NAMESIZE = MDSUMLEN" */
224     char name[NAMESIZE + 1];
225     char sum[MDSUMLEN + 1], chk[MDSUMLEN + 1];
226     const char *dname;
227     char *s;
228     int rval, error, c, fd;
229     char ch;
230
231     rval = 0;
232     while (fgets(buf, sizeof(buf), fp)) {
233         dname = NULL;
234         error = 0;
235         if (((c = sscanf(buf, "MD5 (%256s = %32s%c", name, sum,
236                          &ch)) != 3 && (!feof(fp) || c != 2)) ||
237             (c == 3 && ch != '\n') ||
238             (s = strrchr(name, ')')) == NULL ||
239             strlen(sum) != MDSUMLEN)
240             error = E_BADMD5;
241         else {
242             *s = 0;
243             if ((dname = distname(path, name, NULL)) == NULL)
244                 error = E_NAME;
245             else if (opt_exist) {
246                 if ((fd = open(dname, O_RDONLY)) == -1)
247                     error = E_ERRNO;
248                 else if (close(fd))
249                     err(2, "%s", dname);
250             } else if (!MD5File(dname, chk))
251                 error = E_ERRNO;
252             else if (strcmp(chk, sum))
253                 error = E_CHKSUM;
254         }
255         if (opt_ignore && error == E_ERRNO && errno == ENOENT)
256             continue;
257         if (error || opt_all)
258             rval |= report(path, dname, error);
259         if (isfatal(error))
260             break;
261     }
262     return rval;
263 }
264
265 static int
266 chkinf(FILE * fp, const char *path)
267 {
268     char buf[30];               /* "cksum.2 = 10 6" */
269     char ext[3];
270     struct stat sb;
271     const char *dname;
272     off_t len;
273     u_long sum;
274     intmax_t sumlen;
275     uint32_t chk;
276     int rval, error, c, pieces, cnt, fd;
277     char ch;
278
279     rval = 0;
280     for (cnt = -1; fgets(buf, sizeof(buf), fp); cnt++) {
281         fd = -1;
282         dname = NULL;
283         error = 0;
284         if (cnt == -1) {
285             if ((c = sscanf(buf, "Pieces =  %d%c", &pieces, &ch)) != 2 ||
286                 ch != '\n' || pieces < 1)
287                 error = E_BADINF;
288         } else if (((c = sscanf(buf, "cksum.%2s = %lu %jd%c", ext, &sum,
289                                 &sumlen, &ch)) != 4 &&
290                     (!feof(fp) || c != 3)) || (c == 4 && ch != '\n') ||
291                    ext[0] != 'a' + cnt / 26 || ext[1] != 'a' + cnt % 26)
292             error = E_BADINF;
293         else if ((dname = distname(fp == stdin ? NULL : path, NULL,
294                                     ext)) == NULL)
295             error = E_NAME;
296         else if ((fd = open(dname, O_RDONLY)) == -1)
297             error = E_ERRNO;
298         else if (fstat(fd, &sb))
299             error = E_ERRNO;
300         else if (sb.st_size != (off_t)sumlen)
301             error = E_LENGTH;
302         else if (!opt_exist) {
303             if (crc(fd, &chk, &len))
304                 error = E_ERRNO;
305             else if (chk != sum)
306                 error = E_CHKSUM;
307         }
308         if (fd != -1 && close(fd))
309             err(2, "%s", dname);
310         if (opt_ignore && error == E_ERRNO && errno == ENOENT)
311             continue;
312         if (error || (opt_all && cnt >= 0))
313             rval |= report(path, dname, error);
314         if (isfatal(error))
315             break;
316     }
317     return rval;
318 }
319
320 static int
321 report(const char *path, const char *name, int error)
322 {
323     if (name)
324         name = stripath(name);
325     switch (error) {
326     case E_UNKNOWN:
327         printf("%s: Unknown format\n", path);
328         break;
329     case E_BADMD5:
330         printf("%s: Invalid MD5 format\n", path);
331         break;
332     case E_BADINF:
333         printf("%s: Invalid .inf format\n", path);
334         break;
335     case E_NAME:
336         printf("%s: Can't derive component name\n", path);
337         break;
338     case E_LENGTH:
339         printf("%s: %s: Size mismatch\n", path, name);
340         break;
341     case E_CHKSUM:
342         printf("%s: %s: Checksum mismatch\n", path, name);
343         break;
344     case E_ERRNO:
345         printf("%s: %s: %s\n", path, name, sys_errlist[errno]);
346         break;
347     default:
348         printf("%s: %s: OK\n", path, name);
349     }
350     return error != 0;
351 }
352
353 static const char *
354 distname(const char *path, const char *name, const char *ext)
355 {
356     static char buf[NAMESIZE];
357     size_t plen, nlen;
358     char *s;
359
360     if (opt_name)
361         name = opt_name;
362     else if (!name) {
363         if (!path)
364             return NULL;
365         name = stripath(path);
366     }
367     nlen = strlen(name);
368     if (ext && nlen > 4 && name[nlen - 4] == '.' &&
369         disttype(name + nlen - 3) == DISTINF)
370         nlen -= 4;
371     if (opt_dir) {
372         path = opt_dir;
373         plen = strlen(path);
374     } else
375         plen = path && (s = strrchr(path, '/')) != NULL ? 
376             (size_t)(s - path) : 0;
377     if (plen + (plen > 0) + nlen + (ext ? 3 : 0) >= sizeof(buf))
378         return NULL;
379     s = buf;
380     if (plen) {
381         memcpy(s, path, plen);
382         s += plen;
383         *s++ = '/';
384     }
385     memcpy(s, name, nlen);
386     s += nlen;
387     if (ext) {
388         *s++ = '.';
389         memcpy(s, ext, 2);
390         s += 2;
391     }
392     *s = 0;
393     return buf;
394 }
395
396 static const char *
397 stripath(const char *path)
398 {
399     const char *s;
400
401     return ((s = strrchr(path, '/')) != NULL && s[1] ? 
402                     s + 1 : path);
403 }
404
405 static int
406 distfile(const char *path)
407 {
408     const char *s;
409     int type;
410
411     if ((type = disttype(path)) == DISTMD5 ||
412         ((s = strrchr(path, '.')) != NULL && s > path &&
413          (type = disttype(s + 1)) != 0))
414         return type;
415     return 0;
416 }
417
418 static int
419 disttype(const char *name)
420 {
421     static const char dname[DISTTYPES][4] = {"md5", "inf"};
422     int i;
423
424     for (i = 0; i < DISTTYPES; i++)
425         if (!strcmp(dname[i], name))
426             return 1 + i;
427     return 0;
428 }
429
430 static int
431 fail(const char *path, const char *msg)
432 {
433     if (opt_silent)
434         return 0;
435     warnx("%s: %s", path, msg ? msg : sys_errlist[errno]);
436     return 2;
437 }
438
439 static void
440 usage(void)
441 {
442     fprintf(stderr,
443             "usage: ckdist [-airsx] [-d dir] [-n name] [-t type] file ...\n");
444     exit(2);
445 }