2 * Copyright (c) 1997 Robert Nordier
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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
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.
27 * $FreeBSD: src/usr.sbin/ckdist/ckdist.c,v 1.3.2.1 2000/07/01 10:37:21 ps Exp $
28 * $DragonFly: src/usr.sbin/ckdist/ckdist.c,v 1.3 2004/12/18 22:48:02 swildner Exp $
31 #include <sys/types.h>
43 extern int crc(int fd
, u_long
* cval
, u_long
* clen
);
45 #define DISTMD5 1 /* MD5 format */
46 #define DISTINF 2 /* .inf format */
47 #define DISTTYPES 2 /* types supported */
49 #define E_UNKNOWN 1 /* Unknown format */
50 #define E_BADMD5 2 /* Invalid MD5 format */
51 #define E_BADINF 3 /* Invalid .inf format */
52 #define E_NAME 4 /* Can't derive component name */
53 #define E_LENGTH 5 /* Length mismatch */
54 #define E_CHKSUM 6 /* Checksum mismatch */
55 #define E_ERRNO 7 /* sys_errlist[errno] */
57 #define isfatal(err) ((err) && (err) <= E_NAME)
59 #define NAMESIZE 256 /* filename buffer size */
60 #define MDSUMLEN 32 /* length of MD5 message digest */
62 #define isstdin(path) ((path)[0] == '-' && !(path)[1])
64 static const char *opt_dir
; /* where to look for components */
65 static const char *opt_name
; /* name for accessing components */
66 static int opt_all
; /* report on all components */
67 static int opt_ignore
; /* ignore missing components */
68 static int opt_recurse
; /* search directories recursively */
69 static int opt_silent
; /* silent about inaccessible files */
70 static int opt_type
; /* dist type: md5 or inf */
71 static int opt_exist
; /* just verify existence */
73 static int ckdist(const char *path
, int type
);
74 static int chkmd5(FILE * fp
, const char *path
);
75 static int chkinf(FILE * fp
, const char *path
);
76 static int report(const char *path
, const char *name
, int error
);
77 static const char *distname(const char *path
, const char *name
,
79 static char *stripath(const char *path
);
80 static int distfile(const char *path
);
81 static int disttype(const char *name
);
82 static int fail(const char *path
, const char *msg
);
83 static void usage(void);
86 main(int argc
, char *argv
[])
94 while ((c
= getopt(argc
, argv
, "ad:in:rst:x")) != -1)
115 if ((opt_type
= disttype(optarg
)) == 0) {
116 warnx("illegal argument to -t option");
131 if (stat(opt_dir
, &sb
))
132 err(2, "%s", opt_dir
);
133 if (!S_ISDIR(sb
.st_mode
))
134 errx(2, "%s: not a directory", opt_dir
);
139 rval
|= ckdist(*argv
, opt_type
);
140 else if (stat(*argv
, &sb
))
141 rval
|= fail(*argv
, NULL
);
142 else if (S_ISREG(sb
.st_mode
))
143 rval
|= ckdist(*argv
, opt_type
);
146 if ((ftsp
= fts_open(arg
, FTS_LOGICAL
, NULL
)) == NULL
)
148 while ((f
= fts_read(ftsp
)) != NULL
)
149 switch (f
->fts_info
) {
151 rval
= fail(f
->fts_path
, "Directory causes a cycle");
156 rval
= fail(f
->fts_path
, sys_errlist
[f
->fts_errno
]);
159 if (!opt_recurse
&& f
->fts_level
> FTS_ROOTLEVEL
&&
160 fts_set(ftsp
, f
, FTS_SKIP
))
164 if ((type
= distfile(f
->fts_name
)) != 0 &&
165 (!opt_type
|| type
== opt_type
))
166 rval
|= ckdist(f
->fts_path
, type
);
180 ckdist(const char *path
, int type
)
188 } else if ((fp
= fopen(path
, "r")) == NULL
)
189 return fail(path
, NULL
);
192 type
= distfile(path
);
194 if ((c
= fgetc(fp
)) != EOF
) {
195 type
= c
== 'M' ? DISTMD5
: c
== 'P' ? DISTINF
: 0;
201 rval
= chkmd5(fp
, path
);
204 rval
= chkinf(fp
, path
);
207 rval
= report(path
, NULL
, E_UNKNOWN
);
211 if (fp
!= stdin
&& fclose(fp
))
217 chkmd5(FILE * fp
, const char *path
)
219 char buf
[298]; /* "MD5 (NAMESIZE = MDSUMLEN" */
220 char name
[NAMESIZE
+ 1];
221 char sum
[MDSUMLEN
+ 1], chk
[MDSUMLEN
+ 1];
224 int rval
, error
, c
, fd
;
228 while (fgets(buf
, sizeof(buf
), fp
)) {
231 if (((c
= sscanf(buf
, "MD5 (%256s = %32s%c", name
, sum
,
232 &ch
)) != 3 && (!feof(fp
) || c
!= 2)) ||
233 (c
== 3 && ch
!= '\n') ||
234 (s
= strrchr(name
, ')')) == NULL
||
235 strlen(sum
) != MDSUMLEN
)
239 if ((dname
= distname(path
, name
, NULL
)) == NULL
)
241 else if (opt_exist
) {
242 if ((fd
= open(dname
, O_RDONLY
)) == -1)
246 } else if (!MD5File((char *)dname
, chk
))
248 else if (strcmp(chk
, sum
))
251 if (opt_ignore
&& error
== E_ERRNO
&& errno
== ENOENT
)
253 if (error
|| opt_all
)
254 rval
|= report(path
, dname
, error
);
262 chkinf(FILE * fp
, const char *path
)
264 char buf
[30]; /* "cksum.2 = 10 6" */
268 u_long sum
, len
, chk
;
269 int rval
, error
, c
, pieces
, cnt
, fd
;
273 for (cnt
= -1; fgets(buf
, sizeof(buf
), fp
); cnt
++) {
278 if ((c
= sscanf(buf
, "Pieces = %d%c", &pieces
, &ch
)) != 2 ||
279 ch
!= '\n' || pieces
< 1)
281 } else if (((c
= sscanf(buf
, "cksum.%2s = %lu %lu%c", ext
, &sum
,
283 (!feof(fp
) || c
!= 3)) || (c
== 4 && ch
!= '\n') ||
284 ext
[0] != 'a' + cnt
/ 26 || ext
[1] != 'a' + cnt
% 26)
286 else if ((dname
= distname(fp
== stdin
? NULL
: path
, NULL
,
289 else if ((fd
= open(dname
, O_RDONLY
)) == -1)
291 else if (fstat(fd
, &sb
))
293 else if (sb
.st_size
!= (off_t
)len
)
295 else if (!opt_exist
) {
296 if (crc(fd
, &chk
, &len
))
301 if (fd
!= -1 && close(fd
))
303 if (opt_ignore
&& error
== E_ERRNO
&& errno
== ENOENT
)
305 if (error
|| (opt_all
&& cnt
>= 0))
306 rval
|= report(path
, dname
, error
);
314 report(const char *path
, const char *name
, int error
)
317 name
= stripath(name
);
320 printf("%s: Unknown format\n", path
);
323 printf("%s: Invalid MD5 format\n", path
);
326 printf("%s: Invalid .inf format\n", path
);
329 printf("%s: Can't derive component name\n", path
);
332 printf("%s: %s: Size mismatch\n", path
, name
);
335 printf("%s: %s: Checksum mismatch\n", path
, name
);
338 printf("%s: %s: %s\n", path
, name
, sys_errlist
[errno
]);
341 printf("%s: %s: OK\n", path
, name
);
347 distname(const char *path
, const char *name
, const char *ext
)
349 static char buf
[NAMESIZE
];
358 name
= stripath(path
);
361 if (ext
&& nlen
> 4 && name
[nlen
- 4] == '.' &&
362 disttype(name
+ nlen
- 3) == DISTINF
)
368 plen
= path
&& (s
= strrchr(path
, '/')) != NULL
?
369 (size_t)(s
- path
) : 0;
370 if (plen
+ (plen
> 0) + nlen
+ (ext
? 3 : 0) >= sizeof(buf
))
374 memcpy(s
, path
, plen
);
378 memcpy(s
, name
, nlen
);
390 stripath(const char *path
)
394 return (char *)((s
= strrchr(path
, '/')) != NULL
&& s
[1] ?
399 distfile(const char *path
)
404 if ((type
= disttype(path
)) == DISTMD5
||
405 ((s
= strrchr(path
, '.')) != NULL
&& s
> path
&&
406 (type
= disttype(s
+ 1)) != 0))
412 disttype(const char *name
)
414 static const char dname
[DISTTYPES
][4] = {"md5", "inf"};
417 for (i
= 0; i
< DISTTYPES
; i
++)
418 if (!strcmp(dname
[i
], name
))
424 fail(const char *path
, const char *msg
)
428 warnx("%s: %s", path
, msg
? msg
: sys_errlist
[errno
]);
436 "usage: ckdist [-airsx] [-d dir] [-n name] [-t type] file ...\n");