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