compare.c revision 112214
1/*-
2 * Copyright (c) 1989, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
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 the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35#if 0
36static char sccsid[] = "@(#)compare.c	8.1 (Berkeley) 6/6/93";
37#endif
38static const char rcsid[] =
39  "$FreeBSD: head/usr.sbin/mtree/compare.c 112214 2003-03-13 23:35:30Z robert $";
40#endif /* not lint */
41
42#include <sys/param.h>
43#include <sys/stat.h>
44#include <err.h>
45#include <errno.h>
46#include <fcntl.h>
47#include <fts.h>
48#ifdef MD5
49#include <md5.h>
50#endif
51#ifdef SHA1
52#include <sha.h>
53#endif
54#ifdef RMD160
55#include <ripemd.h>
56#endif
57#include <stdint.h>
58#include <stdio.h>
59#include <time.h>
60#include <unistd.h>
61#include "mtree.h"
62#include "extern.h"
63
64extern int uflag;
65extern int lineno;
66
67static const char *ftype(u_int);
68
69#define	INDENTNAMELEN	8
70#define	LABEL \
71	if (!label++) { \
72		len = printf("%s changed\n", RP(p)); \
73		tab = "\t"; \
74	}
75
76int
77compare(name, s, p)
78	char *name __unused;
79	register NODE *s;
80	register FTSENT *p;
81{
82	uint32_t val;
83	int fd, label;
84	off_t len;
85	char *cp;
86	const char *tab = "";
87	char *fflags;
88
89	label = 0;
90	switch(s->type) {
91	case F_BLOCK:
92		if (!S_ISBLK(p->fts_statp->st_mode))
93			goto typeerr;
94		break;
95	case F_CHAR:
96		if (!S_ISCHR(p->fts_statp->st_mode))
97			goto typeerr;
98		break;
99	case F_DIR:
100		if (!S_ISDIR(p->fts_statp->st_mode))
101			goto typeerr;
102		break;
103	case F_FIFO:
104		if (!S_ISFIFO(p->fts_statp->st_mode))
105			goto typeerr;
106		break;
107	case F_FILE:
108		if (!S_ISREG(p->fts_statp->st_mode))
109			goto typeerr;
110		break;
111	case F_LINK:
112		if (!S_ISLNK(p->fts_statp->st_mode))
113			goto typeerr;
114		break;
115	case F_SOCK:
116		if (!S_ISSOCK(p->fts_statp->st_mode)) {
117typeerr:		LABEL;
118			(void)printf("\ttype expected %s found %s\n",
119			    ftype(s->type), inotype(p->fts_statp->st_mode));
120			return (label);
121		}
122		break;
123	}
124	/* Set the uid/gid first, then set the mode. */
125	if (s->flags & (F_UID | F_UNAME) && s->st_uid != p->fts_statp->st_uid) {
126		LABEL;
127		(void)printf("%suser expected %lu found %lu",
128		    tab, (u_long)s->st_uid, (u_long)p->fts_statp->st_uid);
129		if (uflag)
130			if (chown(p->fts_accpath, s->st_uid, -1))
131				(void)printf(" not modified: %s\n",
132				    strerror(errno));
133			else
134				(void)printf(" modified\n");
135		else
136			(void)printf("\n");
137		tab = "\t";
138	}
139	if (s->flags & (F_GID | F_GNAME) && s->st_gid != p->fts_statp->st_gid) {
140		LABEL;
141		(void)printf("%sgid expected %lu found %lu",
142		    tab, (u_long)s->st_gid, (u_long)p->fts_statp->st_gid);
143		if (uflag)
144			if (chown(p->fts_accpath, -1, s->st_gid))
145				(void)printf(" not modified: %s\n",
146				    strerror(errno));
147			else
148				(void)printf(" modified\n");
149		else
150			(void)printf("\n");
151		tab = "\t";
152	}
153	if (s->flags & F_MODE &&
154	    !S_ISLNK(p->fts_statp->st_mode) &&
155	    s->st_mode != (p->fts_statp->st_mode & MBITS)) {
156		LABEL;
157		(void)printf("%spermissions expected %#o found %#o",
158		    tab, s->st_mode, p->fts_statp->st_mode & MBITS);
159		if (uflag)
160			if (chmod(p->fts_accpath, s->st_mode))
161				(void)printf(" not modified: %s\n",
162				    strerror(errno));
163			else
164				(void)printf(" modified\n");
165		else
166			(void)printf("\n");
167		tab = "\t";
168	}
169	if (s->flags & F_NLINK && s->type != F_DIR &&
170	    s->st_nlink != p->fts_statp->st_nlink) {
171		LABEL;
172		(void)printf("%slink_count expected %u found %u\n",
173		    tab, s->st_nlink, p->fts_statp->st_nlink);
174		tab = "\t";
175	}
176	if (s->flags & F_SIZE && s->st_size != p->fts_statp->st_size &&
177		!S_ISDIR(p->fts_statp->st_mode)) {
178		LABEL;
179		(void)printf("%ssize expected %jd found %jd\n", tab,
180		    (intmax_t)s->st_size, (intmax_t)p->fts_statp->st_size);
181		tab = "\t";
182	}
183	/*
184	 * XXX
185	 * Catches nano-second differences, but doesn't display them.
186	 */
187	if ((s->flags & F_TIME) &&
188	     ((s->st_mtimespec.tv_sec != p->fts_statp->st_mtimespec.tv_sec) ||
189	     (s->st_mtimespec.tv_nsec != p->fts_statp->st_mtimespec.tv_nsec))) {
190		LABEL;
191		(void)printf("%smodification time expected %.24s ",
192		    tab, ctime(&s->st_mtimespec.tv_sec));
193		(void)printf("found %.24s\n",
194		    ctime(&p->fts_statp->st_mtimespec.tv_sec));
195		tab = "\t";
196	}
197	if (s->flags & F_CKSUM) {
198		if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0) {
199			LABEL;
200			(void)printf("%scksum: %s: %s\n",
201			    tab, p->fts_accpath, strerror(errno));
202			tab = "\t";
203		} else if (crc(fd, &val, &len)) {
204			(void)close(fd);
205			LABEL;
206			(void)printf("%scksum: %s: %s\n",
207			    tab, p->fts_accpath, strerror(errno));
208			tab = "\t";
209		} else {
210			(void)close(fd);
211			if (s->cksum != val) {
212				LABEL;
213				(void)printf("%scksum expected %lu found %lu\n",
214				    tab, s->cksum, (unsigned long)val);
215				tab = "\t";
216			}
217		}
218	}
219	/*
220	 * XXX
221	 * since chflags(2) will reset file times, the utimes() above
222	 * may have been useless!  oh well, we'd rather have correct
223	 * flags, rather than times?
224	 */
225	if ((s->flags & F_FLAGS) && s->st_flags != p->fts_statp->st_flags) {
226		LABEL;
227		fflags = flags_to_string(s->st_flags);
228		(void)printf("%sflags expected \"%s\"", tab, fflags);
229		free(fflags);
230
231		fflags = flags_to_string(p->fts_statp->st_flags);
232		(void)printf(" found \"%s\"", fflags);
233		free(fflags);
234
235		if (uflag)
236			if (chflags(p->fts_accpath, s->st_flags))
237				(void)printf(" not modified: %s\n",
238				    strerror(errno));
239			else
240				(void)printf(" modified\n");
241		else
242			(void)printf("\n");
243		tab = "\t";
244	}
245#ifdef MD5
246	if (s->flags & F_MD5) {
247		char *new_digest, buf[33];
248
249		new_digest = MD5File(p->fts_accpath, buf);
250		if (!new_digest) {
251			LABEL;
252			printf("%sMD5: %s: %s\n", tab, p->fts_accpath,
253			       strerror(errno));
254			tab = "\t";
255		} else if (strcmp(new_digest, s->md5digest)) {
256			LABEL;
257			printf("%sMD5 expected %s found %s\n", tab, s->md5digest,
258			       new_digest);
259			tab = "\t";
260		}
261	}
262#endif /* MD5 */
263#ifdef SHA1
264	if (s->flags & F_SHA1) {
265		char *new_digest, buf[41];
266
267		new_digest = SHA1_File(p->fts_accpath, buf);
268		if (!new_digest) {
269			LABEL;
270			printf("%sSHA-1: %s: %s\n", tab, p->fts_accpath,
271			       strerror(errno));
272			tab = "\t";
273		} else if (strcmp(new_digest, s->sha1digest)) {
274			LABEL;
275			printf("%sSHA-1 expected %s found %s\n",
276			       tab, s->sha1digest, new_digest);
277			tab = "\t";
278		}
279	}
280#endif /* SHA1 */
281#ifdef RMD160
282	if (s->flags & F_RMD160) {
283		char *new_digest, buf[41];
284
285		new_digest = RIPEMD160_File(p->fts_accpath, buf);
286		if (!new_digest) {
287			LABEL;
288			printf("%sRIPEMD160: %s: %s\n", tab,
289			       p->fts_accpath, strerror(errno));
290			tab = "\t";
291		} else if (strcmp(new_digest, s->rmd160digest)) {
292			LABEL;
293			printf("%sRIPEMD160 expected %s found %s\n",
294			       tab, s->rmd160digest, new_digest);
295			tab = "\t";
296		}
297	}
298#endif /* RMD160 */
299
300	if (s->flags & F_SLINK &&
301	    strcmp(cp = rlink(p->fts_accpath), s->slink)) {
302		LABEL;
303		(void)printf("%slink_ref expected %s found %s\n",
304		      tab, s->slink, cp);
305	}
306	return (label);
307}
308
309const char *
310inotype(type)
311	u_int type;
312{
313	switch(type & S_IFMT) {
314	case S_IFBLK:
315		return ("block");
316	case S_IFCHR:
317		return ("char");
318	case S_IFDIR:
319		return ("dir");
320	case S_IFIFO:
321		return ("fifo");
322	case S_IFREG:
323		return ("file");
324	case S_IFLNK:
325		return ("link");
326	case S_IFSOCK:
327		return ("socket");
328	default:
329		return ("unknown");
330	}
331	/* NOTREACHED */
332}
333
334static const char *
335ftype(type)
336	u_int type;
337{
338	switch(type) {
339	case F_BLOCK:
340		return ("block");
341	case F_CHAR:
342		return ("char");
343	case F_DIR:
344		return ("dir");
345	case F_FIFO:
346		return ("fifo");
347	case F_FILE:
348		return ("file");
349	case F_LINK:
350		return ("link");
351	case F_SOCK:
352		return ("socket");
353	default:
354		return ("unknown");
355	}
356	/* NOTREACHED */
357}
358
359char *
360rlink(name)
361	char *name;
362{
363	static char lbuf[MAXPATHLEN];
364	register int len;
365
366	if ((len = readlink(name, lbuf, sizeof(lbuf) - 1)) == -1)
367		err(1, "line %d: %s", lineno, name);
368	lbuf[len] = '\0';
369	return (lbuf);
370}
371