compare.c revision 121853
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 121853 2003-11-01 08:43:54Z bde $");
37
38#include <sys/param.h>
39#include <sys/stat.h>
40#include <sys/time.h>
41
42#include <err.h>
43#include <errno.h>
44#include <fcntl.h>
45#include <fts.h>
46#ifdef MD5
47#include <md5.h>
48#endif
49#ifdef RMD160
50#include <ripemd.h>
51#endif
52#ifdef SHA1
53#include <sha.h>
54#endif
55#include <stdint.h>
56#include <stdio.h>
57#include <time.h>
58#include <unistd.h>
59#include <vis.h>
60
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(char *name __unused, NODE *s, FTSENT *p)
78{
79	struct timeval tv[2];
80	uint32_t val;
81	int fd, label;
82	off_t len;
83	char *cp;
84	const char *tab = "";
85	char *fflags;
86
87	label = 0;
88	switch(s->type) {
89	case F_BLOCK:
90		if (!S_ISBLK(p->fts_statp->st_mode))
91			goto typeerr;
92		break;
93	case F_CHAR:
94		if (!S_ISCHR(p->fts_statp->st_mode))
95			goto typeerr;
96		break;
97	case F_DIR:
98		if (!S_ISDIR(p->fts_statp->st_mode))
99			goto typeerr;
100		break;
101	case F_FIFO:
102		if (!S_ISFIFO(p->fts_statp->st_mode))
103			goto typeerr;
104		break;
105	case F_FILE:
106		if (!S_ISREG(p->fts_statp->st_mode))
107			goto typeerr;
108		break;
109	case F_LINK:
110		if (!S_ISLNK(p->fts_statp->st_mode))
111			goto typeerr;
112		break;
113	case F_SOCK:
114		if (!S_ISSOCK(p->fts_statp->st_mode)) {
115typeerr:		LABEL;
116			(void)printf("\ttype expected %s found %s\n",
117			    ftype(s->type), inotype(p->fts_statp->st_mode));
118			return (label);
119		}
120		break;
121	}
122	/* Set the uid/gid first, then set the mode. */
123	if (s->flags & (F_UID | F_UNAME) && s->st_uid != p->fts_statp->st_uid) {
124		LABEL;
125		(void)printf("%suser expected %lu found %lu",
126		    tab, (u_long)s->st_uid, (u_long)p->fts_statp->st_uid);
127		if (uflag)
128			if (chown(p->fts_accpath, s->st_uid, -1))
129				(void)printf(" not modified: %s\n",
130				    strerror(errno));
131			else
132				(void)printf(" modified\n");
133		else
134			(void)printf("\n");
135		tab = "\t";
136	}
137	if (s->flags & (F_GID | F_GNAME) && s->st_gid != p->fts_statp->st_gid) {
138		LABEL;
139		(void)printf("%sgid expected %lu found %lu",
140		    tab, (u_long)s->st_gid, (u_long)p->fts_statp->st_gid);
141		if (uflag)
142			if (chown(p->fts_accpath, -1, s->st_gid))
143				(void)printf(" not modified: %s\n",
144				    strerror(errno));
145			else
146				(void)printf(" modified\n");
147		else
148			(void)printf("\n");
149		tab = "\t";
150	}
151	if (s->flags & F_MODE &&
152	    !S_ISLNK(p->fts_statp->st_mode) &&
153	    s->st_mode != (p->fts_statp->st_mode & MBITS)) {
154		LABEL;
155		(void)printf("%spermissions expected %#o found %#o",
156		    tab, s->st_mode, p->fts_statp->st_mode & MBITS);
157		if (uflag)
158			if (chmod(p->fts_accpath, s->st_mode))
159				(void)printf(" not modified: %s\n",
160				    strerror(errno));
161			else
162				(void)printf(" modified\n");
163		else
164			(void)printf("\n");
165		tab = "\t";
166	}
167	if (s->flags & F_NLINK && s->type != F_DIR &&
168	    s->st_nlink != p->fts_statp->st_nlink) {
169		LABEL;
170		(void)printf("%slink_count expected %u found %u\n",
171		    tab, s->st_nlink, p->fts_statp->st_nlink);
172		tab = "\t";
173	}
174	if (s->flags & F_SIZE && s->st_size != p->fts_statp->st_size &&
175		!S_ISDIR(p->fts_statp->st_mode)) {
176		LABEL;
177		(void)printf("%ssize expected %jd found %jd\n", tab,
178		    (intmax_t)s->st_size, (intmax_t)p->fts_statp->st_size);
179		tab = "\t";
180	}
181	/*
182	 * XXX
183	 * Catches nano-second differences, but doesn't display them.
184	 */
185	if ((s->flags & F_TIME) &&
186	     ((s->st_mtimespec.tv_sec != p->fts_statp->st_mtimespec.tv_sec) ||
187	     (s->st_mtimespec.tv_nsec != p->fts_statp->st_mtimespec.tv_nsec))) {
188		LABEL;
189		(void)printf("%smodification time expected %.24s ",
190		    tab, ctime(&s->st_mtimespec.tv_sec));
191		(void)printf("found %.24s",
192		    ctime(&p->fts_statp->st_mtimespec.tv_sec));
193		if (uflag) {
194			tv[0].tv_sec = s->st_mtimespec.tv_sec;
195			tv[0].tv_usec = s->st_mtimespec.tv_nsec / 1000;
196			tv[1] = tv[0];
197			if (utimes(p->fts_accpath, tv))
198				(void)printf(" not modified: %s\n",
199				    strerror(errno));
200			else
201				(void)printf(" modified\n");
202		} else
203			(void)printf("\n");
204		tab = "\t";
205	}
206	if (s->flags & F_CKSUM) {
207		if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0) {
208			LABEL;
209			(void)printf("%scksum: %s: %s\n",
210			    tab, p->fts_accpath, strerror(errno));
211			tab = "\t";
212		} else if (crc(fd, &val, &len)) {
213			(void)close(fd);
214			LABEL;
215			(void)printf("%scksum: %s: %s\n",
216			    tab, p->fts_accpath, strerror(errno));
217			tab = "\t";
218		} else {
219			(void)close(fd);
220			if (s->cksum != val) {
221				LABEL;
222				(void)printf("%scksum expected %lu found %lu\n",
223				    tab, s->cksum, (unsigned long)val);
224				tab = "\t";
225			}
226		}
227	}
228	if ((s->flags & F_FLAGS) && s->st_flags != p->fts_statp->st_flags) {
229		LABEL;
230		fflags = flags_to_string(s->st_flags);
231		(void)printf("%sflags expected \"%s\"", tab, fflags);
232		free(fflags);
233
234		fflags = flags_to_string(p->fts_statp->st_flags);
235		(void)printf(" found \"%s\"", fflags);
236		free(fflags);
237
238		if (uflag)
239			if (chflags(p->fts_accpath, s->st_flags))
240				(void)printf(" not modified: %s\n",
241				    strerror(errno));
242			else
243				(void)printf(" modified\n");
244		else
245			(void)printf("\n");
246		tab = "\t";
247	}
248#ifdef MD5
249	if (s->flags & F_MD5) {
250		char *new_digest, buf[33];
251
252		new_digest = MD5File(p->fts_accpath, buf);
253		if (!new_digest) {
254			LABEL;
255			printf("%sMD5: %s: %s\n", tab, p->fts_accpath,
256			       strerror(errno));
257			tab = "\t";
258		} else if (strcmp(new_digest, s->md5digest)) {
259			LABEL;
260			printf("%sMD5 expected %s found %s\n", tab, s->md5digest,
261			       new_digest);
262			tab = "\t";
263		}
264	}
265#endif /* MD5 */
266#ifdef SHA1
267	if (s->flags & F_SHA1) {
268		char *new_digest, buf[41];
269
270		new_digest = SHA1_File(p->fts_accpath, buf);
271		if (!new_digest) {
272			LABEL;
273			printf("%sSHA-1: %s: %s\n", tab, p->fts_accpath,
274			       strerror(errno));
275			tab = "\t";
276		} else if (strcmp(new_digest, s->sha1digest)) {
277			LABEL;
278			printf("%sSHA-1 expected %s found %s\n",
279			       tab, s->sha1digest, new_digest);
280			tab = "\t";
281		}
282	}
283#endif /* SHA1 */
284#ifdef RMD160
285	if (s->flags & F_RMD160) {
286		char *new_digest, buf[41];
287
288		new_digest = RIPEMD160_File(p->fts_accpath, buf);
289		if (!new_digest) {
290			LABEL;
291			printf("%sRIPEMD160: %s: %s\n", tab,
292			       p->fts_accpath, strerror(errno));
293			tab = "\t";
294		} else if (strcmp(new_digest, s->rmd160digest)) {
295			LABEL;
296			printf("%sRIPEMD160 expected %s found %s\n",
297			       tab, s->rmd160digest, new_digest);
298			tab = "\t";
299		}
300	}
301#endif /* RMD160 */
302
303	if (s->flags & F_SLINK &&
304	    strcmp(cp = rlink(p->fts_accpath), s->slink)) {
305		LABEL;
306		(void)printf("%slink_ref expected %s found %s\n",
307		      tab, s->slink, cp);
308	}
309	return (label);
310}
311
312const char *
313inotype(u_int type)
314{
315	switch(type & S_IFMT) {
316	case S_IFBLK:
317		return ("block");
318	case S_IFCHR:
319		return ("char");
320	case S_IFDIR:
321		return ("dir");
322	case S_IFIFO:
323		return ("fifo");
324	case S_IFREG:
325		return ("file");
326	case S_IFLNK:
327		return ("link");
328	case S_IFSOCK:
329		return ("socket");
330	default:
331		return ("unknown");
332	}
333	/* NOTREACHED */
334}
335
336static const char *
337ftype(u_int type)
338{
339	switch(type) {
340	case F_BLOCK:
341		return ("block");
342	case F_CHAR:
343		return ("char");
344	case F_DIR:
345		return ("dir");
346	case F_FIFO:
347		return ("fifo");
348	case F_FILE:
349		return ("file");
350	case F_LINK:
351		return ("link");
352	case F_SOCK:
353		return ("socket");
354	default:
355		return ("unknown");
356	}
357	/* NOTREACHED */
358}
359
360char *
361rlink(char *name)
362{
363	static char lbuf[MAXPATHLEN * 4];
364	int len;
365	char tbuf[MAXPATHLEN];
366
367	if ((len = readlink(name, tbuf, sizeof(tbuf) - 1)) == -1)
368		err(1, "line %d: %s", lineno, name);
369	tbuf[len] = '\0';
370	strvis(lbuf, tbuf, VIS_WHITE | VIS_OCTAL);
371	return (lbuf);
372}
373