aboutsummaryrefslogtreecommitdiff
path: root/usr.sbin/ckdist/ckdist.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/ckdist/ckdist.c')
-rw-r--r--usr.sbin/ckdist/ckdist.c442
1 files changed, 442 insertions, 0 deletions
diff --git a/usr.sbin/ckdist/ckdist.c b/usr.sbin/ckdist/ckdist.c
new file mode 100644
index 000000000000..ccd3a7ddf298
--- /dev/null
+++ b/usr.sbin/ckdist/ckdist.c
@@ -0,0 +1,442 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 1997 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fts.h>
+#include <md5.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+extern int crc(int fd, uint32_t *cval, off_t *clen);
+
+#define DISTMD5 1 /* MD5 format */
+#define DISTINF 2 /* .inf format */
+#define DISTTYPES 2 /* types supported */
+
+#define E_UNKNOWN 1 /* Unknown format */
+#define E_BADMD5 2 /* Invalid MD5 format */
+#define E_BADINF 3 /* Invalid .inf format */
+#define E_NAME 4 /* Can't derive component name */
+#define E_LENGTH 5 /* Length mismatch */
+#define E_CHKSUM 6 /* Checksum mismatch */
+#define E_ERRNO 7 /* sys_errlist[errno] */
+
+#define isfatal(err) ((err) && (err) <= E_NAME)
+
+#define NAMESIZE 256 /* filename buffer size */
+#define MDSUMLEN 32 /* length of MD5 message digest */
+
+#define isstdin(path) ((path)[0] == '-' && !(path)[1])
+
+static const char *opt_dir; /* where to look for components */
+static const char *opt_name; /* name for accessing components */
+static int opt_all; /* report on all components */
+static int opt_ignore; /* ignore missing components */
+static int opt_recurse; /* search directories recursively */
+static int opt_silent; /* silent about inaccessible files */
+static int opt_type; /* dist type: md5 or inf */
+static int opt_exist; /* just verify existence */
+
+static int ckdist(const char *path, int type);
+static int chkmd5(FILE * fp, const char *path);
+static int chkinf(FILE * fp, const char *path);
+static int report(const char *path, const char *name, int error);
+static const char *distname(const char *path, const char *name,
+ const char *ext);
+static const char *stripath(const char *path);
+static int distfile(const char *path);
+static int disttype(const char *name);
+static int fail(const char *path, const char *msg);
+static void usage(void) __dead2;
+
+int
+main(int argc, char *argv[])
+{
+ static char *arg[2];
+ struct stat sb;
+ FTS *ftsp;
+ FTSENT *f;
+ int rval, c, type;
+
+ while ((c = getopt(argc, argv, "ad:in:rst:x")) != -1)
+ switch (c) {
+ case 'a':
+ opt_all = 1;
+ break;
+ case 'd':
+ opt_dir = optarg;
+ break;
+ case 'i':
+ opt_ignore = 1;
+ break;
+ case 'n':
+ opt_name = optarg;
+ break;
+ case 'r':
+ opt_recurse = 1;
+ break;
+ case 's':
+ opt_silent = 1;
+ break;
+ case 't':
+ if ((opt_type = disttype(optarg)) == 0) {
+ warnx("illegal argument to -t option");
+ usage();
+ }
+ break;
+ case 'x':
+ opt_exist = 1;
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc < 1)
+ usage();
+ if (opt_dir) {
+ if (stat(opt_dir, &sb))
+ err(2, "%s", opt_dir);
+ if (!S_ISDIR(sb.st_mode))
+ errx(2, "%s: not a directory", opt_dir);
+ }
+ rval = 0;
+ do {
+ if (isstdin(*argv))
+ rval |= ckdist(*argv, opt_type);
+ else if (stat(*argv, &sb))
+ rval |= fail(*argv, NULL);
+ else if (S_ISREG(sb.st_mode))
+ rval |= ckdist(*argv, opt_type);
+ else {
+ arg[0] = *argv;
+ if ((ftsp = fts_open(arg, FTS_LOGICAL, NULL)) == NULL)
+ err(2, "fts_open");
+ while (errno = 0, (f = fts_read(ftsp)) != NULL)
+ switch (f->fts_info) {
+ case FTS_DC:
+ rval = fail(f->fts_path, "Directory causes a cycle");
+ break;
+ case FTS_DNR:
+ case FTS_ERR:
+ case FTS_NS:
+ rval = fail(f->fts_path, sys_errlist[f->fts_errno]);
+ break;
+ case FTS_D:
+ if (!opt_recurse && f->fts_level > FTS_ROOTLEVEL &&
+ fts_set(ftsp, f, FTS_SKIP))
+ err(2, "fts_set");
+ break;
+ case FTS_F:
+ if ((type = distfile(f->fts_name)) != 0 &&
+ (!opt_type || type == opt_type))
+ rval |= ckdist(f->fts_path, type);
+ break;
+ default: ;
+ }
+ if (errno)
+ err(2, "fts_read");
+ if (fts_close(ftsp))
+ err(2, "fts_close");
+ }
+ } while (*++argv);
+ return rval;
+}
+
+static int
+ckdist(const char *path, int type)
+{
+ FILE *fp;
+ int rval, c;
+
+ if (isstdin(path)) {
+ path = "(stdin)";
+ fp = stdin;
+ } else if ((fp = fopen(path, "r")) == NULL)
+ return fail(path, NULL);
+ if (!type) {
+ if (fp != stdin)
+ type = distfile(path);
+ if (!type)
+ if ((c = fgetc(fp)) != EOF) {
+ type = c == 'M' ? DISTMD5 : c == 'P' ? DISTINF : 0;
+ (void)ungetc(c, fp);
+ }
+ }
+ switch (type) {
+ case DISTMD5:
+ rval = chkmd5(fp, path);
+ break;
+ case DISTINF:
+ rval = chkinf(fp, path);
+ break;
+ default:
+ rval = report(path, NULL, E_UNKNOWN);
+ }
+ if (ferror(fp))
+ warn("%s", path);
+ if (fp != stdin && fclose(fp))
+ err(2, "%s", path);
+ return rval;
+}
+
+static int
+chkmd5(FILE * fp, const char *path)
+{
+ char buf[298]; /* "MD5 (NAMESIZE = MDSUMLEN" */
+ char name[NAMESIZE + 1];
+ char sum[MDSUMLEN + 1], chk[MDSUMLEN + 1];
+ const char *dname;
+ char *s;
+ int rval, error, c, fd;
+ char ch;
+
+ rval = 0;
+ while (fgets(buf, sizeof(buf), fp)) {
+ dname = NULL;
+ error = 0;
+ if (((c = sscanf(buf, "MD5 (%256s = %32s%c", name, sum,
+ &ch)) != 3 && (!feof(fp) || c != 2)) ||
+ (c == 3 && ch != '\n') ||
+ (s = strrchr(name, ')')) == NULL ||
+ strlen(sum) != MDSUMLEN)
+ error = E_BADMD5;
+ else {
+ *s = 0;
+ if ((dname = distname(path, name, NULL)) == NULL)
+ error = E_NAME;
+ else if (opt_exist) {
+ if ((fd = open(dname, O_RDONLY)) == -1)
+ error = E_ERRNO;
+ else if (close(fd))
+ err(2, "%s", dname);
+ } else if (!MD5File(dname, chk))
+ error = E_ERRNO;
+ else if (strcmp(chk, sum))
+ error = E_CHKSUM;
+ }
+ if (opt_ignore && error == E_ERRNO && errno == ENOENT)
+ continue;
+ if (error || opt_all)
+ rval |= report(path, dname, error);
+ if (isfatal(error))
+ break;
+ }
+ return rval;
+}
+
+static int
+chkinf(FILE * fp, const char *path)
+{
+ char buf[30]; /* "cksum.2 = 10 6" */
+ char ext[3];
+ struct stat sb;
+ const char *dname;
+ off_t len;
+ u_long sum;
+ intmax_t sumlen;
+ uint32_t chk;
+ int rval, error, c, pieces, cnt, fd;
+ char ch;
+
+ rval = 0;
+ for (cnt = -1; fgets(buf, sizeof(buf), fp); cnt++) {
+ fd = -1;
+ dname = NULL;
+ error = 0;
+ if (cnt == -1) {
+ if ((c = sscanf(buf, "Pieces = %d%c", &pieces, &ch)) != 2 ||
+ ch != '\n' || pieces < 1)
+ error = E_BADINF;
+ } else if (((c = sscanf(buf, "cksum.%2s = %lu %jd%c", ext, &sum,
+ &sumlen, &ch)) != 4 &&
+ (!feof(fp) || c != 3)) || (c == 4 && ch != '\n') ||
+ ext[0] != 'a' + cnt / 26 || ext[1] != 'a' + cnt % 26)
+ error = E_BADINF;
+ else if ((dname = distname(fp == stdin ? NULL : path, NULL,
+ ext)) == NULL)
+ error = E_NAME;
+ else if ((fd = open(dname, O_RDONLY)) == -1)
+ error = E_ERRNO;
+ else if (fstat(fd, &sb))
+ error = E_ERRNO;
+ else if (sb.st_size != (off_t)sumlen)
+ error = E_LENGTH;
+ else if (!opt_exist) {
+ if (crc(fd, &chk, &len))
+ error = E_ERRNO;
+ else if (chk != sum)
+ error = E_CHKSUM;
+ }
+ if (fd != -1 && close(fd))
+ err(2, "%s", dname);
+ if (opt_ignore && error == E_ERRNO && errno == ENOENT)
+ continue;
+ if (error || (opt_all && cnt >= 0))
+ rval |= report(path, dname, error);
+ if (isfatal(error))
+ break;
+ }
+ return rval;
+}
+
+static int
+report(const char *path, const char *name, int error)
+{
+ if (name)
+ name = stripath(name);
+ switch (error) {
+ case E_UNKNOWN:
+ printf("%s: Unknown format\n", path);
+ break;
+ case E_BADMD5:
+ printf("%s: Invalid MD5 format\n", path);
+ break;
+ case E_BADINF:
+ printf("%s: Invalid .inf format\n", path);
+ break;
+ case E_NAME:
+ printf("%s: Can't derive component name\n", path);
+ break;
+ case E_LENGTH:
+ printf("%s: %s: Size mismatch\n", path, name);
+ break;
+ case E_CHKSUM:
+ printf("%s: %s: Checksum mismatch\n", path, name);
+ break;
+ case E_ERRNO:
+ printf("%s: %s: %s\n", path, name, sys_errlist[errno]);
+ break;
+ default:
+ printf("%s: %s: OK\n", path, name);
+ }
+ return error != 0;
+}
+
+static const char *
+distname(const char *path, const char *name, const char *ext)
+{
+ static char buf[NAMESIZE];
+ size_t plen, nlen;
+ char *s;
+
+ if (opt_name)
+ name = opt_name;
+ else if (!name) {
+ if (!path)
+ return NULL;
+ name = stripath(path);
+ }
+ nlen = strlen(name);
+ if (ext && nlen > 4 && name[nlen - 4] == '.' &&
+ disttype(name + nlen - 3) == DISTINF)
+ nlen -= 4;
+ if (opt_dir) {
+ path = opt_dir;
+ plen = strlen(path);
+ } else
+ plen = path && (s = strrchr(path, '/')) != NULL ?
+ (size_t)(s - path) : 0;
+ if (plen + (plen > 0) + nlen + (ext ? 3 : 0) >= sizeof(buf))
+ return NULL;
+ s = buf;
+ if (plen) {
+ memcpy(s, path, plen);
+ s += plen;
+ *s++ = '/';
+ }
+ memcpy(s, name, nlen);
+ s += nlen;
+ if (ext) {
+ *s++ = '.';
+ memcpy(s, ext, 2);
+ s += 2;
+ }
+ *s = 0;
+ return buf;
+}
+
+static const char *
+stripath(const char *path)
+{
+ const char *s;
+
+ return ((s = strrchr(path, '/')) != NULL && s[1] ?
+ s + 1 : path);
+}
+
+static int
+distfile(const char *path)
+{
+ const char *s;
+ int type;
+
+ if ((type = disttype(path)) == DISTMD5 ||
+ ((s = strrchr(path, '.')) != NULL && s > path &&
+ (type = disttype(s + 1)) != 0))
+ return type;
+ return 0;
+}
+
+static int
+disttype(const char *name)
+{
+ static const char dname[DISTTYPES][4] = {"md5", "inf"};
+ int i;
+
+ for (i = 0; i < DISTTYPES; i++)
+ if (!strcmp(dname[i], name))
+ return 1 + i;
+ return 0;
+}
+
+static int
+fail(const char *path, const char *msg)
+{
+ if (opt_silent)
+ return 0;
+ warnx("%s: %s", path, msg ? msg : sys_errlist[errno]);
+ return 2;
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "usage: ckdist [-airsx] [-d dir] [-n name] [-t type] file ...\n");
+ exit(2);
+}