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