xref: /openbsd-src/usr.sbin/mtree/compare.c (revision 3a3fbb3f2e2521ab7c4a56b7ff7462ebd9095ec5)
1 /*	$NetBSD: compare.c,v 1.11 1996/09/05 09:56:48 mycroft Exp $	*/
2 /*	$OpenBSD: compare.c,v 1.13 2001/10/01 16:48:18 jakob Exp $	*/
3 
4 /*-
5  * Copyright (c) 1989, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by the University of
19  *	California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36 
37 #ifndef lint
38 #if 0
39 static const char sccsid[] = "@(#)compare.c	8.1 (Berkeley) 6/6/93";
40 #else
41 static const char rcsid[] = "$OpenBSD: compare.c,v 1.13 2001/10/01 16:48:18 jakob Exp $";
42 #endif
43 #endif /* not lint */
44 
45 #include <sys/param.h>
46 #include <sys/stat.h>
47 #include <fcntl.h>
48 #include <fts.h>
49 #include <errno.h>
50 #include <stdio.h>
51 #include <time.h>
52 #include <unistd.h>
53 #include <md5.h>
54 #include <sha1.h>
55 #include <rmd160.h>
56 #include "mtree.h"
57 #include "extern.h"
58 
59 extern int lflag, tflag, uflag;
60 
61 static char *ftype __P((u_int));
62 
63 #define	INDENTNAMELEN	8
64 #define	LABEL \
65 	if (!label++) { \
66 		len = printf("%s: ", RP(p)); \
67 		if (len > INDENTNAMELEN) { \
68 			tab = "\t"; \
69 			(void)printf("\n"); \
70 		} else { \
71 			tab = ""; \
72 			(void)printf("%*s", INDENTNAMELEN - (int)len, ""); \
73 		} \
74 	}
75 
76 #define REPLACE_COMMA(x)						\
77 	do {								\
78 		char *l;						\
79 		for (l = x; *l; l++) {					\
80 			if (*l == ',')					\
81 				*l = ' ';				\
82 		}							\
83 	} while (0)							\
84 
85 int
86 compare(name, s, p)
87 	char *name;
88 	register NODE *s;
89 	register FTSENT *p;
90 {
91 	u_int32_t len, val;
92 	int fd, label;
93 	char *cp, *tab = "";
94 
95 	label = 0;
96 	switch(s->type) {
97 	case F_BLOCK:
98 		if (!S_ISBLK(p->fts_statp->st_mode))
99 			goto typeerr;
100 		break;
101 	case F_CHAR:
102 		if (!S_ISCHR(p->fts_statp->st_mode))
103 			goto typeerr;
104 		break;
105 	case F_DIR:
106 		if (!S_ISDIR(p->fts_statp->st_mode))
107 			goto typeerr;
108 		break;
109 	case F_FIFO:
110 		if (!S_ISFIFO(p->fts_statp->st_mode))
111 			goto typeerr;
112 		break;
113 	case F_FILE:
114 		if (!S_ISREG(p->fts_statp->st_mode))
115 			goto typeerr;
116 		break;
117 	case F_LINK:
118 		if (!S_ISLNK(p->fts_statp->st_mode))
119 			goto typeerr;
120 		break;
121 	case F_SOCK:
122 		if (!S_ISSOCK(p->fts_statp->st_mode)) {
123 typeerr:		LABEL;
124 			(void)printf("\ttype (%s, %s)\n",
125 			    ftype(s->type), inotype(p->fts_statp->st_mode));
126 		}
127 		break;
128 	}
129 	/* Set the uid/gid first, then set the mode. */
130 	if (s->flags & (F_UID | F_UNAME) && s->st_uid != p->fts_statp->st_uid) {
131 		LABEL;
132 		(void)printf("%suser (%u, %u",
133 		    tab, s->st_uid, p->fts_statp->st_uid);
134 		if (uflag)
135 			if (chown(p->fts_accpath, s->st_uid, -1))
136 				(void)printf(", not modified: %s)\n",
137 				    strerror(errno));
138 			else
139 				(void)printf(", modified)\n");
140 		else
141 			(void)printf(")\n");
142 		tab = "\t";
143 	}
144 	if (s->flags & (F_GID | F_GNAME) && s->st_gid != p->fts_statp->st_gid) {
145 		LABEL;
146 		(void)printf("%sgid (%u, %u",
147 		    tab, s->st_gid, p->fts_statp->st_gid);
148 		if (uflag)
149 			if (chown(p->fts_accpath, -1, s->st_gid))
150 				(void)printf(", not modified: %s)\n",
151 				    strerror(errno));
152 			else
153 				(void)printf(", modified)\n");
154 		else
155 			(void)printf(")\n");
156 		tab = "\t";
157 	}
158 	if (s->flags & F_MODE &&
159 	    s->st_mode != (p->fts_statp->st_mode & MBITS)) {
160 		if (lflag) {
161 			mode_t tmode, mode;
162 
163 			tmode = s->st_mode;
164 			mode = p->fts_statp->st_mode & MBITS;
165 			/*
166 			 * if none of the suid/sgid/etc bits are set,
167 			 * then if the mode is a subset of the target,
168 			 * skip.
169 			 */
170 			if (!((tmode & ~(S_IRWXU|S_IRWXG|S_IRWXO)) ||
171 			    (mode & ~(S_IRWXU|S_IRWXG|S_IRWXO))))
172 				if ((mode | tmode) == tmode)
173 					goto skip;
174 		}
175 		LABEL;
176 		(void)printf("%spermissions (%#o, %#o",
177 		    tab, s->st_mode, p->fts_statp->st_mode & MBITS);
178 		if (uflag)
179 			if (chmod(p->fts_accpath, s->st_mode))
180 				(void)printf(", not modified: %s)\n",
181 				    strerror(errno));
182 			else
183 				(void)printf(", modified)\n");
184 		else
185 			(void)printf(")\n");
186 		tab = "\t";
187 	skip:
188 	}
189 	if (s->flags & F_NLINK && s->type != F_DIR &&
190 	    s->st_nlink != p->fts_statp->st_nlink) {
191 		LABEL;
192 		(void)printf("%slink count (%u, %u)\n",
193 		    tab, s->st_nlink, p->fts_statp->st_nlink);
194 		tab = "\t";
195 	}
196 	if (s->flags & F_SIZE && s->st_size != p->fts_statp->st_size) {
197 		LABEL;
198 		(void)printf("%ssize (%qd, %qd)\n",
199 		    tab, s->st_size, p->fts_statp->st_size);
200 		tab = "\t";
201 	}
202 	/*
203 	 * XXX
204 	 * Since utimes(2) only takes a timeval, there's no point in
205 	 * comparing the low bits of the timespec nanosecond field.  This
206 	 * will only result in mismatches that we can never fix.
207 	 *
208 	 * Doesn't display microsecond differences.
209 	 */
210 	if (s->flags & F_TIME) {
211 		struct timeval tv[2];
212 
213 		TIMESPEC_TO_TIMEVAL(&tv[0], &s->st_mtimespec);
214 		TIMESPEC_TO_TIMEVAL(&tv[1], &p->fts_statp->st_mtimespec);
215 		if (tv[0].tv_sec != tv[1].tv_sec ||
216 		    tv[0].tv_usec != tv[1].tv_usec) {
217 			LABEL;
218 			(void)printf("%smodification time (%.24s, ",
219 			    tab, ctime(&s->st_mtimespec.tv_sec));
220 			(void)printf("%.24s",
221 			    ctime(&p->fts_statp->st_mtimespec.tv_sec));
222 			if (tflag) {
223 				tv[1] = tv[0];
224 				if (utimes(p->fts_accpath, tv))
225 					(void)printf(", not modified: %s)\n",
226 					    strerror(errno));
227 				else
228 					(void)printf(", modified)\n");
229 			} else
230 				(void)printf(")\n");
231 			tab = "\t";
232 		}
233 	}
234 	if (s->flags & F_CKSUM) {
235 		if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0) {
236 			LABEL;
237 			(void)printf("%scksum: %s: %s\n",
238 			    tab, p->fts_accpath, strerror(errno));
239 			tab = "\t";
240 		} else if (crc(fd, &val, &len)) {
241 			(void)close(fd);
242 			LABEL;
243 			(void)printf("%scksum: %s: %s\n",
244 			    tab, p->fts_accpath, strerror(errno));
245 			tab = "\t";
246 		} else {
247 			(void)close(fd);
248 			if (s->cksum != val) {
249 				LABEL;
250 				(void)printf("%scksum (%u, %u)\n",
251 				    tab, s->cksum, val);
252 			}
253 			tab = "\t";
254 		}
255 	}
256 	if (s->flags & F_MD5) {
257 		char *new_digest, buf[33];
258 
259 		new_digest = MD5File(p->fts_accpath, buf);
260 		if (!new_digest) {
261 			LABEL;
262 			printf("%sMD5File: %s: %s\n", tab, p->fts_accpath,
263 			       strerror(errno));
264 			tab = "\t";
265 		} else if (strcmp(new_digest, s->md5digest)) {
266 			LABEL;
267 			printf("%sMD5 (%s, %s)\n", tab, s->md5digest,
268 			       new_digest);
269 			tab = "\t";
270 		}
271 	}
272 	if (s->flags & F_RMD160) {
273 		char *new_digest, buf[41];
274 
275 		new_digest = RMD160File(p->fts_accpath, buf);
276 		if (!new_digest) {
277 			LABEL;
278 			printf("%sRMD160File: %s: %s\n", tab, p->fts_accpath,
279 			       strerror(errno));
280 			tab = "\t";
281 		} else if (strcmp(new_digest, s->rmd160digest)) {
282 			LABEL;
283 			printf("%sRMD160 (%s, %s)\n", tab, s->rmd160digest,
284 			       new_digest);
285 			tab = "\t";
286 		}
287 	}
288 	if (s->flags & F_SHA1) {
289 		char *new_digest, buf[41];
290 
291 		new_digest = SHA1File(p->fts_accpath, buf);
292 		if (!new_digest) {
293 			LABEL;
294 			printf("%sSHA1File: %s: %s\n", tab, p->fts_accpath,
295 			       strerror(errno));
296 			tab = "\t";
297 		} else if (strcmp(new_digest, s->sha1digest)) {
298 			LABEL;
299 			printf("%sSHA1 (%s, %s)\n", tab, s->sha1digest,
300 			       new_digest);
301 			tab = "\t";
302 		}
303 	}
304 	if (s->flags & F_SLINK && strcmp(cp = rlink(name), s->slink)) {
305 		LABEL;
306 		(void)printf("%slink ref (%s, %s)\n", tab, cp, s->slink);
307 	}
308 	if (s->flags & F_FLAGS && s->file_flags != p->fts_statp->st_flags) {
309 		char *db_flags = NULL;
310 		char *cur_flags = NULL;
311 
312 		if ((db_flags = fflagstostr(s->file_flags)) == NULL ||
313 		    (cur_flags = fflagstostr(p->fts_statp->st_flags)) == NULL) {
314 			LABEL;
315 			(void)printf("%sflags: %s %s\n", tab, p->fts_accpath,
316 				     strerror(errno));
317 			tab = "\t";
318 			if (db_flags != NULL)
319 				free(db_flags);
320 			if (cur_flags != NULL)
321 				free(cur_flags);
322 		} else {
323 			LABEL;
324 			REPLACE_COMMA(db_flags);
325 			REPLACE_COMMA(cur_flags);
326 			printf("%sflags (%s, %s", tab, (*db_flags == '\0') ?
327 						  "-" : db_flags,
328 						  (*cur_flags == '\0') ?
329 						  "-" : cur_flags);
330 				tab = "\t";
331 			if (uflag)
332 				if (chflags(p->fts_accpath, s->file_flags))
333 					(void)printf(", not modified: %s)\n",
334 						strerror(errno));
335 				else
336 					(void)printf(", modified)\n");
337 			else
338 				(void)printf(")\n");
339 			tab = "\t";
340 
341 			free(db_flags);
342 			free(cur_flags);
343 		}
344 	}
345 	return (label);
346 }
347 
348 char *
349 inotype(type)
350 	u_int type;
351 {
352 	switch(type & S_IFMT) {
353 	case S_IFBLK:
354 		return ("block");
355 	case S_IFCHR:
356 		return ("char");
357 	case S_IFDIR:
358 		return ("dir");
359 	case S_IFIFO:
360 		return ("fifo");
361 	case S_IFREG:
362 		return ("file");
363 	case S_IFLNK:
364 		return ("link");
365 	case S_IFSOCK:
366 		return ("socket");
367 	default:
368 		return ("unknown");
369 	}
370 	/* NOTREACHED */
371 }
372 
373 static char *
374 ftype(type)
375 	u_int type;
376 {
377 	switch(type) {
378 	case F_BLOCK:
379 		return ("block");
380 	case F_CHAR:
381 		return ("char");
382 	case F_DIR:
383 		return ("dir");
384 	case F_FIFO:
385 		return ("fifo");
386 	case F_FILE:
387 		return ("file");
388 	case F_LINK:
389 		return ("link");
390 	case F_SOCK:
391 		return ("socket");
392 	default:
393 		return ("unknown");
394 	}
395 	/* NOTREACHED */
396 }
397 
398 char *
399 rlink(name)
400 	char *name;
401 {
402 	static char lbuf[MAXPATHLEN];
403 	register int len;
404 
405 	if ((len = readlink(name, lbuf, sizeof(lbuf)-1)) == -1)
406 		error("%s: %s", name, strerror(errno));
407 	lbuf[len] = '\0';
408 	return (lbuf);
409 }
410