mtree(8): Replace our mtree(8) with NetBSD's version.
[dragonfly.git] / usr.sbin / mtree / compare.c
blobef97a36a0f4bc9f6ff46024c46e1e05a23c14efa
1 /* @(#)compare.c 8.1 (Berkeley) 6/6/93 */
2 /* $NetBSD: compare.c,v 1.58 2013/11/21 18:39:50 christos Exp $ */
4 /*-
5 * Copyright (c) 1989, 1993
6 * The Regents of the University of California. All rights reserved.
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. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
33 #if HAVE_NBTOOL_CONFIG_H
34 #include "nbtool_config.h"
35 #endif
37 #include <sys/param.h>
38 #include <sys/stat.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <stdio.h>
43 #include <stdint.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <time.h>
47 #include <unistd.h>
49 #ifndef NO_MD5
50 #include <md5.h>
51 #endif
52 #ifndef NO_RMD160
53 #include <ripemd.h>
54 #include <rmd160.h>
55 #endif
56 #ifndef NO_SHA1
57 #include <sha.h>
58 #include <sha1hl.h>
59 #endif
60 #ifndef NO_SHA2
61 #include <sha256.h>
62 #include <sha512.h>
63 #endif
65 #include "extern.h"
67 #define INDENTNAMELEN 8
68 #define MARK \
69 do { \
70 if (flavor == F_FREEBSD9) { \
71 len = printf("%s changed\n", RP(p)); \
72 tab = "\t"; \
73 } else { \
74 len = printf("%s: ", RP(p)); \
75 if (len > INDENTNAMELEN) { \
76 tab = "\t"; \
77 printf("\n"); \
78 } else { \
79 tab = ""; \
80 printf("%*s", INDENTNAMELEN - (int)len, ""); \
81 } \
82 } \
83 } while (0)
84 #define LABEL if (!label++) MARK
86 #if HAVE_STRUCT_STAT_ST_FLAGS
89 #define CHANGEFLAGS \
90 if (flags != p->fts_statp->st_flags) { \
91 char *sf; \
92 if (!label) { \
93 MARK; \
94 sf = flags_to_string(p->fts_statp->st_flags, "none"); \
95 printf("%sflags (\"%s\"", tab, sf); \
96 free(sf); \
97 } \
98 if (lchflags(p->fts_accpath, flags)) { \
99 label++; \
100 printf(", not modified: %s)\n", \
101 strerror(errno)); \
102 } else { \
103 sf = flags_to_string(flags, "none"); \
104 printf(", modified to \"%s\")\n", sf); \
105 free(sf); \
109 /* SETFLAGS:
110 * given pflags, additionally set those flags specified in s->st_flags and
111 * selected by mask (the other flags are left unchanged).
113 #define SETFLAGS(pflags, mask) \
114 do { \
115 flags = (s->st_flags & (mask)) | (pflags); \
116 CHANGEFLAGS; \
117 } while (0)
119 /* CLEARFLAGS:
120 * given pflags, reset the flags specified in s->st_flags and selected by mask
121 * (the other flags are left unchanged).
123 #define CLEARFLAGS(pflags, mask) \
124 do { \
125 flags = (~(s->st_flags & (mask)) & CH_MASK) & (pflags); \
126 CHANGEFLAGS; \
127 } while (0)
128 #endif /* HAVE_STRUCT_STAT_ST_FLAGS */
131 compare(NODE *s, FTSENT *p)
133 u_int32_t len, val, flags;
134 int fd, label;
135 const char *cp, *tab;
136 #if !defined(NO_MD5) || !defined(NO_RMD160) || !defined(NO_SHA1) || !defined(NO_SHA2)
137 char *digestbuf;
138 #endif
140 tab = NULL;
141 label = 0;
142 switch(s->type) {
143 case F_BLOCK:
144 if (!S_ISBLK(p->fts_statp->st_mode))
145 goto typeerr;
146 break;
147 case F_CHAR:
148 if (!S_ISCHR(p->fts_statp->st_mode))
149 goto typeerr;
150 break;
151 case F_DIR:
152 if (!S_ISDIR(p->fts_statp->st_mode))
153 goto typeerr;
154 break;
155 case F_FIFO:
156 if (!S_ISFIFO(p->fts_statp->st_mode))
157 goto typeerr;
158 break;
159 case F_FILE:
160 if (!S_ISREG(p->fts_statp->st_mode))
161 goto typeerr;
162 break;
163 case F_LINK:
164 if (!S_ISLNK(p->fts_statp->st_mode))
165 goto typeerr;
166 break;
167 #ifdef S_ISSOCK
168 case F_SOCK:
169 if (!S_ISSOCK(p->fts_statp->st_mode))
170 goto typeerr;
171 break;
172 #endif
173 typeerr: LABEL;
174 printf(flavor == F_FREEBSD9 ?
175 "\ttype expected %s found %s\n" : "\ttype (%s, %s)\n",
176 nodetype(s->type), inotype(p->fts_statp->st_mode));
177 return (label);
179 if (mtree_Wflag)
180 goto afterpermwhack;
181 #if HAVE_STRUCT_STAT_ST_FLAGS
182 if (iflag && !uflag) {
183 if (s->flags & F_FLAGS)
184 SETFLAGS(p->fts_statp->st_flags, SP_FLGS);
185 return (label);
187 if (mflag && !uflag) {
188 if (s->flags & F_FLAGS)
189 CLEARFLAGS(p->fts_statp->st_flags, SP_FLGS);
190 return (label);
192 #endif
193 if (s->flags & F_DEV &&
194 (s->type == F_BLOCK || s->type == F_CHAR) &&
195 s->st_rdev != p->fts_statp->st_rdev) {
196 LABEL;
197 printf(flavor == F_FREEBSD9 ?
198 "%sdevice expected %#jx found %#jx" :
199 "%sdevice (%#jx, %#jx",
200 tab, (uintmax_t)s->st_rdev,
201 (uintmax_t)p->fts_statp->st_rdev);
202 if (uflag) {
203 if ((unlink(p->fts_accpath) == -1) ||
204 (mknod(p->fts_accpath,
205 s->st_mode | nodetoino(s->type),
206 s->st_rdev) == -1) ||
207 (lchown(p->fts_accpath, p->fts_statp->st_uid,
208 p->fts_statp->st_gid) == -1) )
209 printf(", not modified: %s%s\n",
210 strerror(errno),
211 flavor == F_FREEBSD9 ? "" : ")");
212 else
213 printf(", modified%s\n",
214 flavor == F_FREEBSD9 ? "" : ")");
215 } else
216 printf(")\n");
217 tab = "\t";
219 /* Set the uid/gid first, then set the mode. */
220 if (s->flags & (F_UID | F_UNAME) && s->st_uid != p->fts_statp->st_uid) {
221 LABEL;
222 printf(flavor == F_FREEBSD9 ?
223 "%suser expected %lu found %lu" : "%suser (%lu, %lu",
224 tab, (u_long)s->st_uid, (u_long)p->fts_statp->st_uid);
225 if (uflag) {
226 if (lchown(p->fts_accpath, s->st_uid, -1))
227 printf(", not modified: %s%s\n",
228 strerror(errno),
229 flavor == F_FREEBSD9 ? "" : ")");
230 else
231 printf(", modified%s\n",
232 flavor == F_FREEBSD9 ? "" : ")");
233 } else
234 printf(")\n");
235 tab = "\t";
237 if (s->flags & (F_GID | F_GNAME) && s->st_gid != p->fts_statp->st_gid) {
238 LABEL;
239 printf(flavor == F_FREEBSD9 ?
240 "%sgid expected %lu found %lu" : "%sgid (%lu, %lu",
241 tab, (u_long)s->st_gid, (u_long)p->fts_statp->st_gid);
242 if (uflag) {
243 if (lchown(p->fts_accpath, -1, s->st_gid))
244 printf(", not modified: %s%s\n",
245 strerror(errno),
246 flavor == F_FREEBSD9 ? "" : ")");
247 else
248 printf(", modified%s\n",
249 flavor == F_FREEBSD9 ? "" : ")");
251 else
252 printf(")\n");
253 tab = "\t";
255 if (s->flags & F_MODE &&
256 s->st_mode != (p->fts_statp->st_mode & MBITS)) {
257 if (lflag) {
258 mode_t tmode, mode;
260 tmode = s->st_mode;
261 mode = p->fts_statp->st_mode & MBITS;
263 * if none of the suid/sgid/etc bits are set,
264 * then if the mode is a subset of the target,
265 * skip.
267 if (!((tmode & ~(S_IRWXU|S_IRWXG|S_IRWXO)) ||
268 (mode & ~(S_IRWXU|S_IRWXG|S_IRWXO))))
269 if ((mode | tmode) == tmode)
270 goto skip;
273 LABEL;
274 printf(flavor == F_FREEBSD9 ?
275 "%spermissions expcted %#lo found %#lo" :
276 "%spermissions (%#lo, %#lo",
277 tab, (u_long)s->st_mode,
278 (u_long)p->fts_statp->st_mode & MBITS);
279 if (uflag) {
280 if (lchmod(p->fts_accpath, s->st_mode))
281 printf(", not modified: %s%s\n",
282 strerror(errno),
283 flavor == F_FREEBSD9 ? "" : ")");
284 else
285 printf(", modified%s\n",
286 flavor == F_FREEBSD9 ? "" : ")");
288 else
289 printf(")\n");
290 tab = "\t";
291 skip: ;
293 if (s->flags & F_NLINK && s->type != F_DIR &&
294 s->st_nlink != p->fts_statp->st_nlink) {
295 LABEL;
296 printf(flavor == F_FREEBSD9 ?
297 "%slink count expected %lu found %lu\n" :
298 "%slink count (%lu, %lu)\n",
299 tab, (u_long)s->st_nlink, (u_long)p->fts_statp->st_nlink);
300 tab = "\t";
302 if (s->flags & F_SIZE && s->st_size != p->fts_statp->st_size) {
303 LABEL;
304 printf(flavor == F_FREEBSD9 ?
305 "%ssize expected %ju found %ju\n" : "%ssize (%ju, %ju)\n",
306 tab, (uintmax_t)s->st_size,
307 (uintmax_t)p->fts_statp->st_size);
308 tab = "\t";
311 * XXX
312 * Since utimes(2) only takes a timeval, there's no point in
313 * comparing the low bits of the timespec nanosecond field. This
314 * will only result in mismatches that we can never fix.
316 * Doesn't display microsecond differences.
318 if (s->flags & F_TIME) {
319 struct timeval tv[2];
320 struct stat *ps = p->fts_statp;
321 time_t smtime = s->st_mtimespec.tv_sec;
323 #if defined(BSD4_4) && !defined(HAVE_NBTOOL_CONFIG_H)
324 time_t pmtime = ps->st_mtimespec.tv_sec;
326 TIMESPEC_TO_TIMEVAL(&tv[0], &s->st_mtimespec);
327 TIMESPEC_TO_TIMEVAL(&tv[1], &ps->st_mtimespec);
328 #else
329 time_t pmtime = (time_t)ps->st_mtime;
331 tv[0].tv_sec = smtime;
332 tv[0].tv_usec = 0;
333 tv[1].tv_sec = pmtime;
334 tv[1].tv_usec = 0;
335 #endif
337 if (tv[0].tv_sec != tv[1].tv_sec ||
338 tv[0].tv_usec != tv[1].tv_usec) {
339 LABEL;
340 printf(flavor == F_FREEBSD9 ?
341 "%smodification time expected %.24s found " :
342 "%smodification time (%.24s, ",
343 tab, ctime(&smtime));
344 printf("%.24s", ctime(&pmtime));
345 if (tflag) {
346 tv[1] = tv[0];
347 if (utimes(p->fts_accpath, tv))
348 printf(", not modified: %s%s\n",
349 strerror(errno),
350 flavor == F_FREEBSD9 ? "" : ")");
351 else
352 printf(", modified%s\n",
353 flavor == F_FREEBSD9 ? "" : ")");
354 } else
355 printf("%s\n", flavor == F_FREEBSD9 ? "" : ")");
356 tab = "\t";
359 #if HAVE_STRUCT_STAT_ST_FLAGS
361 * XXX
362 * since lchflags(2) will reset file times, the utimes() above
363 * may have been useless! oh well, we'd rather have correct
364 * flags, rather than times?
366 if ((s->flags & F_FLAGS) && ((s->st_flags != p->fts_statp->st_flags)
367 || mflag || iflag)) {
368 if (s->st_flags != p->fts_statp->st_flags) {
369 char *f_s;
370 LABEL;
371 f_s = flags_to_string(s->st_flags, "none");
372 printf(flavor == F_FREEBSD9 ?
373 "%sflags expected \"%s\" found " :
374 "%sflags (\"%s\" is not ", tab, f_s);
375 free(f_s);
376 f_s = flags_to_string(p->fts_statp->st_flags, "none");
377 printf("\"%s\"", f_s);
378 free(f_s);
380 if (uflag) {
381 if (iflag)
382 SETFLAGS(0, CH_MASK);
383 else if (mflag)
384 CLEARFLAGS(0, SP_FLGS);
385 else
386 SETFLAGS(0, (~SP_FLGS & CH_MASK));
387 } else
388 printf("%s\n", flavor == F_FREEBSD9 ? "" : ")");
389 tab = "\t";
391 #endif /* HAVE_STRUCT_STAT_ST_FLAGS */
394 * from this point, no more permission checking or whacking
395 * occurs, only checking of stuff like checksums and symlinks.
397 afterpermwhack:
398 if (s->flags & F_CKSUM) {
399 if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0) {
400 LABEL;
401 printf("%scksum: %s: %s\n",
402 tab, p->fts_accpath, strerror(errno));
403 tab = "\t";
404 } else if (crc(fd, &val, &len)) {
405 close(fd);
406 LABEL;
407 printf("%scksum: %s: %s\n",
408 tab, p->fts_accpath, strerror(errno));
409 tab = "\t";
410 } else {
411 close(fd);
412 if (s->cksum != val) {
413 LABEL;
414 printf(flavor == F_FREEBSD9 ?
415 "%scksum expected %lu found %lu\n" :
416 "%scksum (%lu, %lu)\n",
417 tab, s->cksum, (unsigned long)val);
419 tab = "\t";
422 #ifndef NO_MD5
423 if (s->flags & F_MD5) {
424 if ((digestbuf = MD5File(p->fts_accpath, NULL)) == NULL) {
425 LABEL;
426 printf("%s%s: %s: %s\n",
427 tab, MD5KEY, p->fts_accpath, strerror(errno));
428 tab = "\t";
429 } else {
430 if (strcmp(s->md5digest, digestbuf)) {
431 LABEL;
432 printf(flavor == F_FREEBSD9 ?
433 "%s%s expected %s found %s\n" :
434 "%s%s (0x%s, 0x%s)\n",
435 tab, MD5KEY, s->md5digest, digestbuf);
437 tab = "\t";
438 free(digestbuf);
441 #endif /* ! NO_MD5 */
442 #ifndef NO_RMD160
443 if (s->flags & F_RMD160) {
444 if ((digestbuf = RIPEMD160_File(p->fts_accpath, NULL)) == NULL) {
445 LABEL;
446 printf("%s%s: %s: %s\n",
447 tab, RMD160KEY, p->fts_accpath, strerror(errno));
448 tab = "\t";
449 } else {
450 if (strcmp(s->rmd160digest, digestbuf)) {
451 LABEL;
452 printf(flavor == F_FREEBSD9 ?
453 "%s%s expected %s found %s\n" :
454 "%s%s (0x%s, 0x%s)\n",
455 tab, RMD160KEY, s->rmd160digest, digestbuf);
457 tab = "\t";
458 free(digestbuf);
461 #endif /* ! NO_RMD160 */
462 #ifndef NO_SHA1
463 if (s->flags & F_SHA1) {
464 if ((digestbuf = SHA1_File(p->fts_accpath, NULL)) == NULL) {
465 LABEL;
466 printf("%s%s: %s: %s\n",
467 tab, SHA1KEY, p->fts_accpath, strerror(errno));
468 tab = "\t";
469 } else {
470 if (strcmp(s->sha1digest, digestbuf)) {
471 LABEL;
472 printf(flavor == F_FREEBSD9 ?
473 "%s%s expected %s found %s\n" :
474 "%s%s (0x%s, 0x%s)\n",
475 tab, SHA1KEY, s->sha1digest, digestbuf);
477 tab = "\t";
478 free(digestbuf);
481 #endif /* ! NO_SHA1 */
482 #ifndef NO_SHA2
483 if (s->flags & F_SHA256) {
484 if ((digestbuf = SHA256_File(p->fts_accpath, NULL)) == NULL) {
485 LABEL;
486 printf("%s%s: %s: %s\n",
487 tab, SHA256KEY, p->fts_accpath, strerror(errno));
488 tab = "\t";
489 } else {
490 if (strcmp(s->sha256digest, digestbuf)) {
491 LABEL;
492 printf(flavor == F_FREEBSD9 ?
493 "%s%s expected %s found %s\n" :
494 "%s%s (0x%s, 0x%s)\n",
495 tab, SHA256KEY, s->sha256digest, digestbuf);
497 tab = "\t";
498 free(digestbuf);
501 #ifdef SHA384_BLOCK_LENGTH
502 #if !defined(__DragonFly__)
503 if (s->flags & F_SHA384) {
504 if ((digestbuf = SHA384_File(p->fts_accpath, NULL)) == NULL) {
505 LABEL;
506 printf("%s%s: %s: %s\n",
507 tab, SHA384KEY, p->fts_accpath, strerror(errno));
508 tab = "\t";
509 } else {
510 if (strcmp(s->sha384digest, digestbuf)) {
511 LABEL;
512 printf(flavor == F_FREEBSD9 ?
513 "%s%s expected %s found %s\n" :
514 "%s%s (0x%s, 0x%s)\n",
515 tab, SHA384KEY, s->sha384digest, digestbuf);
517 tab = "\t";
518 free(digestbuf);
521 #endif
522 #endif
523 if (s->flags & F_SHA512) {
524 if ((digestbuf = SHA512_File(p->fts_accpath, NULL)) == NULL) {
525 LABEL;
526 printf("%s%s: %s: %s\n",
527 tab, SHA512KEY, p->fts_accpath, strerror(errno));
528 tab = "\t";
529 } else {
530 if (strcmp(s->sha512digest, digestbuf)) {
531 LABEL;
532 printf(flavor == F_FREEBSD9 ?
533 "%s%s expected %s found %s\n" :
534 "%s%s (0x%s, 0x%s)\n",
535 tab, SHA512KEY, s->sha512digest, digestbuf);
537 tab = "\t";
538 free(digestbuf);
541 #endif /* ! NO_SHA2 */
542 if (s->flags & F_SLINK &&
543 strcmp(cp = rlink(p->fts_accpath), s->slink)) {
544 LABEL;
545 printf(flavor == F_FREEBSD9 ?
546 "%slink ref expected %s found %s" :
547 "%slink ref (%s, %s", tab, cp, s->slink);
548 if (uflag) {
549 if ((unlink(p->fts_accpath) == -1) ||
550 (symlink(s->slink, p->fts_accpath) == -1) )
551 printf(", not modified: %s%s\n",
552 strerror(errno),
553 flavor == F_FREEBSD9 ? "" : ")");
554 else
555 printf(", modified%s\n",
556 flavor == F_FREEBSD9 ? "" : ")");
557 } else
558 printf("%s\n", flavor == F_FREEBSD9 ? "" : ")");
560 return (label);
563 const char *
564 rlink(const char *name)
566 static char lbuf[MAXPATHLEN];
567 int len;
569 if ((len = readlink(name, lbuf, sizeof(lbuf) - 1)) == -1)
570 mtree_err("%s: %s", name, strerror(errno));
571 lbuf[len] = '\0';
572 return (lbuf);