xref: /dragonfly/usr.sbin/mtree/compare.c (revision 58645856)
1 /*	@(#)compare.c	8.1 (Berkeley) 6/6/93	*/
2 /*	$NetBSD: compare.c,v 1.58 2013/11/21 18:39:50 christos 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. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include <sys/param.h>
34 #include <sys/stat.h>
35 #include <sys/time.h>
36 
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <stdio.h>
40 #include <stdint.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <time.h>
44 #include <unistd.h>
45 
46 #ifndef NO_MD5
47 #include <openssl/md5.h>
48 #endif
49 #ifndef NO_RMD160
50 #include <openssl/ripemd.h>
51 #endif
52 #ifndef NO_SHA
53 #include <openssl/sha.h>
54 #endif
55 
56 #include "extern.h"
57 
58 #define	INDENTNAMELEN	8
59 #define MARK								\
60 do {									\
61 	if (flavor == F_FREEBSD9) {					\
62 		len = printf("%s changed\n", RP(p));			\
63 		tab = "\t";						\
64 	} else {							\
65 		len = printf("%s: ", RP(p));				\
66 		if (len > INDENTNAMELEN) {				\
67 			tab = "\t";					\
68 			printf("\n");					\
69 		} else {						\
70 			tab = "";					\
71 			printf("%*s", INDENTNAMELEN - (int)len, "");	\
72 		}							\
73 	}								\
74 } while (0)
75 #define	LABEL if (!label++) MARK
76 
77 #if HAVE_STRUCT_STAT_ST_FLAGS
78 
79 
80 #define CHANGEFLAGS							\
81 	if (flags != p->fts_statp->st_flags) {				\
82 		char *sf;						\
83 		if (!label) {						\
84 			MARK;						\
85 			sf = flags_to_string(p->fts_statp->st_flags, "none"); \
86 			printf("%sflags (\"%s\"", tab, sf);		\
87 			free(sf);					\
88 		}							\
89 		if (lchflags(p->fts_accpath, flags)) {			\
90 			label++;					\
91 			printf(", not modified: %s)\n",			\
92 			    strerror(errno));				\
93 		} else {						\
94 			sf = flags_to_string(flags, "none");		\
95 			printf(", modified to \"%s\")\n", sf);		\
96 			free(sf);					\
97 		}							\
98 	}
99 
100 /* SETFLAGS:
101  * given pflags, additionally set those flags specified in s->st_flags and
102  * selected by mask (the other flags are left unchanged).
103  */
104 #define SETFLAGS(pflags, mask)						\
105 do {									\
106 	flags = (s->st_flags & (mask)) | (pflags);			\
107 	CHANGEFLAGS;							\
108 } while (0)
109 
110 /* CLEARFLAGS:
111  * given pflags, reset the flags specified in s->st_flags and selected by mask
112  * (the other flags are left unchanged).
113  */
114 #define CLEARFLAGS(pflags, mask)					\
115 do {									\
116 	flags = (~(s->st_flags & (mask)) & CH_MASK) & (pflags);		\
117 	CHANGEFLAGS;							\
118 } while (0)
119 #endif	/* HAVE_STRUCT_STAT_ST_FLAGS */
120 
121 int
122 compare(NODE *s, FTSENT *p)
123 {
124 	u_int32_t len, val;
125 #if HAVE_STRUCT_STAT_ST_FLAGS
126 	u_int32_t flags;
127 #endif
128 	int fd, label;
129 	const char *cp, *tab;
130 #if !defined(NO_MD5) || !defined(NO_RMD160) || !defined(NO_SHA)
131 	char *digestbuf;
132 #endif
133 
134 	tab = NULL;
135 	label = 0;
136 	switch(s->type) {
137 	case F_BLOCK:
138 		if (!S_ISBLK(p->fts_statp->st_mode))
139 			goto typeerr;
140 		break;
141 	case F_CHAR:
142 		if (!S_ISCHR(p->fts_statp->st_mode))
143 			goto typeerr;
144 		break;
145 	case F_DIR:
146 		if (!S_ISDIR(p->fts_statp->st_mode))
147 			goto typeerr;
148 		break;
149 	case F_FIFO:
150 		if (!S_ISFIFO(p->fts_statp->st_mode))
151 			goto typeerr;
152 		break;
153 	case F_FILE:
154 		if (!S_ISREG(p->fts_statp->st_mode))
155 			goto typeerr;
156 		break;
157 	case F_LINK:
158 		if (!S_ISLNK(p->fts_statp->st_mode))
159 			goto typeerr;
160 		break;
161 #ifdef S_ISSOCK
162 	case F_SOCK:
163 		if (!S_ISSOCK(p->fts_statp->st_mode))
164 			goto typeerr;
165 		break;
166 #endif
167 typeerr:		LABEL;
168 		printf(flavor == F_FREEBSD9 ?
169 		    "\ttype expected %s found %s\n" : "\ttype (%s, %s)\n",
170 		    nodetype(s->type), inotype(p->fts_statp->st_mode));
171 		return (label);
172 	}
173 	if (mtree_Wflag)
174 		goto afterpermwhack;
175 #if HAVE_STRUCT_STAT_ST_FLAGS
176 	if (iflag && !uflag) {
177 		if (s->flags & F_FLAGS)
178 		    SETFLAGS(p->fts_statp->st_flags, SP_FLGS);
179 		return (label);
180         }
181 	if (mflag && !uflag) {
182 		if (s->flags & F_FLAGS)
183 		    CLEARFLAGS(p->fts_statp->st_flags, SP_FLGS);
184 		return (label);
185         }
186 #endif
187 	if (s->flags & F_DEV &&
188 	    (s->type == F_BLOCK || s->type == F_CHAR) &&
189 	    s->st_rdev != p->fts_statp->st_rdev) {
190 		LABEL;
191 		printf(flavor == F_FREEBSD9 ?
192 		    "%sdevice expected %#jx found %#jx" :
193 		    "%sdevice (%#jx, %#jx",
194 		    tab, (uintmax_t)s->st_rdev,
195 		    (uintmax_t)p->fts_statp->st_rdev);
196 		if (uflag) {
197 			if ((unlink(p->fts_accpath) == -1) ||
198 			    (mknod(p->fts_accpath,
199 			      s->st_mode | nodetoino(s->type),
200 			      s->st_rdev) == -1) ||
201 			    (lchown(p->fts_accpath, p->fts_statp->st_uid,
202 			      p->fts_statp->st_gid) == -1) )
203 				printf(", not modified: %s%s\n",
204 				    strerror(errno),
205 				    flavor == F_FREEBSD9 ? "" : ")");
206 			 else
207 				printf(", modified%s\n",
208 				    flavor == F_FREEBSD9 ? "" : ")");
209 		} else
210 			printf(")\n");
211 		tab = "\t";
212 	}
213 	/* Set the uid/gid first, then set the mode. */
214 	if (s->flags & (F_UID | F_UNAME) && s->st_uid != p->fts_statp->st_uid) {
215 		LABEL;
216 		printf(flavor == F_FREEBSD9 ?
217 		    "%suser expected %lu found %lu" : "%suser (%lu, %lu",
218 		    tab, (u_long)s->st_uid, (u_long)p->fts_statp->st_uid);
219 		if (uflag) {
220 			if (lchown(p->fts_accpath, s->st_uid, -1))
221 				printf(", not modified: %s%s\n",
222 				    strerror(errno),
223 				    flavor == F_FREEBSD9 ? "" : ")");
224 			else
225 				printf(", modified%s\n",
226 				    flavor == F_FREEBSD9 ? "" : ")");
227 		} else
228 			printf(")\n");
229 		tab = "\t";
230 	}
231 	if (s->flags & (F_GID | F_GNAME) && s->st_gid != p->fts_statp->st_gid) {
232 		LABEL;
233 		printf(flavor == F_FREEBSD9 ?
234 		    "%sgid expected %lu found %lu" : "%sgid (%lu, %lu",
235 		    tab, (u_long)s->st_gid, (u_long)p->fts_statp->st_gid);
236 		if (uflag) {
237 			if (lchown(p->fts_accpath, -1, s->st_gid))
238 				printf(", not modified: %s%s\n",
239 				    strerror(errno),
240 				    flavor == F_FREEBSD9 ? "" : ")");
241 			else
242 				printf(", modified%s\n",
243 				    flavor == F_FREEBSD9 ? "" : ")");
244 		}
245 		else
246 			printf(")\n");
247 		tab = "\t";
248 	}
249 	if (s->flags & F_MODE &&
250 	    s->st_mode != (p->fts_statp->st_mode & MBITS)) {
251 		if (lflag) {
252 			mode_t tmode, mode;
253 
254 			tmode = s->st_mode;
255 			mode = p->fts_statp->st_mode & MBITS;
256 			/*
257 			 * if none of the suid/sgid/etc bits are set,
258 			 * then if the mode is a subset of the target,
259 			 * skip.
260 			 */
261 			if (!((tmode & ~(S_IRWXU|S_IRWXG|S_IRWXO)) ||
262 			    (mode & ~(S_IRWXU|S_IRWXG|S_IRWXO))))
263 				if ((mode | tmode) == tmode)
264 					goto skip;
265 		}
266 
267 		LABEL;
268 		printf(flavor == F_FREEBSD9 ?
269 		    "%spermissions expcted %#lo found %#lo" :
270 		    "%spermissions (%#lo, %#lo",
271 		    tab, (u_long)s->st_mode,
272 		    (u_long)p->fts_statp->st_mode & MBITS);
273 		if (uflag) {
274 			if (lchmod(p->fts_accpath, s->st_mode))
275 				printf(", not modified: %s%s\n",
276 				    strerror(errno),
277 				    flavor == F_FREEBSD9 ? "" : ")");
278 			else
279 				printf(", modified%s\n",
280 				    flavor == F_FREEBSD9 ? "" : ")");
281 		}
282 		else
283 			printf(")\n");
284 		tab = "\t";
285 	skip:	;
286 	}
287 	if (s->flags & F_NLINK && s->type != F_DIR &&
288 	    s->st_nlink != p->fts_statp->st_nlink) {
289 		LABEL;
290 		printf(flavor == F_FREEBSD9 ?
291 		    "%slink count expected %lu found %lu\n" :
292 		    "%slink count (%lu, %lu)\n",
293 		    tab, (u_long)s->st_nlink, (u_long)p->fts_statp->st_nlink);
294 		tab = "\t";
295 	}
296 	if (s->flags & F_SIZE && s->st_size != p->fts_statp->st_size) {
297 		LABEL;
298 		printf(flavor == F_FREEBSD9 ?
299 		    "%ssize expected %ju found %ju\n" : "%ssize (%ju, %ju)\n",
300 		    tab, (uintmax_t)s->st_size,
301 		    (uintmax_t)p->fts_statp->st_size);
302 		tab = "\t";
303 	}
304 	/*
305 	 * XXX
306 	 * Since utimes(2) only takes a timeval, there's no point in
307 	 * comparing the low bits of the timespec nanosecond field.  This
308 	 * will only result in mismatches that we can never fix.
309 	 *
310 	 * Doesn't display microsecond differences.
311 	 */
312 	if (s->flags & F_TIME) {
313 		struct timeval tv[2];
314 		struct stat *ps = p->fts_statp;
315 		time_t smtime = s->st_mtimespec.tv_sec;
316 
317 #if defined(BSD4_4)
318 		time_t pmtime = ps->st_mtimespec.tv_sec;
319 
320 		TIMESPEC_TO_TIMEVAL(&tv[0], &s->st_mtimespec);
321 		TIMESPEC_TO_TIMEVAL(&tv[1], &ps->st_mtimespec);
322 #else
323 		time_t pmtime = (time_t)ps->st_mtime;
324 
325 		tv[0].tv_sec = smtime;
326 		tv[0].tv_usec = 0;
327 		tv[1].tv_sec = pmtime;
328 		tv[1].tv_usec = 0;
329 #endif
330 
331 		if (tv[0].tv_sec != tv[1].tv_sec ||
332 		    tv[0].tv_usec != tv[1].tv_usec) {
333 			LABEL;
334 			printf(flavor == F_FREEBSD9 ?
335 			    "%smodification time expected %.24s found " :
336 			    "%smodification time (%.24s, ",
337 			    tab, ctime(&smtime));
338 			printf("%.24s", ctime(&pmtime));
339 			if (tflag) {
340 				tv[1] = tv[0];
341 				if (utimes(p->fts_accpath, tv))
342 					printf(", not modified: %s%s\n",
343 					    strerror(errno),
344 					    flavor == F_FREEBSD9 ? "" : ")");
345 				else
346 					printf(", modified%s\n",
347 					    flavor == F_FREEBSD9 ? "" : ")");
348 			} else
349 				printf("%s\n", flavor == F_FREEBSD9 ? "" : ")");
350 			tab = "\t";
351 		}
352 	}
353 #if HAVE_STRUCT_STAT_ST_FLAGS
354 	/*
355 	 * XXX
356 	 * since lchflags(2) will reset file times, the utimes() above
357 	 * may have been useless!  oh well, we'd rather have correct
358 	 * flags, rather than times?
359 	 */
360         if ((s->flags & F_FLAGS) && ((s->st_flags != p->fts_statp->st_flags)
361 	    || mflag || iflag)) {
362 		if (s->st_flags != p->fts_statp->st_flags) {
363 			char *f_s;
364 			LABEL;
365 			f_s = flags_to_string(s->st_flags, "none");
366 			printf(flavor == F_FREEBSD9 ?
367 			    "%sflags expected \"%s\" found " :
368 			    "%sflags (\"%s\" is not ", tab, f_s);
369 			free(f_s);
370 			f_s = flags_to_string(p->fts_statp->st_flags, "none");
371 			printf("\"%s\"", f_s);
372 			free(f_s);
373 		}
374 		if (uflag) {
375 			if (iflag)
376 				SETFLAGS(0, CH_MASK);
377 			else if (mflag)
378 				CLEARFLAGS(0, SP_FLGS);
379 			else
380 				SETFLAGS(0, (~SP_FLGS & CH_MASK));
381 		} else
382 			printf("%s\n", flavor == F_FREEBSD9 ? "" : ")");
383 		tab = "\t";
384 	}
385 #endif	/* HAVE_STRUCT_STAT_ST_FLAGS */
386 
387 	/*
388 	 * from this point, no more permission checking or whacking
389 	 * occurs, only checking of stuff like checksums and symlinks.
390 	 */
391  afterpermwhack:
392 	if (s->flags & F_CKSUM) {
393 		if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0) {
394 			LABEL;
395 			printf("%scksum: %s: %s\n",
396 			    tab, p->fts_accpath, strerror(errno));
397 			tab = "\t";
398 		} else if (crc(fd, &val, &len)) {
399 			close(fd);
400 			LABEL;
401 			printf("%scksum: %s: %s\n",
402 			    tab, p->fts_accpath, strerror(errno));
403 			tab = "\t";
404 		} else {
405 			close(fd);
406 			if (s->cksum != val) {
407 				LABEL;
408 				printf(flavor == F_FREEBSD9 ?
409 				    "%scksum expected %lu found %lu\n" :
410 				    "%scksum (%lu, %lu)\n",
411 				    tab, s->cksum, (unsigned long)val);
412 			}
413 			tab = "\t";
414 		}
415 	}
416 #ifndef NO_MD5
417 	if (s->flags & F_MD5) {
418 		if ((digestbuf = dohash(F_MD5, p->fts_accpath)) == NULL) {
419 			LABEL;
420 			printf("%s%s: %s: %s\n",
421 			    tab, MD5KEY, p->fts_accpath, strerror(errno));
422 			tab = "\t";
423 		} else {
424 			if (strcmp(s->md5digest, digestbuf)) {
425 				LABEL;
426 				printf(flavor == F_FREEBSD9 ?
427 				    "%s%s expected %s found %s\n" :
428 				    "%s%s (0x%s, 0x%s)\n",
429 				    tab, MD5KEY, s->md5digest, digestbuf);
430 			}
431 			tab = "\t";
432 			free(digestbuf);
433 		}
434 	}
435 #endif	/* ! NO_MD5 */
436 #ifndef NO_RMD160
437 	if (s->flags & F_RMD160) {
438 		if ((digestbuf = dohash(F_RMD160, p->fts_accpath)) == NULL) {
439 			LABEL;
440 			printf("%s%s: %s: %s\n",
441 			    tab, RMD160KEY, p->fts_accpath, strerror(errno));
442 			tab = "\t";
443 		} else {
444 			if (strcmp(s->rmd160digest, digestbuf)) {
445 				LABEL;
446 				printf(flavor == F_FREEBSD9 ?
447 				    "%s%s expected %s found %s\n" :
448 				    "%s%s (0x%s, 0x%s)\n",
449 				    tab, RMD160KEY, s->rmd160digest, digestbuf);
450 			}
451 			tab = "\t";
452 			free(digestbuf);
453 		}
454 	}
455 #endif	/* ! NO_RMD160 */
456 #ifndef NO_SHA
457 	if (s->flags & F_SHA1) {
458 		if ((digestbuf = dohash(F_SHA1, p->fts_accpath)) == NULL) {
459 			LABEL;
460 			printf("%s%s: %s: %s\n",
461 			    tab, SHA1KEY, p->fts_accpath, strerror(errno));
462 			tab = "\t";
463 		} else {
464 			if (strcmp(s->sha1digest, digestbuf)) {
465 				LABEL;
466 				printf(flavor == F_FREEBSD9 ?
467 				    "%s%s expected %s found %s\n" :
468 				    "%s%s (0x%s, 0x%s)\n",
469 				    tab, SHA1KEY, s->sha1digest, digestbuf);
470 			}
471 			tab = "\t";
472 			free(digestbuf);
473 		}
474 	}
475 	if (s->flags & F_SHA256) {
476 		if ((digestbuf = dohash(F_SHA256, p->fts_accpath)) == NULL) {
477 			LABEL;
478 			printf("%s%s: %s: %s\n",
479 			    tab, SHA256KEY, p->fts_accpath, strerror(errno));
480 			tab = "\t";
481 		} else {
482 			if (strcmp(s->sha256digest, digestbuf)) {
483 				LABEL;
484 				printf(flavor == F_FREEBSD9 ?
485 				    "%s%s expected %s found %s\n" :
486 				    "%s%s (0x%s, 0x%s)\n",
487 				    tab, SHA256KEY, s->sha256digest, digestbuf);
488 			}
489 			tab = "\t";
490 			free(digestbuf);
491 		}
492 	}
493 #ifdef SHA384_DIGEST_LENGTH
494 	if (s->flags & F_SHA384) {
495 		if ((digestbuf = dohash(F_SHA384, p->fts_accpath)) == NULL) {
496 			LABEL;
497 			printf("%s%s: %s: %s\n",
498 			    tab, SHA384KEY, p->fts_accpath, strerror(errno));
499 			tab = "\t";
500 		} else {
501 			if (strcmp(s->sha384digest, digestbuf)) {
502 				LABEL;
503 				printf(flavor == F_FREEBSD9 ?
504 				    "%s%s expected %s found %s\n" :
505 				    "%s%s (0x%s, 0x%s)\n",
506 				    tab, SHA384KEY, s->sha384digest, digestbuf);
507 			}
508 			tab = "\t";
509 			free(digestbuf);
510 		}
511 	}
512 #endif
513 	if (s->flags & F_SHA512) {
514 		if ((digestbuf = dohash(F_SHA512, p->fts_accpath)) == NULL) {
515 			LABEL;
516 			printf("%s%s: %s: %s\n",
517 			    tab, SHA512KEY, p->fts_accpath, strerror(errno));
518 			tab = "\t";
519 		} else {
520 			if (strcmp(s->sha512digest, digestbuf)) {
521 				LABEL;
522 				printf(flavor == F_FREEBSD9 ?
523 				    "%s%s expected %s found %s\n" :
524 				    "%s%s (0x%s, 0x%s)\n",
525 				    tab, SHA512KEY, s->sha512digest, digestbuf);
526 			}
527 			tab = "\t";
528 			free(digestbuf);
529 		}
530 	}
531 #endif	/* ! NO_SHA */
532 	if (s->flags & F_SLINK &&
533 	    strcmp(cp = rlink(p->fts_accpath), s->slink)) {
534 		LABEL;
535 		printf(flavor == F_FREEBSD9 ?
536 		    "%slink ref expected %s found %s" :
537 		    "%slink ref (%s, %s", tab, cp, s->slink);
538 		if (uflag) {
539 			if ((unlink(p->fts_accpath) == -1) ||
540 			    (symlink(s->slink, p->fts_accpath) == -1) )
541 				printf(", not modified: %s%s\n",
542 				    strerror(errno),
543 				    flavor == F_FREEBSD9 ? "" : ")");
544 			else
545 				printf(", modified%s\n",
546 				    flavor == F_FREEBSD9 ? "" : ")");
547 		} else
548 			printf("%s\n", flavor == F_FREEBSD9 ? "" : ")");
549 	}
550 	return (label);
551 }
552 
553 const char *
554 rlink(const char *name)
555 {
556 	static char lbuf[MAXPATHLEN];
557 	int len;
558 
559 	if ((len = readlink(name, lbuf, sizeof(lbuf) - 1)) == -1)
560 		mtree_err("%s: %s", name, strerror(errno));
561 	lbuf[len] = '\0';
562 	return (lbuf);
563 }
564