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