xref: /openbsd-src/usr.sbin/mtree/compare.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /*	$NetBSD: compare.c,v 1.11 1996/09/05 09:56:48 mycroft Exp $	*/
2 /*	$OpenBSD: compare.c,v 1.12 2001/08/10 02:37:14 millert 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. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by the University of
19  *	California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36 
37 #ifndef lint
38 #if 0
39 static const char sccsid[] = "@(#)compare.c	8.1 (Berkeley) 6/6/93";
40 #else
41 static const char rcsid[] = "$OpenBSD: compare.c,v 1.12 2001/08/10 02:37:14 millert Exp $";
42 #endif
43 #endif /* not lint */
44 
45 #include <sys/param.h>
46 #include <sys/stat.h>
47 #include <fcntl.h>
48 #include <fts.h>
49 #include <errno.h>
50 #include <stdio.h>
51 #include <time.h>
52 #include <unistd.h>
53 #include <md5.h>
54 #include <sha1.h>
55 #include <rmd160.h>
56 #include "mtree.h"
57 #include "extern.h"
58 
59 extern int tflag, uflag;
60 
61 static char *ftype __P((u_int));
62 
63 #define	INDENTNAMELEN	8
64 #define	LABEL \
65 	if (!label++) { \
66 		len = printf("%s: ", RP(p)); \
67 		if (len > INDENTNAMELEN) { \
68 			tab = "\t"; \
69 			(void)printf("\n"); \
70 		} else { \
71 			tab = ""; \
72 			(void)printf("%*s", INDENTNAMELEN - (int)len, ""); \
73 		} \
74 	}
75 
76 #define REPLACE_COMMA(x)						\
77 	do {								\
78 		char *l;						\
79 		for (l = x; *l; l++) {					\
80 			if (*l == ',')					\
81 				*l = ' ';				\
82 		}							\
83 	} while (0)							\
84 
85 int
86 compare(name, s, p)
87 	char *name;
88 	register NODE *s;
89 	register FTSENT *p;
90 {
91 	u_int32_t len, val;
92 	int fd, label;
93 	char *cp, *tab = "";
94 
95 	label = 0;
96 	switch(s->type) {
97 	case F_BLOCK:
98 		if (!S_ISBLK(p->fts_statp->st_mode))
99 			goto typeerr;
100 		break;
101 	case F_CHAR:
102 		if (!S_ISCHR(p->fts_statp->st_mode))
103 			goto typeerr;
104 		break;
105 	case F_DIR:
106 		if (!S_ISDIR(p->fts_statp->st_mode))
107 			goto typeerr;
108 		break;
109 	case F_FIFO:
110 		if (!S_ISFIFO(p->fts_statp->st_mode))
111 			goto typeerr;
112 		break;
113 	case F_FILE:
114 		if (!S_ISREG(p->fts_statp->st_mode))
115 			goto typeerr;
116 		break;
117 	case F_LINK:
118 		if (!S_ISLNK(p->fts_statp->st_mode))
119 			goto typeerr;
120 		break;
121 	case F_SOCK:
122 		if (!S_ISSOCK(p->fts_statp->st_mode)) {
123 typeerr:		LABEL;
124 			(void)printf("\ttype (%s, %s)\n",
125 			    ftype(s->type), inotype(p->fts_statp->st_mode));
126 		}
127 		break;
128 	}
129 	/* Set the uid/gid first, then set the mode. */
130 	if (s->flags & (F_UID | F_UNAME) && s->st_uid != p->fts_statp->st_uid) {
131 		LABEL;
132 		(void)printf("%suser (%u, %u",
133 		    tab, s->st_uid, p->fts_statp->st_uid);
134 		if (uflag)
135 			if (chown(p->fts_accpath, s->st_uid, -1))
136 				(void)printf(", not modified: %s)\n",
137 				    strerror(errno));
138 			else
139 				(void)printf(", modified)\n");
140 		else
141 			(void)printf(")\n");
142 		tab = "\t";
143 	}
144 	if (s->flags & (F_GID | F_GNAME) && s->st_gid != p->fts_statp->st_gid) {
145 		LABEL;
146 		(void)printf("%sgid (%u, %u",
147 		    tab, s->st_gid, p->fts_statp->st_gid);
148 		if (uflag)
149 			if (chown(p->fts_accpath, -1, s->st_gid))
150 				(void)printf(", not modified: %s)\n",
151 				    strerror(errno));
152 			else
153 				(void)printf(", modified)\n");
154 		else
155 			(void)printf(")\n");
156 		tab = "\t";
157 	}
158 	if (s->flags & F_MODE &&
159 	    s->st_mode != (p->fts_statp->st_mode & MBITS)) {
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 	}
173 	if (s->flags & F_NLINK && s->type != F_DIR &&
174 	    s->st_nlink != p->fts_statp->st_nlink) {
175 		LABEL;
176 		(void)printf("%slink count (%u, %u)\n",
177 		    tab, s->st_nlink, p->fts_statp->st_nlink);
178 		tab = "\t";
179 	}
180 	if (s->flags & F_SIZE && s->st_size != p->fts_statp->st_size) {
181 		LABEL;
182 		(void)printf("%ssize (%qd, %qd)\n",
183 		    tab, s->st_size, p->fts_statp->st_size);
184 		tab = "\t";
185 	}
186 	/*
187 	 * XXX
188 	 * Since utimes(2) only takes a timeval, there's no point in
189 	 * comparing the low bits of the timespec nanosecond field.  This
190 	 * will only result in mismatches that we can never fix.
191 	 *
192 	 * Doesn't display microsecond differences.
193 	 */
194 	if (s->flags & F_TIME) {
195 		struct timeval tv[2];
196 
197 		TIMESPEC_TO_TIMEVAL(&tv[0], &s->st_mtimespec);
198 		TIMESPEC_TO_TIMEVAL(&tv[1], &p->fts_statp->st_mtimespec);
199 		if (tv[0].tv_sec != tv[1].tv_sec ||
200 		    tv[0].tv_usec != tv[1].tv_usec) {
201 			LABEL;
202 			(void)printf("%smodification time (%.24s, ",
203 			    tab, ctime(&s->st_mtimespec.tv_sec));
204 			(void)printf("%.24s",
205 			    ctime(&p->fts_statp->st_mtimespec.tv_sec));
206 			if (tflag) {
207 				tv[1] = tv[0];
208 				if (utimes(p->fts_accpath, tv))
209 					(void)printf(", not modified: %s)\n",
210 					    strerror(errno));
211 				else
212 					(void)printf(", modified)\n");
213 			} else
214 				(void)printf(")\n");
215 			tab = "\t";
216 		}
217 	}
218 	if (s->flags & F_CKSUM) {
219 		if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0) {
220 			LABEL;
221 			(void)printf("%scksum: %s: %s\n",
222 			    tab, p->fts_accpath, strerror(errno));
223 			tab = "\t";
224 		} else if (crc(fd, &val, &len)) {
225 			(void)close(fd);
226 			LABEL;
227 			(void)printf("%scksum: %s: %s\n",
228 			    tab, p->fts_accpath, strerror(errno));
229 			tab = "\t";
230 		} else {
231 			(void)close(fd);
232 			if (s->cksum != val) {
233 				LABEL;
234 				(void)printf("%scksum (%u, %u)\n",
235 				    tab, s->cksum, val);
236 			}
237 			tab = "\t";
238 		}
239 	}
240 	if (s->flags & F_MD5) {
241 		char *new_digest, buf[33];
242 
243 		new_digest = MD5File(p->fts_accpath, buf);
244 		if (!new_digest) {
245 			LABEL;
246 			printf("%sMD5File: %s: %s\n", tab, p->fts_accpath,
247 			       strerror(errno));
248 			tab = "\t";
249 		} else if (strcmp(new_digest, s->md5digest)) {
250 			LABEL;
251 			printf("%sMD5 (%s, %s)\n", tab, s->md5digest,
252 			       new_digest);
253 			tab = "\t";
254 		}
255 	}
256 	if (s->flags & F_RMD160) {
257 		char *new_digest, buf[41];
258 
259 		new_digest = RMD160File(p->fts_accpath, buf);
260 		if (!new_digest) {
261 			LABEL;
262 			printf("%sRMD160File: %s: %s\n", tab, p->fts_accpath,
263 			       strerror(errno));
264 			tab = "\t";
265 		} else if (strcmp(new_digest, s->rmd160digest)) {
266 			LABEL;
267 			printf("%sRMD160 (%s, %s)\n", tab, s->rmd160digest,
268 			       new_digest);
269 			tab = "\t";
270 		}
271 	}
272 	if (s->flags & F_SHA1) {
273 		char *new_digest, buf[41];
274 
275 		new_digest = SHA1File(p->fts_accpath, buf);
276 		if (!new_digest) {
277 			LABEL;
278 			printf("%sSHA1File: %s: %s\n", tab, p->fts_accpath,
279 			       strerror(errno));
280 			tab = "\t";
281 		} else if (strcmp(new_digest, s->sha1digest)) {
282 			LABEL;
283 			printf("%sSHA1 (%s, %s)\n", tab, s->sha1digest,
284 			       new_digest);
285 			tab = "\t";
286 		}
287 	}
288 	if (s->flags & F_SLINK && strcmp(cp = rlink(name), s->slink)) {
289 		LABEL;
290 		(void)printf("%slink ref (%s, %s)\n", tab, cp, s->slink);
291 	}
292 	if (s->flags & F_FLAGS && s->file_flags != p->fts_statp->st_flags) {
293 		char *db_flags = NULL;
294 		char *cur_flags = NULL;
295 
296 		if ((db_flags = fflagstostr(s->file_flags)) == NULL ||
297 		    (cur_flags = fflagstostr(p->fts_statp->st_flags)) == NULL) {
298 			LABEL;
299 			(void)printf("%sflags: %s %s\n", tab, p->fts_accpath,
300 				     strerror(errno));
301 			tab = "\t";
302 			if (db_flags != NULL)
303 				free(db_flags);
304 			if (cur_flags != NULL)
305 				free(cur_flags);
306 		} else {
307 			LABEL;
308 			REPLACE_COMMA(db_flags);
309 			REPLACE_COMMA(cur_flags);
310 			printf("%sflags (%s, %s", tab, (*db_flags == '\0') ?
311 						  "-" : db_flags,
312 						  (*cur_flags == '\0') ?
313 						  "-" : cur_flags);
314 				tab = "\t";
315 			if (uflag)
316 				if (chflags(p->fts_accpath, s->file_flags))
317 					(void)printf(", not modified: %s)\n",
318 						strerror(errno));
319 				else
320 					(void)printf(", modified)\n");
321 			else
322 				(void)printf(")\n");
323 			tab = "\t";
324 
325 			free(db_flags);
326 			free(cur_flags);
327 		}
328 	}
329 	return (label);
330 }
331 
332 char *
333 inotype(type)
334 	u_int type;
335 {
336 	switch(type & S_IFMT) {
337 	case S_IFBLK:
338 		return ("block");
339 	case S_IFCHR:
340 		return ("char");
341 	case S_IFDIR:
342 		return ("dir");
343 	case S_IFIFO:
344 		return ("fifo");
345 	case S_IFREG:
346 		return ("file");
347 	case S_IFLNK:
348 		return ("link");
349 	case S_IFSOCK:
350 		return ("socket");
351 	default:
352 		return ("unknown");
353 	}
354 	/* NOTREACHED */
355 }
356 
357 static char *
358 ftype(type)
359 	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(name)
384 	char *name;
385 {
386 	static char lbuf[MAXPATHLEN];
387 	register int len;
388 
389 	if ((len = readlink(name, lbuf, sizeof(lbuf)-1)) == -1)
390 		error("%s: %s", name, strerror(errno));
391 	lbuf[len] = '\0';
392 	return (lbuf);
393 }
394