compare.c revision 1.41 1 /* $NetBSD: compare.c,v 1.41 2002/10/06 01:36:09 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.41 2002/10/06 01:36:09 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 #if HAVE_LCHFLAGS
83 #define CHANGE_LCHFLAGS if (lchflags(p->fts_accpath, flags))
84 #else
85 #define CHANGE_LCHFLAGS if (S_ISLNK(p->fts_statp->st_mode)) { \
86 label++; \
87 printf( \
88 ", not modified: no lchflags call)\n"); \
89 } else if (chflags(p->fts_accpath, flags))
90 #endif
91
92 #define CHANGEFLAGS \
93 if (flags != p->fts_statp->st_flags) { \
94 if (!label) { \
95 MARK; \
96 printf("%sflags (\"%s\"", tab, \
97 flags_to_string(p->fts_statp->st_flags, "none")); \
98 } \
99 CHANGE_LCHFLAGS { \
100 label++; \
101 printf(", not modified: %s)\n", \
102 strerror(errno)); \
103 } else \
104 printf(", modified to \"%s\")\n", \
105 flags_to_string(flags, "none")); \
106 }
107
108 /* SETFLAGS:
109 * given pflags, additionally set those flags specified in s->st_flags and
110 * selected by mask (the other flags are left unchanged).
111 */
112 #define SETFLAGS(pflags, mask) \
113 do { \
114 flags = (s->st_flags & (mask)) | (pflags); \
115 CHANGEFLAGS; \
116 } while (0)
117
118 /* CLEARFLAGS:
119 * given pflags, reset the flags specified in s->st_flags and selected by mask
120 * (the other flags are left unchanged).
121 */
122 #define CLEARFLAGS(pflags, mask) \
123 do { \
124 flags = (~(s->st_flags & (mask)) & CH_MASK) & (pflags); \
125 CHANGEFLAGS; \
126 } while (0)
127 #endif /* HAVE_STRUCT_STAT_ST_FLAGS */
128
129 int
130 compare(NODE *s, FTSENT *p)
131 {
132 u_int32_t len, val, flags;
133 int fd, label;
134 const char *cp, *tab;
135 #if !defined(NO_MD5) || !defined(NO_RMD160) || !defined(NO_SHA1)
136 char digestbuf[41]; /* large enough for {MD5,RMD160,SHA1}File() */
137 #endif
138
139 tab = NULL;
140 label = 0;
141 switch(s->type) {
142 case F_BLOCK:
143 if (!S_ISBLK(p->fts_statp->st_mode))
144 goto typeerr;
145 break;
146 case F_CHAR:
147 if (!S_ISCHR(p->fts_statp->st_mode))
148 goto typeerr;
149 break;
150 case F_DIR:
151 if (!S_ISDIR(p->fts_statp->st_mode))
152 goto typeerr;
153 break;
154 case F_FIFO:
155 if (!S_ISFIFO(p->fts_statp->st_mode))
156 goto typeerr;
157 break;
158 case F_FILE:
159 if (!S_ISREG(p->fts_statp->st_mode))
160 goto typeerr;
161 break;
162 case F_LINK:
163 if (!S_ISLNK(p->fts_statp->st_mode))
164 goto typeerr;
165 break;
166 case F_SOCK:
167 if (!S_ISSOCK(p->fts_statp->st_mode)) {
168 typeerr: LABEL;
169 printf("\ttype (%s, %s)\n",
170 nodetype(s->type), inotype(p->fts_statp->st_mode));
171 return (label);
172 }
173 break;
174 }
175 if (Wflag)
176 goto afterpermwhack;
177 #if HAVE_STRUCT_STAT_ST_FLAGS
178 if (iflag && !uflag) {
179 if (s->flags & F_FLAGS)
180 SETFLAGS(p->fts_statp->st_flags, SP_FLGS);
181 return (label);
182 }
183 if (mflag && !uflag) {
184 if (s->flags & F_FLAGS)
185 CLEARFLAGS(p->fts_statp->st_flags, SP_FLGS);
186 return (label);
187 }
188 #endif
189 if (s->flags & F_DEV &&
190 (s->type == F_BLOCK || s->type == F_CHAR) &&
191 s->st_rdev != p->fts_statp->st_rdev) {
192 LABEL;
193 printf("%sdevice (%#x, %#x",
194 tab, s->st_rdev, p->fts_statp->st_rdev);
195 if (uflag) {
196 if ((unlink(p->fts_accpath) == -1) ||
197 (mknod(p->fts_accpath,
198 s->st_mode | nodetoino(s->type),
199 s->st_rdev) == -1) ||
200 (lchown(p->fts_accpath, p->fts_statp->st_uid,
201 p->fts_statp->st_gid) == -1) )
202 printf(", not modified: %s)\n",
203 strerror(errno));
204 else
205 printf(", modified)\n");
206 } else
207 printf(")\n");
208 tab = "\t";
209 }
210 /* Set the uid/gid first, then set the mode. */
211 if (s->flags & (F_UID | F_UNAME) && s->st_uid != p->fts_statp->st_uid) {
212 LABEL;
213 printf("%suser (%lu, %lu",
214 tab, (u_long)s->st_uid, (u_long)p->fts_statp->st_uid);
215 if (uflag) {
216 if (lchown(p->fts_accpath, s->st_uid, -1))
217 printf(", not modified: %s)\n",
218 strerror(errno));
219 else
220 printf(", modified)\n");
221 } else
222 printf(")\n");
223 tab = "\t";
224 }
225 if (s->flags & (F_GID | F_GNAME) && s->st_gid != p->fts_statp->st_gid) {
226 LABEL;
227 printf("%sgid (%lu, %lu",
228 tab, (u_long)s->st_gid, (u_long)p->fts_statp->st_gid);
229 if (uflag) {
230 if (lchown(p->fts_accpath, -1, s->st_gid))
231 printf(", not modified: %s)\n",
232 strerror(errno));
233 else
234 printf(", modified)\n");
235 }
236 else
237 printf(")\n");
238 tab = "\t";
239 }
240 if (s->flags & F_MODE &&
241 s->st_mode != (p->fts_statp->st_mode & MBITS)) {
242 if (lflag) {
243 mode_t tmode, mode;
244
245 tmode = s->st_mode;
246 mode = p->fts_statp->st_mode & MBITS;
247 /*
248 * if none of the suid/sgid/etc bits are set,
249 * then if the mode is a subset of the target,
250 * skip.
251 */
252 if (!((tmode & ~(S_IRWXU|S_IRWXG|S_IRWXO)) ||
253 (mode & ~(S_IRWXU|S_IRWXG|S_IRWXO))))
254 if ((mode | tmode) == tmode)
255 goto skip;
256 }
257
258 LABEL;
259 printf("%spermissions (%#lo, %#lo",
260 tab, (u_long)s->st_mode,
261 (u_long)p->fts_statp->st_mode & MBITS);
262 if (uflag) {
263 #if HAVE_LCHMOD
264 if (lchmod(p->fts_accpath, s->st_mode))
265 #else
266 if (S_ISLNK(p->fts_statp->st_mode))
267 printf(", not modified: no lchmod call\n");
268 else if (chmod(p->fts_accpath, s->st_mode))
269 #endif
270 printf(", not modified: %s)\n",
271 strerror(errno));
272 else
273 printf(", modified)\n");
274 }
275 else
276 printf(")\n");
277 tab = "\t";
278 skip: ;
279 }
280 if (s->flags & F_NLINK && s->type != F_DIR &&
281 s->st_nlink != p->fts_statp->st_nlink) {
282 LABEL;
283 printf("%slink count (%lu, %lu)\n",
284 tab, (u_long)s->st_nlink, (u_long)p->fts_statp->st_nlink);
285 tab = "\t";
286 }
287 if (s->flags & F_SIZE && s->st_size != p->fts_statp->st_size) {
288 LABEL;
289 printf("%ssize (%lld, %lld)\n",
290 tab, (long long)s->st_size,
291 (long long)p->fts_statp->st_size);
292 tab = "\t";
293 }
294 /*
295 * XXX
296 * Since utimes(2) only takes a timeval, there's no point in
297 * comparing the low bits of the timespec nanosecond field. This
298 * will only result in mismatches that we can never fix.
299 *
300 * Doesn't display microsecond differences.
301 */
302 if (s->flags & F_TIME) {
303 struct timeval tv[2];
304 struct stat *ps = p->fts_statp;
305 time_t smtime = s->st_mtimespec.tv_sec;
306
307 #ifdef BSD4_4
308 time_t pmtime = ps->st_mtimespec.tv_sec;
309
310 TIMESPEC_TO_TIMEVAL(&tv[0], &s->st_mtimespec);
311 TIMESPEC_TO_TIMEVAL(&tv[1], &ps->st_mtimespec);
312 #else
313 time_t pmtime = (time_t)ps->st_mtime;
314
315 tv[0].tv_sec = smtime;
316 tv[0].tv_usec = 0;
317 tv[1].tv_sec = pmtime;
318 tv[1].tv_usec = 0;
319 #endif
320
321 if (tv[0].tv_sec != tv[1].tv_sec ||
322 tv[0].tv_usec != tv[1].tv_usec) {
323 LABEL;
324 printf("%smodification time (%.24s, ",
325 tab, ctime(&smtime));
326 printf("%.24s", ctime(&pmtime));
327 if (tflag) {
328 tv[1] = tv[0];
329 if (utimes(p->fts_accpath, tv))
330 printf(", not modified: %s)\n",
331 strerror(errno));
332 else
333 printf(", modified)\n");
334 } else
335 printf(")\n");
336 tab = "\t";
337 }
338 }
339 #if HAVE_STRUCT_STAT_ST_FLAGS
340 /*
341 * XXX
342 * since lchflags(2) will reset file times, the utimes() above
343 * may have been useless! oh well, we'd rather have correct
344 * flags, rather than times?
345 */
346 if ((s->flags & F_FLAGS) && ((s->st_flags != p->fts_statp->st_flags)
347 || mflag || iflag)) {
348 if (s->st_flags != p->fts_statp->st_flags) {
349 LABEL;
350 printf("%sflags (\"%s\" is not ", tab,
351 flags_to_string(s->st_flags, "none"));
352 printf("\"%s\"",
353 flags_to_string(p->fts_statp->st_flags, "none"));
354 }
355 if (uflag) {
356 if (iflag)
357 SETFLAGS(0, CH_MASK);
358 else if (mflag)
359 CLEARFLAGS(0, SP_FLGS);
360 else
361 SETFLAGS(0, (~SP_FLGS & CH_MASK));
362 } else
363 printf(")\n");
364 tab = "\t";
365 }
366 #endif /* HAVE_STRUCT_STAT_ST_FLAGS */
367
368 /*
369 * from this point, no more permission checking or whacking
370 * occurs, only checking of stuff like checksums and symlinks.
371 */
372 afterpermwhack:
373 if (s->flags & F_CKSUM) {
374 if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0) {
375 LABEL;
376 printf("%scksum: %s: %s\n",
377 tab, p->fts_accpath, strerror(errno));
378 tab = "\t";
379 } else if (crc(fd, &val, &len)) {
380 close(fd);
381 LABEL;
382 printf("%scksum: %s: %s\n",
383 tab, p->fts_accpath, strerror(errno));
384 tab = "\t";
385 } else {
386 close(fd);
387 if (s->cksum != val) {
388 LABEL;
389 printf("%scksum (%lu, %lu)\n",
390 tab, s->cksum, (unsigned long)val);
391 }
392 tab = "\t";
393 }
394 }
395 #ifndef NO_MD5
396 if (s->flags & F_MD5) {
397 if (MD5File(p->fts_accpath, digestbuf) == NULL) {
398 LABEL;
399 printf("%smd5: %s: %s\n",
400 tab, p->fts_accpath, strerror(errno));
401 tab = "\t";
402 } else {
403 if (strcmp(s->md5digest, digestbuf)) {
404 LABEL;
405 printf("%smd5 (0x%s, 0x%s)\n",
406 tab, s->md5digest, digestbuf);
407 }
408 tab = "\t";
409 }
410 }
411 #endif /* ! NO_MD5 */
412 #ifndef NO_RMD160
413 if (s->flags & F_RMD160) {
414 if (RMD160File(p->fts_accpath, digestbuf) == NULL) {
415 LABEL;
416 printf("%srmd160: %s: %s\n",
417 tab, p->fts_accpath, strerror(errno));
418 tab = "\t";
419 } else {
420 if (strcmp(s->rmd160digest, digestbuf)) {
421 LABEL;
422 printf("%srmd160 (0x%s, 0x%s)\n",
423 tab, s->rmd160digest, digestbuf);
424 }
425 tab = "\t";
426 }
427 }
428 #endif /* ! NO_RMD160 */
429 #ifndef NO_SHA1
430 if (s->flags & F_SHA1) {
431 if (SHA1File(p->fts_accpath, digestbuf) == NULL) {
432 LABEL;
433 printf("%ssha1: %s: %s\n",
434 tab, p->fts_accpath, strerror(errno));
435 tab = "\t";
436 } else {
437 if (strcmp(s->sha1digest, digestbuf)) {
438 LABEL;
439 printf("%ssha1 (0x%s, 0x%s)\n",
440 tab, s->sha1digest, digestbuf);
441 }
442 tab = "\t";
443 }
444 }
445 #endif /* ! NO_SHA1 */
446 if (s->flags & F_SLINK &&
447 strcmp(cp = rlink(p->fts_accpath), s->slink)) {
448 LABEL;
449 printf("%slink ref (%s, %s", tab, cp, s->slink);
450 if (uflag) {
451 if ((unlink(p->fts_accpath) == -1) ||
452 (symlink(s->slink, p->fts_accpath) == -1) )
453 printf(", not modified: %s)\n",
454 strerror(errno));
455 else
456 printf(", modified)\n");
457 } else
458 printf(")\n");
459 }
460 return (label);
461 }
462
463 const char *
464 rlink(const char *name)
465 {
466 static char lbuf[MAXPATHLEN];
467 int len;
468
469 if ((len = readlink(name, lbuf, sizeof(lbuf) - 1)) == -1)
470 mtree_err("%s: %s", name, strerror(errno));
471 lbuf[len] = '\0';
472 return (lbuf);
473 }
474