compare.c revision 1.42 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