xref: /openbsd-src/usr.sbin/mtree/compare.c (revision d13be5d47e4149db2549a9828e244d59dbc43f15)
1 /*	$NetBSD: compare.c,v 1.11 1996/09/05 09:56:48 mycroft Exp $	*/
2 /*	$OpenBSD: compare.c,v 1.22 2009/10/27 23:59:53 deraadt 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 <fcntl.h>
36 #include <fts.h>
37 #include <errno.h>
38 #include <stdio.h>
39 #include <time.h>
40 #include <unistd.h>
41 #include <md5.h>
42 #include <sha1.h>
43 #include <rmd160.h>
44 #include "mtree.h"
45 #include "extern.h"
46 
47 extern int lflag, tflag, uflag;
48 
49 static char *ftype(u_int);
50 
51 #define	INDENTNAMELEN	8
52 #define	LABEL \
53 	if (!label++) { \
54 		len = printf("%s: ", RP(p)); \
55 		if (len > INDENTNAMELEN) { \
56 			tab = "\t"; \
57 			(void)printf("\n"); \
58 		} else { \
59 			tab = ""; \
60 			(void)printf("%*s", INDENTNAMELEN - (int)len, ""); \
61 		} \
62 	}
63 
64 #define REPLACE_COMMA(x)						\
65 	do {								\
66 		char *l;						\
67 		for (l = x; *l; l++) {					\
68 			if (*l == ',')					\
69 				*l = ' ';				\
70 		}							\
71 	} while (0)							\
72 
73 int
74 compare(char *name, NODE *s, FTSENT *p)
75 {
76 	u_int32_t len, val;
77 	int fd, label;
78 	char *cp, *tab = "";
79 
80 	label = 0;
81 	switch(s->type) {
82 	case F_BLOCK:
83 		if (!S_ISBLK(p->fts_statp->st_mode))
84 			goto typeerr;
85 		break;
86 	case F_CHAR:
87 		if (!S_ISCHR(p->fts_statp->st_mode))
88 			goto typeerr;
89 		break;
90 	case F_DIR:
91 		if (!S_ISDIR(p->fts_statp->st_mode))
92 			goto typeerr;
93 		break;
94 	case F_FIFO:
95 		if (!S_ISFIFO(p->fts_statp->st_mode))
96 			goto typeerr;
97 		break;
98 	case F_FILE:
99 		if (!S_ISREG(p->fts_statp->st_mode))
100 			goto typeerr;
101 		break;
102 	case F_LINK:
103 		if (!S_ISLNK(p->fts_statp->st_mode))
104 			goto typeerr;
105 		break;
106 	case F_SOCK:
107 		if (!S_ISSOCK(p->fts_statp->st_mode)) {
108 typeerr:		LABEL;
109 			(void)printf("\ttype (%s, %s)\n",
110 			    ftype(s->type), inotype(p->fts_statp->st_mode));
111 		}
112 		break;
113 	}
114 	/* Set the uid/gid first, then set the mode. */
115 	if (s->flags & (F_UID | F_UNAME) && s->st_uid != p->fts_statp->st_uid) {
116 		LABEL;
117 		(void)printf("%suser (%u, %u",
118 		    tab, s->st_uid, p->fts_statp->st_uid);
119 		if (uflag)
120 			if (chown(p->fts_accpath, s->st_uid, -1))
121 				(void)printf(", not modified: %s)\n",
122 				    strerror(errno));
123 			else
124 				(void)printf(", modified)\n");
125 		else
126 			(void)printf(")\n");
127 		tab = "\t";
128 	}
129 	if (s->flags & (F_GID | F_GNAME) && s->st_gid != p->fts_statp->st_gid) {
130 		LABEL;
131 		(void)printf("%sgid (%u, %u",
132 		    tab, s->st_gid, p->fts_statp->st_gid);
133 		if (uflag)
134 			if (chown(p->fts_accpath, -1, s->st_gid))
135 				(void)printf(", not modified: %s)\n",
136 				    strerror(errno));
137 			else
138 				(void)printf(", modified)\n");
139 		else
140 			(void)printf(")\n");
141 		tab = "\t";
142 	}
143 	if (s->flags & F_MODE &&
144 	    s->st_mode != (p->fts_statp->st_mode & MBITS)) {
145 		if (lflag) {
146 			mode_t tmode, mode;
147 
148 			tmode = s->st_mode;
149 			mode = p->fts_statp->st_mode & MBITS;
150 			/*
151 			 * if none of the suid/sgid/etc bits are set,
152 			 * then if the mode is a subset of the target,
153 			 * skip.
154 			 */
155 			if (!((tmode & ~(S_IRWXU|S_IRWXG|S_IRWXO)) ||
156 			    (mode & ~(S_IRWXU|S_IRWXG|S_IRWXO))))
157 				if ((mode | tmode) == tmode)
158 					goto skip;
159 		}
160 		LABEL;
161 		(void)printf("%spermissions (%#o, %#o",
162 		    tab, s->st_mode, p->fts_statp->st_mode & MBITS);
163 		if (uflag)
164 			if (chmod(p->fts_accpath, s->st_mode))
165 				(void)printf(", not modified: %s)\n",
166 				    strerror(errno));
167 			else
168 				(void)printf(", modified)\n");
169 		else
170 			(void)printf(")\n");
171 		tab = "\t";
172 	skip:
173 		;
174 	}
175 	if (s->flags & F_NLINK && s->type != F_DIR &&
176 	    s->st_nlink != p->fts_statp->st_nlink) {
177 		LABEL;
178 		(void)printf("%slink count (%u, %u)\n",
179 		    tab, s->st_nlink, p->fts_statp->st_nlink);
180 		tab = "\t";
181 	}
182 	if (s->flags & F_SIZE && s->st_size != p->fts_statp->st_size) {
183 		LABEL;
184 		(void)printf("%ssize (%qd, %qd)\n",
185 		    tab, s->st_size, p->fts_statp->st_size);
186 		tab = "\t";
187 	}
188 	/*
189 	 * XXX
190 	 * Since utimes(2) only takes a timeval, there's no point in
191 	 * comparing the low bits of the timespec nanosecond field.  This
192 	 * will only result in mismatches that we can never fix.
193 	 *
194 	 * Doesn't display microsecond differences.
195 	 */
196 	if (s->flags & F_TIME) {
197 		struct timeval tv[2];
198 
199 		TIMESPEC_TO_TIMEVAL(&tv[0], &s->st_mtimespec);
200 		TIMESPEC_TO_TIMEVAL(&tv[1], &p->fts_statp->st_mtimespec);
201 		if (tv[0].tv_sec != tv[1].tv_sec ||
202 		    tv[0].tv_usec != tv[1].tv_usec) {
203 			LABEL;
204 			(void)printf("%smodification time (%.24s, ",
205 			    tab, ctime(&s->st_mtimespec.tv_sec));
206 			(void)printf("%.24s",
207 			    ctime(&p->fts_statp->st_mtimespec.tv_sec));
208 			if (tflag) {
209 				tv[1] = tv[0];
210 				if (utimes(p->fts_accpath, tv))
211 					(void)printf(", not modified: %s)\n",
212 					    strerror(errno));
213 				else
214 					(void)printf(", modified)\n");
215 			} else
216 				(void)printf(")\n");
217 			tab = "\t";
218 		}
219 	}
220 	if (s->flags & F_CKSUM) {
221 		if ((fd = open(p->fts_accpath, MTREE_O_FLAGS, 0)) < 0) {
222 			LABEL;
223 			(void)printf("%scksum: %s: %s\n",
224 			    tab, p->fts_accpath, strerror(errno));
225 			tab = "\t";
226 		} else if (crc(fd, &val, &len)) {
227 			(void)close(fd);
228 			LABEL;
229 			(void)printf("%scksum: %s: %s\n",
230 			    tab, p->fts_accpath, strerror(errno));
231 			tab = "\t";
232 		} else {
233 			(void)close(fd);
234 			if (s->cksum != val) {
235 				LABEL;
236 				(void)printf("%scksum (%u, %u)\n",
237 				    tab, s->cksum, val);
238 			}
239 			tab = "\t";
240 		}
241 	}
242 	if (s->flags & F_MD5) {
243 		char *new_digest, buf[MD5_DIGEST_STRING_LENGTH];
244 
245 		new_digest = MD5File(p->fts_accpath, buf);
246 		if (!new_digest) {
247 			LABEL;
248 			printf("%sMD5File: %s: %s\n", tab, p->fts_accpath,
249 			       strerror(errno));
250 			tab = "\t";
251 		} else if (strcmp(new_digest, s->md5digest)) {
252 			LABEL;
253 			printf("%sMD5 (%s, %s)\n", tab, s->md5digest,
254 			       new_digest);
255 			tab = "\t";
256 		}
257 	}
258 	if (s->flags & F_RMD160) {
259 		char *new_digest, buf[RMD160_DIGEST_STRING_LENGTH];
260 
261 		new_digest = RMD160File(p->fts_accpath, buf);
262 		if (!new_digest) {
263 			LABEL;
264 			printf("%sRMD160File: %s: %s\n", tab, p->fts_accpath,
265 			       strerror(errno));
266 			tab = "\t";
267 		} else if (strcmp(new_digest, s->rmd160digest)) {
268 			LABEL;
269 			printf("%sRMD160 (%s, %s)\n", tab, s->rmd160digest,
270 			       new_digest);
271 			tab = "\t";
272 		}
273 	}
274 	if (s->flags & F_SHA1) {
275 		char *new_digest, buf[SHA1_DIGEST_STRING_LENGTH];
276 
277 		new_digest = SHA1File(p->fts_accpath, buf);
278 		if (!new_digest) {
279 			LABEL;
280 			printf("%sSHA1File: %s: %s\n", tab, p->fts_accpath,
281 			       strerror(errno));
282 			tab = "\t";
283 		} else if (strcmp(new_digest, s->sha1digest)) {
284 			LABEL;
285 			printf("%sSHA1 (%s, %s)\n", tab, s->sha1digest,
286 			       new_digest);
287 			tab = "\t";
288 		}
289 	}
290 	if (s->flags & F_SLINK && strcmp(cp = rlink(name), s->slink)) {
291 		LABEL;
292 		(void)printf("%slink ref (%s, %s)\n", tab, cp, s->slink);
293 	}
294 	if (s->flags & F_FLAGS && s->file_flags != p->fts_statp->st_flags) {
295 		char *db_flags = NULL;
296 		char *cur_flags = NULL;
297 
298 		if ((db_flags = fflagstostr(s->file_flags)) == NULL ||
299 		    (cur_flags = fflagstostr(p->fts_statp->st_flags)) == NULL) {
300 			LABEL;
301 			(void)printf("%sflags: %s %s\n", tab, p->fts_accpath,
302 				     strerror(errno));
303 			tab = "\t";
304 			if (db_flags != NULL)
305 				free(db_flags);
306 			if (cur_flags != NULL)
307 				free(cur_flags);
308 		} else {
309 			LABEL;
310 			REPLACE_COMMA(db_flags);
311 			REPLACE_COMMA(cur_flags);
312 			printf("%sflags (%s, %s", tab, (*db_flags == '\0') ?
313 						  "-" : db_flags,
314 						  (*cur_flags == '\0') ?
315 						  "-" : cur_flags);
316 				tab = "\t";
317 			if (uflag)
318 				if (chflags(p->fts_accpath, s->file_flags))
319 					(void)printf(", not modified: %s)\n",
320 						strerror(errno));
321 				else
322 					(void)printf(", modified)\n");
323 			else
324 				(void)printf(")\n");
325 			tab = "\t";
326 
327 			free(db_flags);
328 			free(cur_flags);
329 		}
330 	}
331 	return (label);
332 }
333 
334 char *
335 inotype(u_int type)
336 {
337 	switch(type & S_IFMT) {
338 	case S_IFBLK:
339 		return ("block");
340 	case S_IFCHR:
341 		return ("char");
342 	case S_IFDIR:
343 		return ("dir");
344 	case S_IFIFO:
345 		return ("fifo");
346 	case S_IFREG:
347 		return ("file");
348 	case S_IFLNK:
349 		return ("link");
350 	case S_IFSOCK:
351 		return ("socket");
352 	default:
353 		return ("unknown");
354 	}
355 	/* NOTREACHED */
356 }
357 
358 static char *
359 ftype(u_int type)
360 {
361 	switch(type) {
362 	case F_BLOCK:
363 		return ("block");
364 	case F_CHAR:
365 		return ("char");
366 	case F_DIR:
367 		return ("dir");
368 	case F_FIFO:
369 		return ("fifo");
370 	case F_FILE:
371 		return ("file");
372 	case F_LINK:
373 		return ("link");
374 	case F_SOCK:
375 		return ("socket");
376 	default:
377 		return ("unknown");
378 	}
379 	/* NOTREACHED */
380 }
381 
382 char *
383 rlink(char *name)
384 {
385 	static char lbuf[MAXPATHLEN];
386 	int len;
387 
388 	if ((len = readlink(name, lbuf, sizeof(lbuf)-1)) == -1)
389 		error("%s: %s", name, strerror(errno));
390 	lbuf[len] = '\0';
391 	return (lbuf);
392 }
393