ckdist.c revision 61019
121918Sjkh/* 221918Sjkh * Copyright (c) 1997 Robert Nordier 321918Sjkh * All rights reserved. 421918Sjkh * 521918Sjkh * Redistribution and use in source and binary forms, with or without 621918Sjkh * modification, are permitted provided that the following conditions 721918Sjkh * are met: 821918Sjkh * 1. Redistributions of source code must retain the above copyright 921918Sjkh * notice, this list of conditions and the following disclaimer. 1021918Sjkh * 2. Redistributions in binary form must reproduce the above copyright 1121918Sjkh * notice, this list of conditions and the following disclaimer in 1221918Sjkh * the documentation and/or other materials provided with the 1321918Sjkh * distribution. 1421918Sjkh * 1521918Sjkh * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS 1621918Sjkh * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 1721918Sjkh * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1821918Sjkh * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY 1921918Sjkh * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2021918Sjkh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 2121918Sjkh * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2221918Sjkh * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 2321918Sjkh * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 2421918Sjkh * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 2521918Sjkh * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2621918Sjkh */ 2721918Sjkh 2829449Scharnier#ifndef lint 2929449Scharnierstatic const char rcsid[] = 3050479Speter "$FreeBSD: head/usr.sbin/ckdist/ckdist.c 61019 2000-05-28 14:57:49Z charnier $"; 3129449Scharnier#endif /* not lint */ 3229449Scharnier 3321918Sjkh#include <sys/types.h> 3421918Sjkh#include <sys/stat.h> 3529449Scharnier#include <err.h> 3629449Scharnier#include <errno.h> 3721918Sjkh#include <fcntl.h> 3821918Sjkh#include <fts.h> 3921918Sjkh#include <md5.h> 4029449Scharnier#include <stdio.h> 4121918Sjkh#include <stdlib.h> 4221918Sjkh#include <string.h> 4329449Scharnier#include <unistd.h> 4421918Sjkh 4521918Sjkhextern int crc(int fd, u_long * cval, u_long * clen); 4621918Sjkh 4721918Sjkh#define DISTMD5 1 /* MD5 format */ 4821918Sjkh#define DISTINF 2 /* .inf format */ 4921918Sjkh#define DISTTYPES 2 /* types supported */ 5021918Sjkh 5121918Sjkh#define E_UNKNOWN 1 /* Unknown format */ 5221918Sjkh#define E_BADMD5 2 /* Invalid MD5 format */ 5321918Sjkh#define E_BADINF 3 /* Invalid .inf format */ 5421918Sjkh#define E_NAME 4 /* Can't derive component name */ 5521918Sjkh#define E_LENGTH 5 /* Length mismatch */ 5621918Sjkh#define E_CHKSUM 6 /* Checksum mismatch */ 5721918Sjkh#define E_ERRNO 7 /* sys_errlist[errno] */ 5821918Sjkh 5921918Sjkh#define isfatal(err) ((err) && (err) <= E_NAME) 6021918Sjkh 6121918Sjkh#define NAMESIZE 256 /* filename buffer size */ 6221918Sjkh#define MDSUMLEN 32 /* length of MD5 message digest */ 6321918Sjkh 6421918Sjkh#define isstdin(path) ((path)[0] == '-' && !(path)[1]) 6521918Sjkh 6621918Sjkhstatic const char *opt_dir; /* where to look for components */ 6721918Sjkhstatic const char *opt_name; /* name for accessing components */ 6821918Sjkhstatic int opt_all; /* report on all components */ 6921918Sjkhstatic int opt_ignore; /* ignore missing components */ 7021918Sjkhstatic int opt_recurse; /* search directories recursively */ 7121918Sjkhstatic int opt_silent; /* silent about inaccessible files */ 7221918Sjkhstatic int opt_type; /* dist type: md5 or inf */ 7321918Sjkhstatic int opt_exist; /* just verify existence */ 7421918Sjkh 7521918Sjkhstatic int ckdist(const char *path, int type); 7621918Sjkhstatic int chkmd5(FILE * fp, const char *path); 7721918Sjkhstatic int chkinf(FILE * fp, const char *path); 7821918Sjkhstatic int report(const char *path, const char *name, int error); 7921918Sjkhstatic const char *distname(const char *path, const char *name, 8021918Sjkh const char *ext); 8121918Sjkhstatic char *stripath(const char *path); 8221918Sjkhstatic int distfile(const char *path); 8321918Sjkhstatic int disttype(const char *name); 8421918Sjkhstatic int fail(const char *path, const char *msg); 8521918Sjkhstatic void usage(void); 8621918Sjkh 8721918Sjkhint 8821918Sjkhmain(int argc, char *argv[]) 8921918Sjkh{ 9021918Sjkh static char *arg[2]; 9121918Sjkh struct stat sb; 9221918Sjkh FTS *ftsp; 9321918Sjkh FTSENT *f; 9421918Sjkh int rval, c, type; 9521918Sjkh 9621918Sjkh while ((c = getopt(argc, argv, "ad:in:rst:x")) != -1) 9721918Sjkh switch (c) { 9821918Sjkh case 'a': 9921918Sjkh opt_all = 1; 10021918Sjkh break; 10121918Sjkh case 'd': 10221918Sjkh opt_dir = optarg; 10321918Sjkh break; 10421918Sjkh case 'i': 10521918Sjkh opt_ignore = 1; 10621918Sjkh break; 10721918Sjkh case 'n': 10821918Sjkh opt_name = optarg; 10921918Sjkh break; 11021918Sjkh case 'r': 11121918Sjkh opt_recurse = 1; 11221918Sjkh break; 11321918Sjkh case 's': 11421918Sjkh opt_silent = 1; 11521918Sjkh break; 11621918Sjkh case 't': 11721918Sjkh if ((opt_type = disttype(optarg)) == 0) { 11821918Sjkh warnx("illegal argument to -t option"); 11921918Sjkh usage(); 12021918Sjkh } 12121918Sjkh break; 12221918Sjkh case 'x': 12321918Sjkh opt_exist = 1; 12421918Sjkh break; 12521918Sjkh default: 12621918Sjkh usage(); 12721918Sjkh } 12821918Sjkh argc -= optind; 12921918Sjkh argv += optind; 13021918Sjkh if (argc < 1) 13121918Sjkh usage(); 13221918Sjkh if (opt_dir) { 13321918Sjkh if (stat(opt_dir, &sb)) 13461019Scharnier err(2, "%s", opt_dir); 13521918Sjkh if (!S_ISDIR(sb.st_mode)) 13629449Scharnier errx(2, "%s: not a directory", opt_dir); 13721918Sjkh } 13821918Sjkh rval = 0; 13921918Sjkh do { 14021918Sjkh if (isstdin(*argv)) 14121918Sjkh rval |= ckdist(*argv, opt_type); 14221918Sjkh else if (stat(*argv, &sb)) 14321918Sjkh rval |= fail(*argv, NULL); 14421918Sjkh else if (S_ISREG(sb.st_mode)) 14521918Sjkh rval |= ckdist(*argv, opt_type); 14621918Sjkh else { 14721918Sjkh arg[0] = *argv; 14821918Sjkh if ((ftsp = fts_open(arg, FTS_LOGICAL, NULL)) == NULL) 14921918Sjkh err(2, "fts_open"); 15021918Sjkh while ((f = fts_read(ftsp)) != NULL) 15121918Sjkh switch (f->fts_info) { 15221918Sjkh case FTS_DC: 15321918Sjkh rval = fail(f->fts_path, "Directory causes a cycle"); 15421918Sjkh break; 15521918Sjkh case FTS_DNR: 15621918Sjkh case FTS_ERR: 15721918Sjkh case FTS_NS: 15821918Sjkh rval = fail(f->fts_path, sys_errlist[f->fts_errno]); 15921918Sjkh break; 16021918Sjkh case FTS_D: 16121918Sjkh if (!opt_recurse && f->fts_level > FTS_ROOTLEVEL && 16221918Sjkh fts_set(ftsp, f, FTS_SKIP)) 16321918Sjkh err(2, "fts_set"); 16421918Sjkh break; 16521918Sjkh case FTS_F: 16621918Sjkh if ((type = distfile(f->fts_name)) != 0 && 16721918Sjkh (!opt_type || type == opt_type)) 16821918Sjkh rval |= ckdist(f->fts_path, type); 16921918Sjkh break; 17021918Sjkh default: ; 17121918Sjkh } 17221918Sjkh if (errno) 17321918Sjkh err(2, "fts_read"); 17421918Sjkh if (fts_close(ftsp)) 17521918Sjkh err(2, "fts_close"); 17621918Sjkh } 17721918Sjkh } while (*++argv); 17821918Sjkh return rval; 17921918Sjkh} 18021918Sjkh 18121918Sjkhstatic int 18221918Sjkhckdist(const char *path, int type) 18321918Sjkh{ 18421918Sjkh FILE *fp; 18521918Sjkh int rval, c; 18621918Sjkh 18721918Sjkh if (isstdin(path)) { 18821918Sjkh path = "(stdin)"; 18921918Sjkh fp = stdin; 19021918Sjkh } else if ((fp = fopen(path, "r")) == NULL) 19121918Sjkh return fail(path, NULL); 19221918Sjkh if (!type) { 19321918Sjkh if (fp != stdin) 19421918Sjkh type = distfile(path); 19521918Sjkh if (!type) 19621918Sjkh if ((c = fgetc(fp)) != EOF) { 19721918Sjkh type = c == 'M' ? DISTMD5 : c == 'P' ? DISTINF : 0; 19821918Sjkh (void)ungetc(c, fp); 19921918Sjkh } 20021918Sjkh } 20121918Sjkh switch (type) { 20221918Sjkh case DISTMD5: 20321918Sjkh rval = chkmd5(fp, path); 20421918Sjkh break; 20521918Sjkh case DISTINF: 20621918Sjkh rval = chkinf(fp, path); 20721918Sjkh break; 20821918Sjkh default: 20921918Sjkh rval = report(path, NULL, E_UNKNOWN); 21021918Sjkh } 21121918Sjkh if (ferror(fp)) 21261019Scharnier warn("%s", path); 21321918Sjkh if (fp != stdin && fclose(fp)) 21461019Scharnier err(2, "%s", path); 21521918Sjkh return rval; 21621918Sjkh} 21721918Sjkh 21821918Sjkhstatic int 21921918Sjkhchkmd5(FILE * fp, const char *path) 22021918Sjkh{ 22121918Sjkh char buf[298]; /* "MD5 (NAMESIZE = MDSUMLEN" */ 22221918Sjkh char name[NAMESIZE + 1]; 22321918Sjkh char sum[MDSUMLEN + 1], chk[MDSUMLEN + 1]; 22421918Sjkh const char *dname; 22521918Sjkh char *s; 22621918Sjkh int rval, error, c, fd; 22721918Sjkh char ch; 22821918Sjkh 22921918Sjkh rval = 0; 23021918Sjkh while (fgets(buf, sizeof(buf), fp)) { 23121918Sjkh dname = NULL; 23221918Sjkh error = 0; 23321918Sjkh if (((c = sscanf(buf, "MD5 (%256s = %32s%c", name, sum, 23421918Sjkh &ch)) != 3 && (!feof(fp) || c != 2)) || 23521918Sjkh (c == 3 && ch != '\n') || 23621918Sjkh (s = strrchr(name, ')')) == NULL || 23721918Sjkh strlen(sum) != MDSUMLEN) 23821918Sjkh error = E_BADMD5; 23921918Sjkh else { 24021918Sjkh *s = 0; 24121918Sjkh if ((dname = distname(path, name, NULL)) == NULL) 24221918Sjkh error = E_NAME; 24321918Sjkh else if (opt_exist) { 24421918Sjkh if ((fd = open(dname, O_RDONLY)) == -1) 24521918Sjkh error = E_ERRNO; 24621918Sjkh else if (close(fd)) 24761019Scharnier err(2, "%s", dname); 24821918Sjkh } else if (!MD5File((char *)dname, chk)) 24921918Sjkh error = E_ERRNO; 25021918Sjkh else if (strcmp(chk, sum)) 25121918Sjkh error = E_CHKSUM; 25221918Sjkh } 25321918Sjkh if (opt_ignore && error == E_ERRNO && errno == ENOENT) 25421918Sjkh continue; 25521918Sjkh if (error || opt_all) 25621918Sjkh rval |= report(path, dname, error); 25721918Sjkh if (isfatal(error)) 25821918Sjkh break; 25921918Sjkh } 26021918Sjkh return rval; 26121918Sjkh} 26221918Sjkh 26321918Sjkhstatic int 26421918Sjkhchkinf(FILE * fp, const char *path) 26521918Sjkh{ 26621918Sjkh char buf[30]; /* "cksum.2 = 10 6" */ 26721918Sjkh char ext[3]; 26821918Sjkh struct stat sb; 26921918Sjkh const char *dname; 27021918Sjkh u_long sum, len, chk; 27121918Sjkh int rval, error, c, pieces, cnt, fd; 27221918Sjkh char ch; 27321918Sjkh 27421918Sjkh rval = 0; 27521918Sjkh for (cnt = -1; fgets(buf, sizeof(buf), fp); cnt++) { 27621918Sjkh fd = -1; 27721918Sjkh dname = NULL; 27821918Sjkh error = 0; 27921918Sjkh if (cnt == -1) { 28021918Sjkh if ((c = sscanf(buf, "Pieces = %d%c", &pieces, &ch)) != 2 || 28121918Sjkh ch != '\n' || pieces < 1) 28221918Sjkh error = E_BADINF; 28321918Sjkh } else if (((c = sscanf(buf, "cksum.%2s = %lu %lu%c", ext, &sum, 28421918Sjkh &len, &ch)) != 4 && 28521918Sjkh (!feof(fp) || c != 3)) || (c == 4 && ch != '\n') || 28621918Sjkh ext[0] != 'a' + cnt / 26 || ext[1] != 'a' + cnt % 26) 28721918Sjkh error = E_BADINF; 28821918Sjkh else if ((dname = distname(fp == stdin ? NULL : path, NULL, 28921918Sjkh ext)) == NULL) 29021918Sjkh error = E_NAME; 29121918Sjkh else if ((fd = open(dname, O_RDONLY)) == -1) 29221918Sjkh error = E_ERRNO; 29321918Sjkh else if (fstat(fd, &sb)) 29421918Sjkh error = E_ERRNO; 29521918Sjkh else if (sb.st_size != (off_t)len) 29621918Sjkh error = E_LENGTH; 29721918Sjkh else if (!opt_exist) { 29821918Sjkh if (crc(fd, &chk, &len)) 29921918Sjkh error = E_ERRNO; 30021918Sjkh else if (chk != sum) 30121918Sjkh error = E_CHKSUM; 30221918Sjkh } 30321918Sjkh if (fd != -1 && close(fd)) 30461019Scharnier err(2, "%s", dname); 30521918Sjkh if (opt_ignore && error == E_ERRNO && errno == ENOENT) 30621918Sjkh continue; 30721918Sjkh if (error || (opt_all && cnt >= 0)) 30821918Sjkh rval |= report(path, dname, error); 30921918Sjkh if (isfatal(error)) 31021918Sjkh break; 31121918Sjkh } 31221918Sjkh return rval; 31321918Sjkh} 31421918Sjkh 31521918Sjkhstatic int 31621918Sjkhreport(const char *path, const char *name, int error) 31721918Sjkh{ 31821918Sjkh if (name) 31921918Sjkh name = stripath(name); 32021918Sjkh switch (error) { 32121918Sjkh case E_UNKNOWN: 32221918Sjkh printf("%s: Unknown format\n", path); 32321918Sjkh break; 32421918Sjkh case E_BADMD5: 32521918Sjkh printf("%s: Invalid MD5 format\n", path); 32621918Sjkh break; 32721918Sjkh case E_BADINF: 32821918Sjkh printf("%s: Invalid .inf format\n", path); 32921918Sjkh break; 33021918Sjkh case E_NAME: 33121918Sjkh printf("%s: Can't derive component name\n", path); 33221918Sjkh break; 33321918Sjkh case E_LENGTH: 33421918Sjkh printf("%s: %s: Size mismatch\n", path, name); 33521918Sjkh break; 33621918Sjkh case E_CHKSUM: 33721918Sjkh printf("%s: %s: Checksum mismatch\n", path, name); 33821918Sjkh break; 33921918Sjkh case E_ERRNO: 34021918Sjkh printf("%s: %s: %s\n", path, name, sys_errlist[errno]); 34121918Sjkh break; 34221918Sjkh default: 34321918Sjkh printf("%s: %s: OK\n", path, name); 34421918Sjkh } 34521918Sjkh return error != 0; 34621918Sjkh} 34721918Sjkh 34821918Sjkhstatic const char * 34921918Sjkhdistname(const char *path, const char *name, const char *ext) 35021918Sjkh{ 35121918Sjkh static char buf[NAMESIZE]; 35221918Sjkh size_t plen, nlen; 35321918Sjkh char *s; 35421918Sjkh 35521918Sjkh if (opt_name) 35621918Sjkh name = opt_name; 35721918Sjkh else if (!name) { 35821918Sjkh if (!path) 35921918Sjkh return NULL; 36021918Sjkh name = stripath(path); 36121918Sjkh } 36221918Sjkh nlen = strlen(name); 36321918Sjkh if (ext && nlen > 4 && name[nlen - 4] == '.' && 36421918Sjkh disttype(name + nlen - 3) == DISTINF) 36521918Sjkh nlen -= 4; 36621918Sjkh if (opt_dir) { 36721918Sjkh path = opt_dir; 36821918Sjkh plen = strlen(path); 36921918Sjkh } else 37021918Sjkh plen = path && (s = strrchr(path, '/')) != NULL ? 37121918Sjkh (size_t)(s - path) : 0; 37221918Sjkh if (plen + (plen > 0) + nlen + (ext ? 3 : 0) >= sizeof(buf)) 37321918Sjkh return NULL; 37421918Sjkh s = buf; 37521918Sjkh if (plen) { 37621918Sjkh memcpy(s, path, plen); 37721918Sjkh s += plen; 37821918Sjkh *s++ = '/'; 37921918Sjkh } 38021918Sjkh memcpy(s, name, nlen); 38121918Sjkh s += nlen; 38221918Sjkh if (ext) { 38321918Sjkh *s++ = '.'; 38421918Sjkh memcpy(s, ext, 2); 38521918Sjkh s += 2; 38621918Sjkh } 38721918Sjkh *s = 0; 38821918Sjkh return buf; 38921918Sjkh} 39021918Sjkh 39121918Sjkhstatic char * 39221918Sjkhstripath(const char *path) 39321918Sjkh{ 39421918Sjkh const char *s; 39521918Sjkh 39621918Sjkh return (char *)((s = strrchr(path, '/')) != NULL && s[1] ? 39721918Sjkh s + 1 : path); 39821918Sjkh} 39921918Sjkh 40021918Sjkhstatic int 40121918Sjkhdistfile(const char *path) 40221918Sjkh{ 40321918Sjkh const char *s; 40421918Sjkh int type; 40521918Sjkh 40621918Sjkh if ((type = disttype(path)) == DISTMD5 || 40721918Sjkh ((s = strrchr(path, '.')) != NULL && s > path && 40821918Sjkh (type = disttype(s + 1)) != 0)) 40921918Sjkh return type; 41021918Sjkh return 0; 41121918Sjkh} 41221918Sjkh 41321918Sjkhstatic int 41421918Sjkhdisttype(const char *name) 41521918Sjkh{ 41621918Sjkh static const char dname[DISTTYPES][4] = {"md5", "inf"}; 41721918Sjkh int i; 41821918Sjkh 41921918Sjkh for (i = 0; i < DISTTYPES; i++) 42021918Sjkh if (!strcmp(dname[i], name)) 42121918Sjkh return 1 + i; 42221918Sjkh return 0; 42321918Sjkh} 42421918Sjkh 42521918Sjkhstatic int 42621918Sjkhfail(const char *path, const char *msg) 42721918Sjkh{ 42821918Sjkh if (opt_silent) 42921918Sjkh return 0; 43021918Sjkh warnx("%s: %s", path, msg ? msg : sys_errlist[errno]); 43121918Sjkh return 2; 43221918Sjkh} 43321918Sjkh 43421918Sjkhstatic void 43521918Sjkhusage(void) 43621918Sjkh{ 43721918Sjkh fprintf(stderr, 43829449Scharnier "usage: ckdist [-airsx] [-d dir] [-n name] [-t type] file ...\n"); 43921918Sjkh exit(2); 44021918Sjkh} 441