Merge commit 'brent/register_phys_mem'
[freebsd-src/fkvm-freebsd.git] / usr.sbin / ckdist / ckdist.c
blob92bcd0d81a43e78e34530776e8dcbcd6b079e498
1 /*
2 * Copyright (c) 1997 Robert Nordier
3 * All rights reserved.
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.
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.
28 #ifndef lint
29 static const char rcsid[] =
30 "$FreeBSD$";
31 #endif /* not lint */
33 #include <sys/types.h>
34 #include <sys/stat.h>
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>
47 extern int crc(int fd, uint32_t *cval, off_t *clen);
49 #define DISTMD5 1 /* MD5 format */
50 #define DISTINF 2 /* .inf format */
51 #define DISTTYPES 2 /* types supported */
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] */
61 #define isfatal(err) ((err) && (err) <= E_NAME)
63 #define NAMESIZE 256 /* filename buffer size */
64 #define MDSUMLEN 32 /* length of MD5 message digest */
66 #define isstdin(path) ((path)[0] == '-' && !(path)[1])
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 */
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 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);
89 int
90 main(int argc, char *argv[])
92 static char *arg[2];
93 struct stat sb;
94 FTS *ftsp;
95 FTSENT *f;
96 int rval, c, type;
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();
123 break;
124 case 'x':
125 opt_exist = 1;
126 break;
127 default:
128 usage();
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);
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: ;
174 if (errno)
175 err(2, "fts_read");
176 if (fts_close(ftsp))
177 err(2, "fts_close");
179 } while (*++argv);
180 return rval;
183 static int
184 ckdist(const char *path, int type)
186 FILE *fp;
187 int rval, c;
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);
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);
213 if (ferror(fp))
214 warn("%s", path);
215 if (fp != stdin && fclose(fp))
216 err(2, "%s", path);
217 return rval;
220 static int
221 chkmd5(FILE * fp, const char *path)
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;
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((char *)dname, chk))
251 error = E_ERRNO;
252 else if (strcmp(chk, sum))
253 error = E_CHKSUM;
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;
262 return rval;
265 static int
266 chkinf(FILE * fp, const char *path)
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;
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;
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;
317 return rval;
320 static int
321 report(const char *path, const char *name, int error)
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);
350 return error != 0;
353 static const char *
354 distname(const char *path, const char *name, const char *ext)
356 static char buf[NAMESIZE];
357 size_t plen, nlen;
358 char *s;
360 if (opt_name)
361 name = opt_name;
362 else if (!name) {
363 if (!path)
364 return NULL;
365 name = stripath(path);
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++ = '/';
385 memcpy(s, name, nlen);
386 s += nlen;
387 if (ext) {
388 *s++ = '.';
389 memcpy(s, ext, 2);
390 s += 2;
392 *s = 0;
393 return buf;
396 static char *
397 stripath(const char *path)
399 const char *s;
401 return (char *)((s = strrchr(path, '/')) != NULL && s[1] ?
402 s + 1 : path);
405 static int
406 distfile(const char *path)
408 const char *s;
409 int type;
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;
418 static int
419 disttype(const char *name)
421 static const char dname[DISTTYPES][4] = {"md5", "inf"};
422 int i;
424 for (i = 0; i < DISTTYPES; i++)
425 if (!strcmp(dname[i], name))
426 return 1 + i;
427 return 0;
430 static int
431 fail(const char *path, const char *msg)
433 if (opt_silent)
434 return 0;
435 warnx("%s: %s", path, msg ? msg : sys_errlist[errno]);
436 return 2;
439 static void
440 usage(void)
442 fprintf(stderr,
443 "usage: ckdist [-airsx] [-d dir] [-n name] [-t type] file ...\n");
444 exit(2);