2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright (c) 1997 Robert Nordier
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
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.
30 #include <sys/types.h>
44 extern int crc(int fd, uint32_t *cval, off_t *clen);
46 #define DISTMD5 1 /* MD5 format */
47 #define DISTINF 2 /* .inf format */
48 #define DISTTYPES 2 /* types supported */
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] */
58 #define isfatal(err) ((err) && (err) <= E_NAME)
60 #define NAMESIZE 256 /* filename buffer size */
61 #define MDSUMLEN 32 /* length of MD5 message digest */
63 #define isstdin(path) ((path)[0] == '-' && !(path)[1])
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 */
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,
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;
87 main(int argc, char *argv[])
95 while ((c = getopt(argc, argv, "ad:in:rst:x")) != -1)
116 if ((opt_type = disttype(optarg)) == 0) {
117 warnx("illegal argument to -t option");
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);
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);
147 if ((ftsp = fts_open(arg, FTS_LOGICAL, NULL)) == NULL)
149 while (errno = 0, (f = fts_read(ftsp)) != NULL)
150 switch (f->fts_info) {
152 rval = fail(f->fts_path, "Directory causes a cycle");
157 rval = fail(f->fts_path, sys_errlist[f->fts_errno]);
160 if (!opt_recurse && f->fts_level > FTS_ROOTLEVEL &&
161 fts_set(ftsp, f, FTS_SKIP))
165 if ((type = distfile(f->fts_name)) != 0 &&
166 (!opt_type || type == opt_type))
167 rval |= ckdist(f->fts_path, type);
181 ckdist(const char *path, int type)
189 } else if ((fp = fopen(path, "r")) == NULL)
190 return fail(path, NULL);
193 type = distfile(path);
195 if ((c = fgetc(fp)) != EOF) {
196 type = c == 'M' ? DISTMD5 : c == 'P' ? DISTINF : 0;
202 rval = chkmd5(fp, path);
205 rval = chkinf(fp, path);
208 rval = report(path, NULL, E_UNKNOWN);
212 if (fp != stdin && fclose(fp))
218 chkmd5(FILE * fp, const char *path)
220 char buf[298]; /* "MD5 (NAMESIZE = MDSUMLEN" */
221 char name[NAMESIZE + 1];
222 char sum[MDSUMLEN + 1], chk[MDSUMLEN + 1];
225 int rval, error, c, fd;
229 while (fgets(buf, sizeof(buf), fp)) {
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)
240 if ((dname = distname(path, name, NULL)) == NULL)
242 else if (opt_exist) {
243 if ((fd = open(dname, O_RDONLY)) == -1)
247 } else if (!MD5File(dname, chk))
249 else if (strcmp(chk, sum))
252 if (opt_ignore && error == E_ERRNO && errno == ENOENT)
254 if (error || opt_all)
255 rval |= report(path, dname, error);
263 chkinf(FILE * fp, const char *path)
265 char buf[30]; /* "cksum.2 = 10 6" */
273 int rval, error, c, pieces, cnt, fd;
277 for (cnt = -1; fgets(buf, sizeof(buf), fp); cnt++) {
282 if ((c = sscanf(buf, "Pieces = %d%c", &pieces, &ch)) != 2 ||
283 ch != '\n' || pieces < 1)
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)
290 else if ((dname = distname(fp == stdin ? NULL : path, NULL,
293 else if ((fd = open(dname, O_RDONLY)) == -1)
295 else if (fstat(fd, &sb))
297 else if (sb.st_size != (off_t)sumlen)
299 else if (!opt_exist) {
300 if (crc(fd, &chk, &len))
305 if (fd != -1 && close(fd))
307 if (opt_ignore && error == E_ERRNO && errno == ENOENT)
309 if (error || (opt_all && cnt >= 0))
310 rval |= report(path, dname, error);
318 report(const char *path, const char *name, int error)
321 name = stripath(name);
324 printf("%s: Unknown format\n", path);
327 printf("%s: Invalid MD5 format\n", path);
330 printf("%s: Invalid .inf format\n", path);
333 printf("%s: Can't derive component name\n", path);
336 printf("%s: %s: Size mismatch\n", path, name);
339 printf("%s: %s: Checksum mismatch\n", path, name);
342 printf("%s: %s: %s\n", path, name, sys_errlist[errno]);
345 printf("%s: %s: OK\n", path, name);
351 distname(const char *path, const char *name, const char *ext)
353 static char buf[NAMESIZE];
362 name = stripath(path);
365 if (ext && nlen > 4 && name[nlen - 4] == '.' &&
366 disttype(name + nlen - 3) == DISTINF)
372 plen = path && (s = strrchr(path, '/')) != NULL ?
373 (size_t)(s - path) : 0;
374 if (plen + (plen > 0) + nlen + (ext ? 3 : 0) >= sizeof(buf))
378 memcpy(s, path, plen);
382 memcpy(s, name, nlen);
394 stripath(const char *path)
398 return ((s = strrchr(path, '/')) != NULL && s[1] ?
403 distfile(const char *path)
408 if ((type = disttype(path)) == DISTMD5 ||
409 ((s = strrchr(path, '.')) != NULL && s > path &&
410 (type = disttype(s + 1)) != 0))
416 disttype(const char *name)
418 static const char dname[DISTTYPES][4] = {"md5", "inf"};
421 for (i = 0; i < DISTTYPES; i++)
422 if (!strcmp(dname[i], name))
428 fail(const char *path, const char *msg)
432 warnx("%s: %s", path, msg ? msg : sys_errlist[errno]);
440 "usage: ckdist [-airsx] [-d dir] [-n name] [-t type] file ...\n");