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