compare.c revision 1.27 1 /* $NetBSD: compare.c,v 1.27 2001/10/01 02:30:40 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 #ifndef 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.27 2001/10/01 02:30:40 lukem Exp $");
42 #endif
43 #endif /* not lint */
44
45 #include <sys/param.h>
46 #include <sys/stat.h>
47 #include <sys/types.h>
48
49 #include <errno.h>
50 #include <fcntl.h>
51 #include <fts.h>
52 #include <md5.h>
53 #include <stdio.h>
54 #include <time.h>
55 #include <unistd.h>
56
57 #include "mtree.h"
58 #include "extern.h"
59
60 extern int iflag, lflag, mflag, tflag, uflag;
61
62 #define INDENTNAMELEN 8
63 #define MARK \
64 do { \
65 len = printf("%s: ", RP(p)); \
66 if (len > INDENTNAMELEN) { \
67 tab = "\t"; \
68 (void)printf("\n"); \
69 } else { \
70 tab = ""; \
71 (void)printf("%*s", INDENTNAMELEN - (int)len, ""); \
72 } \
73 } while (0)
74 #define LABEL if (!label++) MARK
75
76 #define CHANGEFLAGS(path, oflags) \
77 if (flags != (oflags)) { \
78 if (!label) { \
79 MARK; \
80 (void)printf("%sflags (\"%s\"", tab, \
81 flags_to_string(p->fts_statp->st_flags, "none")); \
82 } \
83 if (chflags(path, flags)) { \
84 label++; \
85 (void)printf(", not modified: %s)\n", \
86 strerror(errno)); \
87 } else \
88 (void)printf(", modified to \"%s\")\n", \
89 flags_to_string(flags, "none")); \
90 }
91
92 /* SETFLAGS:
93 * given pflags, additionally set those flags specified in sflags and
94 * selected by mask (the other flags are left unchanged). oflags is
95 * passed as reference to check if chflags is necessary.
96 */
97 #define SETFLAGS(path, sflags, pflags, oflags, mask) \
98 do { \
99 flags = ((sflags) & (mask)) | (pflags); \
100 CHANGEFLAGS(path, oflags); \
101 } while (0)
102
103 /* CLEARFLAGS:
104 * given pflags, reset the flags specified in sflags and selected by mask
105 * (the other flags are left unchanged). oflags is
106 * passed as reference to check if chflags is necessary.
107 */
108 #define CLEARFLAGS(path, sflags, pflags, oflags, mask) \
109 do { \
110 flags = (~((sflags) & (mask)) & CH_MASK) & (pflags); \
111 CHANGEFLAGS(path, oflags); \
112 } while (0)
113
114 int
115 compare(const char *name, NODE *s, FTSENT *p)
116 {
117 u_int32_t len, val, flags;
118 int fd, label;
119 const char *cp;
120 char *tab;
121 char md5buf[35];
122
123 tab = NULL;
124 label = 0;
125 switch(s->type) {
126 case F_BLOCK:
127 if (!S_ISBLK(p->fts_statp->st_mode))
128 goto typeerr;
129 break;
130 case F_CHAR:
131 if (!S_ISCHR(p->fts_statp->st_mode))
132 goto typeerr;
133 break;
134 case F_DIR:
135 if (!S_ISDIR(p->fts_statp->st_mode))
136 goto typeerr;
137 break;
138 case F_FIFO:
139 if (!S_ISFIFO(p->fts_statp->st_mode))
140 goto typeerr;
141 break;
142 case F_FILE:
143 if (!S_ISREG(p->fts_statp->st_mode))
144 goto typeerr;
145 break;
146 case F_LINK:
147 if (!S_ISLNK(p->fts_statp->st_mode))
148 goto typeerr;
149 break;
150 case F_SOCK:
151 if (!S_ISSOCK(p->fts_statp->st_mode)) {
152 typeerr: LABEL;
153 (void)printf("\ttype (%s, %s)\n",
154 nodetype(s->type), inotype(p->fts_statp->st_mode));
155 }
156 break;
157 }
158 if (iflag && !uflag) {
159 if (s->flags & F_FLAGS)
160 SETFLAGS(p->fts_accpath, s->st_flags,
161 p->fts_statp->st_flags, p->fts_statp->st_flags,
162 SP_FLGS);
163 return (label);
164 }
165 if (mflag && !uflag) {
166 if (s->flags & F_FLAGS)
167 CLEARFLAGS(p->fts_accpath, s->st_flags,
168 p->fts_statp->st_flags, p->fts_statp->st_flags,
169 SP_FLGS);
170 return (label);
171 }
172 /* Set the uid/gid first, then set the mode. */
173 if (s->flags & (F_UID | F_UNAME) && s->st_uid != p->fts_statp->st_uid) {
174 LABEL;
175 (void)printf("%suser (%lu, %lu",
176 tab, (u_long)s->st_uid, (u_long)p->fts_statp->st_uid);
177 if (uflag) {
178 if (chown(p->fts_accpath, s->st_uid, -1))
179 (void)printf(", not modified: %s)\n",
180 strerror(errno));
181 else
182 (void)printf(", modified)\n");
183 } else
184 (void)printf(")\n");
185 tab = "\t";
186 }
187 if (s->flags & (F_GID | F_GNAME) && s->st_gid != p->fts_statp->st_gid) {
188 LABEL;
189 (void)printf("%sgid (%lu, %lu",
190 tab, (u_long)s->st_gid, (u_long)p->fts_statp->st_gid);
191 if (uflag) {
192 if (chown(p->fts_accpath, -1, s->st_gid))
193 (void)printf(", not modified: %s)\n",
194 strerror(errno));
195 else
196 (void)printf(", modified)\n");
197 }
198 else
199 (void)printf(")\n");
200 tab = "\t";
201 }
202 if (s->flags & F_MODE &&
203 s->st_mode != (p->fts_statp->st_mode & MBITS)) {
204 if (lflag) {
205 mode_t tmode, mode;
206
207 tmode = s->st_mode;
208 mode = p->fts_statp->st_mode & MBITS;
209 /*
210 * if none of the suid/sgid/etc bits are set,
211 * then if the mode is a subset of the target,
212 * skip.
213 */
214 if (!((tmode & ~(S_IRWXU|S_IRWXG|S_IRWXO)) ||
215 (mode & ~(S_IRWXU|S_IRWXG|S_IRWXO))))
216 if ((mode | tmode) == tmode)
217 goto skip;
218 }
219
220 LABEL;
221 (void)printf("%spermissions (%#lo, %#lo",
222 tab, (u_long)s->st_mode,
223 (u_long)p->fts_statp->st_mode & MBITS);
224 if (uflag) {
225 if (chmod(p->fts_accpath, s->st_mode))
226 (void)printf(", not modified: %s)\n",
227 strerror(errno));
228 else
229 (void)printf(", modified)\n");
230 }
231 else
232 (void)printf(")\n");
233 tab = "\t";
234 skip:
235 }
236 if (s->flags & F_NLINK && s->type != F_DIR &&
237 s->st_nlink != p->fts_statp->st_nlink) {
238 LABEL;
239 (void)printf("%slink count (%lu, %lu)\n",
240 tab, (u_long)s->st_nlink, (u_long)p->fts_statp->st_nlink);
241 tab = "\t";
242 }
243 if (s->flags & F_SIZE && s->st_size != p->fts_statp->st_size) {
244 LABEL;
245 (void)printf("%ssize (%lld, %lld)\n",
246 tab, (long long)s->st_size,
247 (long long)p->fts_statp->st_size);
248 tab = "\t";
249 }
250 /*
251 * XXX
252 * Since utimes(2) only takes a timeval, there's no point in
253 * comparing the low bits of the timespec nanosecond field. This
254 * will only result in mismatches that we can never fix.
255 *
256 * Doesn't display microsecond differences.
257 */
258 if (s->flags & F_TIME) {
259 struct timeval tv[2];
260 struct stat *ps = p->fts_statp;
261 time_t smtime = s->st_mtimespec.tv_sec;
262
263 #ifdef BSD4_4
264 time_t pmtime = ps->st_mtimespec.tv_sec;
265
266 TIMESPEC_TO_TIMEVAL(&tv[1], &ps->st_mtimespec);
267 #else
268 time_t pmtime = (time_t)ps->st_mtime;
269
270 tv[1].tv_sec = ps->st_mtime;
271 tv[1].tv_usec = 0;
272 #endif
273 TIMESPEC_TO_TIMEVAL(&tv[0], &s->st_mtimespec);
274
275 if (tv[0].tv_sec != tv[1].tv_sec ||
276 tv[0].tv_usec != tv[1].tv_usec) {
277 LABEL;
278 (void)printf("%smodification time (%.24s, ",
279 tab, ctime(&smtime));
280 (void)printf("%.24s", ctime(&pmtime));
281 if (tflag) {
282 tv[1] = tv[0];
283 if (utimes(p->fts_accpath, tv))
284 (void)printf(", not modified: %s)\n",
285 strerror(errno));
286 else
287 (void)printf(", modified)\n");
288 } else
289 (void)printf(")\n");
290 tab = "\t";
291 }
292 }
293 /*
294 * XXX
295 * since chflags(2) will reset file times, the utimes() above
296 * may have been useless! oh well, we'd rather have correct
297 * flags, rather than times?
298 */
299 if ((s->flags & F_FLAGS) && ((s->st_flags != p->fts_statp->st_flags)
300 || mflag || iflag)) {
301 if (s->st_flags != p->fts_statp->st_flags) {
302 LABEL;
303 (void)printf("%sflags (\"%s\" is not ", tab,
304 flags_to_string(s->st_flags, "none"));
305 (void)printf("\"%s\"",
306 flags_to_string(p->fts_statp->st_flags, "none"));
307 }
308 if (uflag) {
309 if (iflag)
310 SETFLAGS(p->fts_accpath, s->st_flags,
311 0, p->fts_statp->st_flags, CH_MASK);
312 else if (mflag)
313 CLEARFLAGS(p->fts_accpath, s->st_flags,
314 0, p->fts_statp->st_flags, SP_FLGS);
315 else
316 SETFLAGS(p->fts_accpath, s->st_flags,
317 0, p->fts_statp->st_flags,
318 (~SP_FLGS & CH_MASK));
319 } else
320 (void)printf(")\n");
321 tab = "\t";
322 }
323 if (s->flags & F_CKSUM) {
324 if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0) {
325 LABEL;
326 (void)printf("%scksum: %s: %s\n",
327 tab, p->fts_accpath, strerror(errno));
328 tab = "\t";
329 } else if (crc(fd, &val, &len)) {
330 (void)close(fd);
331 LABEL;
332 (void)printf("%scksum: %s: %s\n",
333 tab, p->fts_accpath, strerror(errno));
334 tab = "\t";
335 } else {
336 (void)close(fd);
337 if (s->cksum != val) {
338 LABEL;
339 (void)printf("%scksum (%lu, %lu)\n",
340 tab, s->cksum, (unsigned long)val);
341 }
342 tab = "\t";
343 }
344 }
345 if (s->flags & F_MD5) {
346 if (MD5File(p->fts_accpath, md5buf) == NULL) {
347 LABEL;
348 (void)printf("%smd5: %s: %s\n",
349 tab, p->fts_accpath, strerror(errno));
350 tab = "\t";
351 } else {
352 if (strcmp(s->md5sum, md5buf)) {
353 LABEL;
354 (void)printf("%smd5 (0x%s, 0x%s)\n",
355 tab, s->md5sum, md5buf);
356 }
357 tab = "\t";
358 }
359 }
360
361 if (s->flags & F_SLINK && strcmp(cp = rlink(name), s->slink)) {
362 LABEL;
363 (void)printf("%slink ref (%s, %s)\n", tab, cp, s->slink);
364 }
365 return (label);
366 }
367
368 const char *
369 inotype(u_int type)
370 {
371
372 return (nodetype(type & S_IFMT));
373 }
374
375 const char *
376 nodetype(u_int type)
377 {
378
379 switch(type) {
380 case F_BLOCK:
381 case S_IFBLK:
382 return ("block");
383 case F_CHAR:
384 case S_IFCHR:
385 return ("char");
386 case F_DIR:
387 case S_IFDIR:
388 return ("dir");
389 case F_FIFO:
390 case S_IFIFO:
391 return ("fifo");
392 case F_FILE:
393 case S_IFREG:
394 return ("file");
395 case F_LINK:
396 case S_IFLNK:
397 return ("link");
398 case F_SOCK:
399 case S_IFSOCK:
400 return ("socket");
401 default:
402 return ("unknown");
403 }
404 /* NOTREACHED */
405 }
406
407 const char *
408 rlink(const char *name)
409 {
410 static char lbuf[MAXPATHLEN];
411 int len;
412
413 if ((len = readlink(name, lbuf, sizeof(lbuf))) == -1)
414 mtree_err("%s: %s", name, strerror(errno));
415 lbuf[len] = '\0';
416 return (lbuf);
417 }
418