Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/usr.sbin/ckdist/ckdist.c
105584 views
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
}
443
444