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