compare.c revision 1.44 1 /* $NetBSD: compare.c,v 1.44 2004/06/20 22:20:18 jmc 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. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #if HAVE_NBTOOL_CONFIG_H
33 #include "nbtool_config.h"
34 #endif
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.44 2004/06/20 22:20:18 jmc 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 #ifdef S_ISSOCK
158 case F_SOCK:
159 if (!S_ISSOCK(p->fts_statp->st_mode))
160 goto typeerr;
161 break;
162 #endif
163 typeerr: LABEL;
164 printf("\ttype (%s, %s)\n",
165 nodetype(s->type), inotype(p->fts_statp->st_mode));
166 return (label);
167 }
168 if (Wflag)
169 goto afterpermwhack;
170 #if HAVE_STRUCT_STAT_ST_FLAGS
171 if (iflag && !uflag) {
172 if (s->flags & F_FLAGS)
173 SETFLAGS(p->fts_statp->st_flags, SP_FLGS);
174 return (label);
175 }
176 if (mflag && !uflag) {
177 if (s->flags & F_FLAGS)
178 CLEARFLAGS(p->fts_statp->st_flags, SP_FLGS);
179 return (label);
180 }
181 #endif
182 if (s->flags & F_DEV &&
183 (s->type == F_BLOCK || s->type == F_CHAR) &&
184 s->st_rdev != p->fts_statp->st_rdev) {
185 LABEL;
186 printf("%sdevice (%#x, %#x",
187 tab, s->st_rdev, p->fts_statp->st_rdev);
188 if (uflag) {
189 if ((unlink(p->fts_accpath) == -1) ||
190 (mknod(p->fts_accpath,
191 s->st_mode | nodetoino(s->type),
192 s->st_rdev) == -1) ||
193 (lchown(p->fts_accpath, p->fts_statp->st_uid,
194 p->fts_statp->st_gid) == -1) )
195 printf(", not modified: %s)\n",
196 strerror(errno));
197 else
198 printf(", modified)\n");
199 } else
200 printf(")\n");
201 tab = "\t";
202 }
203 /* Set the uid/gid first, then set the mode. */
204 if (s->flags & (F_UID | F_UNAME) && s->st_uid != p->fts_statp->st_uid) {
205 LABEL;
206 printf("%suser (%lu, %lu",
207 tab, (u_long)s->st_uid, (u_long)p->fts_statp->st_uid);
208 if (uflag) {
209 if (lchown(p->fts_accpath, s->st_uid, -1))
210 printf(", not modified: %s)\n",
211 strerror(errno));
212 else
213 printf(", modified)\n");
214 } else
215 printf(")\n");
216 tab = "\t";
217 }
218 if (s->flags & (F_GID | F_GNAME) && s->st_gid != p->fts_statp->st_gid) {
219 LABEL;
220 printf("%sgid (%lu, %lu",
221 tab, (u_long)s->st_gid, (u_long)p->fts_statp->st_gid);
222 if (uflag) {
223 if (lchown(p->fts_accpath, -1, s->st_gid))
224 printf(", not modified: %s)\n",
225 strerror(errno));
226 else
227 printf(", modified)\n");
228 }
229 else
230 printf(")\n");
231 tab = "\t";
232 }
233 if (s->flags & F_MODE &&
234 s->st_mode != (p->fts_statp->st_mode & MBITS)) {
235 if (lflag) {
236 mode_t tmode, mode;
237
238 tmode = s->st_mode;
239 mode = p->fts_statp->st_mode & MBITS;
240 /*
241 * if none of the suid/sgid/etc bits are set,
242 * then if the mode is a subset of the target,
243 * skip.
244 */
245 if (!((tmode & ~(S_IRWXU|S_IRWXG|S_IRWXO)) ||
246 (mode & ~(S_IRWXU|S_IRWXG|S_IRWXO))))
247 if ((mode | tmode) == tmode)
248 goto skip;
249 }
250
251 LABEL;
252 printf("%spermissions (%#lo, %#lo",
253 tab, (u_long)s->st_mode,
254 (u_long)p->fts_statp->st_mode & MBITS);
255 if (uflag) {
256 if (lchmod(p->fts_accpath, s->st_mode))
257 printf(", not modified: %s)\n",
258 strerror(errno));
259 else
260 printf(", modified)\n");
261 }
262 else
263 printf(")\n");
264 tab = "\t";
265 skip: ;
266 }
267 if (s->flags & F_NLINK && s->type != F_DIR &&
268 s->st_nlink != p->fts_statp->st_nlink) {
269 LABEL;
270 printf("%slink count (%lu, %lu)\n",
271 tab, (u_long)s->st_nlink, (u_long)p->fts_statp->st_nlink);
272 tab = "\t";
273 }
274 if (s->flags & F_SIZE && s->st_size != p->fts_statp->st_size) {
275 LABEL;
276 printf("%ssize (%lld, %lld)\n",
277 tab, (long long)s->st_size,
278 (long long)p->fts_statp->st_size);
279 tab = "\t";
280 }
281 /*
282 * XXX
283 * Since utimes(2) only takes a timeval, there's no point in
284 * comparing the low bits of the timespec nanosecond field. This
285 * will only result in mismatches that we can never fix.
286 *
287 * Doesn't display microsecond differences.
288 */
289 if (s->flags & F_TIME) {
290 struct timeval tv[2];
291 struct stat *ps = p->fts_statp;
292 time_t smtime = s->st_mtimespec.tv_sec;
293
294 #if defined(BSD4_4) && !defined(HAVE_NBTOOL_CONFIG_H)
295 time_t pmtime = ps->st_mtimespec.tv_sec;
296
297 TIMESPEC_TO_TIMEVAL(&tv[0], &s->st_mtimespec);
298 TIMESPEC_TO_TIMEVAL(&tv[1], &ps->st_mtimespec);
299 #else
300 time_t pmtime = (time_t)ps->st_mtime;
301
302 tv[0].tv_sec = smtime;
303 tv[0].tv_usec = 0;
304 tv[1].tv_sec = pmtime;
305 tv[1].tv_usec = 0;
306 #endif
307
308 if (tv[0].tv_sec != tv[1].tv_sec ||
309 tv[0].tv_usec != tv[1].tv_usec) {
310 LABEL;
311 printf("%smodification time (%.24s, ",
312 tab, ctime(&smtime));
313 printf("%.24s", ctime(&pmtime));
314 if (tflag) {
315 tv[1] = tv[0];
316 if (utimes(p->fts_accpath, tv))
317 printf(", not modified: %s)\n",
318 strerror(errno));
319 else
320 printf(", modified)\n");
321 } else
322 printf(")\n");
323 tab = "\t";
324 }
325 }
326 #if HAVE_STRUCT_STAT_ST_FLAGS
327 /*
328 * XXX
329 * since lchflags(2) will reset file times, the utimes() above
330 * may have been useless! oh well, we'd rather have correct
331 * flags, rather than times?
332 */
333 if ((s->flags & F_FLAGS) && ((s->st_flags != p->fts_statp->st_flags)
334 || mflag || iflag)) {
335 if (s->st_flags != p->fts_statp->st_flags) {
336 LABEL;
337 printf("%sflags (\"%s\" is not ", tab,
338 flags_to_string(s->st_flags, "none"));
339 printf("\"%s\"",
340 flags_to_string(p->fts_statp->st_flags, "none"));
341 }
342 if (uflag) {
343 if (iflag)
344 SETFLAGS(0, CH_MASK);
345 else if (mflag)
346 CLEARFLAGS(0, SP_FLGS);
347 else
348 SETFLAGS(0, (~SP_FLGS & CH_MASK));
349 } else
350 printf(")\n");
351 tab = "\t";
352 }
353 #endif /* HAVE_STRUCT_STAT_ST_FLAGS */
354
355 /*
356 * from this point, no more permission checking or whacking
357 * occurs, only checking of stuff like checksums and symlinks.
358 */
359 afterpermwhack:
360 if (s->flags & F_CKSUM) {
361 if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0) {
362 LABEL;
363 printf("%scksum: %s: %s\n",
364 tab, p->fts_accpath, strerror(errno));
365 tab = "\t";
366 } else if (crc(fd, &val, &len)) {
367 close(fd);
368 LABEL;
369 printf("%scksum: %s: %s\n",
370 tab, p->fts_accpath, strerror(errno));
371 tab = "\t";
372 } else {
373 close(fd);
374 if (s->cksum != val) {
375 LABEL;
376 printf("%scksum (%lu, %lu)\n",
377 tab, s->cksum, (unsigned long)val);
378 }
379 tab = "\t";
380 }
381 }
382 #ifndef NO_MD5
383 if (s->flags & F_MD5) {
384 if (MD5File(p->fts_accpath, digestbuf) == NULL) {
385 LABEL;
386 printf("%smd5: %s: %s\n",
387 tab, p->fts_accpath, strerror(errno));
388 tab = "\t";
389 } else {
390 if (strcmp(s->md5digest, digestbuf)) {
391 LABEL;
392 printf("%smd5 (0x%s, 0x%s)\n",
393 tab, s->md5digest, digestbuf);
394 }
395 tab = "\t";
396 }
397 }
398 #endif /* ! NO_MD5 */
399 #ifndef NO_RMD160
400 if (s->flags & F_RMD160) {
401 if (RMD160File(p->fts_accpath, digestbuf) == NULL) {
402 LABEL;
403 printf("%srmd160: %s: %s\n",
404 tab, p->fts_accpath, strerror(errno));
405 tab = "\t";
406 } else {
407 if (strcmp(s->rmd160digest, digestbuf)) {
408 LABEL;
409 printf("%srmd160 (0x%s, 0x%s)\n",
410 tab, s->rmd160digest, digestbuf);
411 }
412 tab = "\t";
413 }
414 }
415 #endif /* ! NO_RMD160 */
416 #ifndef NO_SHA1
417 if (s->flags & F_SHA1) {
418 if (SHA1File(p->fts_accpath, digestbuf) == NULL) {
419 LABEL;
420 printf("%ssha1: %s: %s\n",
421 tab, p->fts_accpath, strerror(errno));
422 tab = "\t";
423 } else {
424 if (strcmp(s->sha1digest, digestbuf)) {
425 LABEL;
426 printf("%ssha1 (0x%s, 0x%s)\n",
427 tab, s->sha1digest, digestbuf);
428 }
429 tab = "\t";
430 }
431 }
432 #endif /* ! NO_SHA1 */
433 if (s->flags & F_SLINK &&
434 strcmp(cp = rlink(p->fts_accpath), s->slink)) {
435 LABEL;
436 printf("%slink ref (%s, %s", tab, cp, s->slink);
437 if (uflag) {
438 if ((unlink(p->fts_accpath) == -1) ||
439 (symlink(s->slink, p->fts_accpath) == -1) )
440 printf(", not modified: %s)\n",
441 strerror(errno));
442 else
443 printf(", modified)\n");
444 } else
445 printf(")\n");
446 }
447 return (label);
448 }
449
450 const char *
451 rlink(const char *name)
452 {
453 static char lbuf[MAXPATHLEN];
454 int len;
455
456 if ((len = readlink(name, lbuf, sizeof(lbuf) - 1)) == -1)
457 mtree_err("%s: %s", name, strerror(errno));
458 lbuf[len] = '\0';
459 return (lbuf);
460 }
461