Upgrade libarchive, bsdcat(1), bsdcpio(1) and bsdtar(1). 1/2
[dragonfly.git] / usr.sbin / mtree / compare.c
1 /*      @(#)compare.c   8.1 (Berkeley) 6/6/93   */
2 /*      $NetBSD: compare.c,v 1.58 2013/11/21 18:39:50 christos Exp $    */
3
4 /*-
5  * Copyright (c) 1989, 1993
6  *      The Regents of the University of California.  All rights reserved.
7  *
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.
19  *
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.
31  */
32
33 #include <sys/param.h>
34 #include <sys/stat.h>
35 #include <sys/time.h>
36
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <stdio.h>
40 #include <stdint.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <time.h>
44 #include <unistd.h>
45
46 #ifndef NO_MD5
47 #include <openssl/md5.h>
48 #endif
49 #ifndef NO_RMD160
50 #include <openssl/ripemd.h>
51 #endif
52 #ifndef NO_SHA
53 #include <openssl/sha.h>
54 #endif
55
56 #include "extern.h"
57
58 #define INDENTNAMELEN   8
59 #define MARK                                                            \
60 do {                                                                    \
61         if (flavor == F_FREEBSD9) {                                     \
62                 len = printf("%s changed\n", RP(p));                    \
63                 tab = "\t";                                             \
64         } else {                                                        \
65                 len = printf("%s: ", RP(p));                            \
66                 if (len > INDENTNAMELEN) {                              \
67                         tab = "\t";                                     \
68                         printf("\n");                                   \
69                 } else {                                                \
70                         tab = "";                                       \
71                         printf("%*s", INDENTNAMELEN - (int)len, "");    \
72                 }                                                       \
73         }                                                               \
74 } while (0)
75 #define LABEL if (!label++) MARK
76
77 #if HAVE_STRUCT_STAT_ST_FLAGS
78
79
80 #define CHANGEFLAGS                                                     \
81         if (flags != p->fts_statp->st_flags) {                          \
82                 char *sf;                                               \
83                 if (!label) {                                           \
84                         MARK;                                           \
85                         sf = flags_to_string(p->fts_statp->st_flags, "none"); \
86                         printf("%sflags (\"%s\"", tab, sf);             \
87                         free(sf);                                       \
88                 }                                                       \
89                 if (lchflags(p->fts_accpath, flags)) {                  \
90                         label++;                                        \
91                         printf(", not modified: %s)\n",                 \
92                             strerror(errno));                           \
93                 } else {                                                \
94                         sf = flags_to_string(flags, "none");            \
95                         printf(", modified to \"%s\")\n", sf);          \
96                         free(sf);                                       \
97                 }                                                       \
98         }
99
100 /* SETFLAGS:
101  * given pflags, additionally set those flags specified in s->st_flags and
102  * selected by mask (the other flags are left unchanged).
103  */
104 #define SETFLAGS(pflags, mask)                                          \
105 do {                                                                    \
106         flags = (s->st_flags & (mask)) | (pflags);                      \
107         CHANGEFLAGS;                                                    \
108 } while (0)
109
110 /* CLEARFLAGS:
111  * given pflags, reset the flags specified in s->st_flags and selected by mask
112  * (the other flags are left unchanged).
113  */
114 #define CLEARFLAGS(pflags, mask)                                        \
115 do {                                                                    \
116         flags = (~(s->st_flags & (mask)) & CH_MASK) & (pflags);         \
117         CHANGEFLAGS;                                                    \
118 } while (0)
119 #endif  /* HAVE_STRUCT_STAT_ST_FLAGS */
120
121 int
122 compare(NODE *s, FTSENT *p)
123 {
124         u_int32_t len, val;
125 #if HAVE_STRUCT_STAT_ST_FLAGS
126         u_int32_t flags;
127 #endif
128         int fd, label;
129         const char *cp, *tab;
130 #if !defined(NO_MD5) || !defined(NO_RMD160) || !defined(NO_SHA)
131         char *digestbuf;
132 #endif
133
134         tab = NULL;
135         label = 0;
136         switch(s->type) {
137         case F_BLOCK:
138                 if (!S_ISBLK(p->fts_statp->st_mode))
139                         goto typeerr;
140                 break;
141         case F_CHAR:
142                 if (!S_ISCHR(p->fts_statp->st_mode))
143                         goto typeerr;
144                 break;
145         case F_DIR:
146                 if (!S_ISDIR(p->fts_statp->st_mode))
147                         goto typeerr;
148                 break;
149         case F_FIFO:
150                 if (!S_ISFIFO(p->fts_statp->st_mode))
151                         goto typeerr;
152                 break;
153         case F_FILE:
154                 if (!S_ISREG(p->fts_statp->st_mode))
155                         goto typeerr;
156                 break;
157         case F_LINK:
158                 if (!S_ISLNK(p->fts_statp->st_mode))
159                         goto typeerr;
160                 break;
161 #ifdef S_ISSOCK
162         case F_SOCK:
163                 if (!S_ISSOCK(p->fts_statp->st_mode))
164                         goto typeerr;
165                 break;
166 #endif
167 typeerr:                LABEL;
168                 printf(flavor == F_FREEBSD9 ?
169                     "\ttype expected %s found %s\n" : "\ttype (%s, %s)\n",
170                     nodetype(s->type), inotype(p->fts_statp->st_mode));
171                 return (label);
172         }
173         if (mtree_Wflag)
174                 goto afterpermwhack;
175 #if HAVE_STRUCT_STAT_ST_FLAGS
176         if (iflag && !uflag) {
177                 if (s->flags & F_FLAGS)
178                     SETFLAGS(p->fts_statp->st_flags, SP_FLGS);
179                 return (label);
180         }
181         if (mflag && !uflag) {
182                 if (s->flags & F_FLAGS)
183                     CLEARFLAGS(p->fts_statp->st_flags, SP_FLGS);
184                 return (label);
185         }
186 #endif
187         if (s->flags & F_DEV &&
188             (s->type == F_BLOCK || s->type == F_CHAR) &&
189             s->st_rdev != p->fts_statp->st_rdev) {
190                 LABEL;
191                 printf(flavor == F_FREEBSD9 ?
192                     "%sdevice expected %#jx found %#jx" :
193                     "%sdevice (%#jx, %#jx",
194                     tab, (uintmax_t)s->st_rdev,
195                     (uintmax_t)p->fts_statp->st_rdev);
196                 if (uflag) {
197                         if ((unlink(p->fts_accpath) == -1) ||
198                             (mknod(p->fts_accpath,
199                               s->st_mode | nodetoino(s->type),
200                               s->st_rdev) == -1) ||
201                             (lchown(p->fts_accpath, p->fts_statp->st_uid,
202                               p->fts_statp->st_gid) == -1) )
203                                 printf(", not modified: %s%s\n",
204                                     strerror(errno),
205                                     flavor == F_FREEBSD9 ? "" : ")");
206                          else
207                                 printf(", modified%s\n",
208                                     flavor == F_FREEBSD9 ? "" : ")");
209                 } else
210                         printf(")\n");
211                 tab = "\t";
212         }
213         /* Set the uid/gid first, then set the mode. */
214         if (s->flags & (F_UID | F_UNAME) && s->st_uid != p->fts_statp->st_uid) {
215                 LABEL;
216                 printf(flavor == F_FREEBSD9 ?
217                     "%suser expected %lu found %lu" : "%suser (%lu, %lu",
218                     tab, (u_long)s->st_uid, (u_long)p->fts_statp->st_uid);
219                 if (uflag) {
220                         if (lchown(p->fts_accpath, s->st_uid, -1))
221                                 printf(", not modified: %s%s\n",
222                                     strerror(errno),
223                                     flavor == F_FREEBSD9 ? "" : ")");
224                         else
225                                 printf(", modified%s\n",
226                                     flavor == F_FREEBSD9 ? "" : ")");
227                 } else
228                         printf(")\n");
229                 tab = "\t";
230         }
231         if (s->flags & (F_GID | F_GNAME) && s->st_gid != p->fts_statp->st_gid) {
232                 LABEL;
233                 printf(flavor == F_FREEBSD9 ?
234                     "%sgid expected %lu found %lu" : "%sgid (%lu, %lu",
235                     tab, (u_long)s->st_gid, (u_long)p->fts_statp->st_gid);
236                 if (uflag) {
237                         if (lchown(p->fts_accpath, -1, s->st_gid))
238                                 printf(", not modified: %s%s\n",
239                                     strerror(errno),
240                                     flavor == F_FREEBSD9 ? "" : ")");
241                         else
242                                 printf(", modified%s\n",
243                                     flavor == F_FREEBSD9 ? "" : ")");
244                 }
245                 else
246                         printf(")\n");
247                 tab = "\t";
248         }
249         if (s->flags & F_MODE &&
250             s->st_mode != (p->fts_statp->st_mode & MBITS)) {
251                 if (lflag) {
252                         mode_t tmode, mode;
253
254                         tmode = s->st_mode;
255                         mode = p->fts_statp->st_mode & MBITS;
256                         /*
257                          * if none of the suid/sgid/etc bits are set,
258                          * then if the mode is a subset of the target,
259                          * skip.
260                          */
261                         if (!((tmode & ~(S_IRWXU|S_IRWXG|S_IRWXO)) ||
262                             (mode & ~(S_IRWXU|S_IRWXG|S_IRWXO))))
263                                 if ((mode | tmode) == tmode)
264                                         goto skip;
265                 }
266
267                 LABEL;
268                 printf(flavor == F_FREEBSD9 ?
269                     "%spermissions expcted %#lo found %#lo" :
270                     "%spermissions (%#lo, %#lo",
271                     tab, (u_long)s->st_mode,
272                     (u_long)p->fts_statp->st_mode & MBITS);
273                 if (uflag) {
274                         if (lchmod(p->fts_accpath, s->st_mode))
275                                 printf(", not modified: %s%s\n",
276                                     strerror(errno),
277                                     flavor == F_FREEBSD9 ? "" : ")");
278                         else
279                                 printf(", modified%s\n",
280                                     flavor == F_FREEBSD9 ? "" : ")");
281                 }
282                 else
283                         printf(")\n");
284                 tab = "\t";
285         skip:   ;
286         }
287         if (s->flags & F_NLINK && s->type != F_DIR &&
288             s->st_nlink != p->fts_statp->st_nlink) {
289                 LABEL;
290                 printf(flavor == F_FREEBSD9 ?
291                     "%slink count expected %lu found %lu\n" :
292                     "%slink count (%lu, %lu)\n",
293                     tab, (u_long)s->st_nlink, (u_long)p->fts_statp->st_nlink);
294                 tab = "\t";
295         }
296         if (s->flags & F_SIZE && s->st_size != p->fts_statp->st_size) {
297                 LABEL;
298                 printf(flavor == F_FREEBSD9 ?
299                     "%ssize expected %ju found %ju\n" : "%ssize (%ju, %ju)\n",
300                     tab, (uintmax_t)s->st_size,
301                     (uintmax_t)p->fts_statp->st_size);
302                 tab = "\t";
303         }
304         /*
305          * XXX
306          * Since utimes(2) only takes a timeval, there's no point in
307          * comparing the low bits of the timespec nanosecond field.  This
308          * will only result in mismatches that we can never fix.
309          *
310          * Doesn't display microsecond differences.
311          */
312         if (s->flags & F_TIME) {
313                 struct timeval tv[2];
314                 struct stat *ps = p->fts_statp;
315                 time_t smtime = s->st_mtimespec.tv_sec;
316
317 #if defined(BSD4_4)
318                 time_t pmtime = ps->st_mtimespec.tv_sec;
319
320                 TIMESPEC_TO_TIMEVAL(&tv[0], &s->st_mtimespec);
321                 TIMESPEC_TO_TIMEVAL(&tv[1], &ps->st_mtimespec);
322 #else
323                 time_t pmtime = (time_t)ps->st_mtime;
324
325                 tv[0].tv_sec = smtime;
326                 tv[0].tv_usec = 0;
327                 tv[1].tv_sec = pmtime;
328                 tv[1].tv_usec = 0;
329 #endif
330
331                 if (tv[0].tv_sec != tv[1].tv_sec ||
332                     tv[0].tv_usec != tv[1].tv_usec) {
333                         LABEL;
334                         printf(flavor == F_FREEBSD9 ?
335                             "%smodification time expected %.24s found " :
336                             "%smodification time (%.24s, ",
337                             tab, ctime(&smtime));
338                         printf("%.24s", ctime(&pmtime));
339                         if (tflag) {
340                                 tv[1] = tv[0];
341                                 if (utimes(p->fts_accpath, tv))
342                                         printf(", not modified: %s%s\n",
343                                             strerror(errno),
344                                             flavor == F_FREEBSD9 ? "" : ")");
345                                 else
346                                         printf(", modified%s\n",
347                                             flavor == F_FREEBSD9 ? "" : ")");
348                         } else
349                                 printf("%s\n", flavor == F_FREEBSD9 ? "" : ")");
350                         tab = "\t";
351                 }
352         }
353 #if HAVE_STRUCT_STAT_ST_FLAGS
354         /*
355          * XXX
356          * since lchflags(2) will reset file times, the utimes() above
357          * may have been useless!  oh well, we'd rather have correct
358          * flags, rather than times?
359          */
360         if ((s->flags & F_FLAGS) && ((s->st_flags != p->fts_statp->st_flags)
361             || mflag || iflag)) {
362                 if (s->st_flags != p->fts_statp->st_flags) {
363                         char *f_s;
364                         LABEL;
365                         f_s = flags_to_string(s->st_flags, "none");
366                         printf(flavor == F_FREEBSD9 ?
367                             "%sflags expected \"%s\" found " :
368                             "%sflags (\"%s\" is not ", tab, f_s);
369                         free(f_s);
370                         f_s = flags_to_string(p->fts_statp->st_flags, "none");
371                         printf("\"%s\"", f_s);
372                         free(f_s);
373                 }
374                 if (uflag) {
375                         if (iflag)
376                                 SETFLAGS(0, CH_MASK);
377                         else if (mflag)
378                                 CLEARFLAGS(0, SP_FLGS);
379                         else
380                                 SETFLAGS(0, (~SP_FLGS & CH_MASK));
381                 } else
382                         printf("%s\n", flavor == F_FREEBSD9 ? "" : ")");
383                 tab = "\t";
384         }
385 #endif  /* HAVE_STRUCT_STAT_ST_FLAGS */
386
387         /*
388          * from this point, no more permission checking or whacking
389          * occurs, only checking of stuff like checksums and symlinks.
390          */
391  afterpermwhack:
392         if (s->flags & F_CKSUM) {
393                 if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0) {
394                         LABEL;
395                         printf("%scksum: %s: %s\n",
396                             tab, p->fts_accpath, strerror(errno));
397                         tab = "\t";
398                 } else if (crc(fd, &val, &len)) {
399                         close(fd);
400                         LABEL;
401                         printf("%scksum: %s: %s\n",
402                             tab, p->fts_accpath, strerror(errno));
403                         tab = "\t";
404                 } else {
405                         close(fd);
406                         if (s->cksum != val) {
407                                 LABEL;
408                                 printf(flavor == F_FREEBSD9 ?
409                                     "%scksum expected %lu found %lu\n" :
410                                     "%scksum (%lu, %lu)\n",
411                                     tab, s->cksum, (unsigned long)val);
412                         }
413                         tab = "\t";
414                 }
415         }
416 #ifndef NO_MD5
417         if (s->flags & F_MD5) {
418                 if ((digestbuf = dohash(F_MD5, p->fts_accpath)) == NULL) {
419                         LABEL;
420                         printf("%s%s: %s: %s\n",
421                             tab, MD5KEY, p->fts_accpath, strerror(errno));
422                         tab = "\t";
423                 } else {
424                         if (strcmp(s->md5digest, digestbuf)) {
425                                 LABEL;
426                                 printf(flavor == F_FREEBSD9 ?
427                                     "%s%s expected %s found %s\n" :
428                                     "%s%s (0x%s, 0x%s)\n",
429                                     tab, MD5KEY, s->md5digest, digestbuf);
430                         }
431                         tab = "\t";
432                         free(digestbuf);
433                 }
434         }
435 #endif  /* ! NO_MD5 */
436 #ifndef NO_RMD160
437         if (s->flags & F_RMD160) {
438                 if ((digestbuf = dohash(F_RMD160, p->fts_accpath)) == NULL) {
439                         LABEL;
440                         printf("%s%s: %s: %s\n",
441                             tab, RMD160KEY, p->fts_accpath, strerror(errno));
442                         tab = "\t";
443                 } else {
444                         if (strcmp(s->rmd160digest, digestbuf)) {
445                                 LABEL;
446                                 printf(flavor == F_FREEBSD9 ?
447                                     "%s%s expected %s found %s\n" :
448                                     "%s%s (0x%s, 0x%s)\n",
449                                     tab, RMD160KEY, s->rmd160digest, digestbuf);
450                         }
451                         tab = "\t";
452                         free(digestbuf);
453                 }
454         }
455 #endif  /* ! NO_RMD160 */
456 #ifndef NO_SHA
457         if (s->flags & F_SHA1) {
458                 if ((digestbuf = dohash(F_SHA1, p->fts_accpath)) == NULL) {
459                         LABEL;
460                         printf("%s%s: %s: %s\n",
461                             tab, SHA1KEY, p->fts_accpath, strerror(errno));
462                         tab = "\t";
463                 } else {
464                         if (strcmp(s->sha1digest, digestbuf)) {
465                                 LABEL;
466                                 printf(flavor == F_FREEBSD9 ?
467                                     "%s%s expected %s found %s\n" :
468                                     "%s%s (0x%s, 0x%s)\n",
469                                     tab, SHA1KEY, s->sha1digest, digestbuf);
470                         }
471                         tab = "\t";
472                         free(digestbuf);
473                 }
474         }
475         if (s->flags & F_SHA256) {
476                 if ((digestbuf = dohash(F_SHA256, p->fts_accpath)) == NULL) {
477                         LABEL;
478                         printf("%s%s: %s: %s\n",
479                             tab, SHA256KEY, p->fts_accpath, strerror(errno));
480                         tab = "\t";
481                 } else {
482                         if (strcmp(s->sha256digest, digestbuf)) {
483                                 LABEL;
484                                 printf(flavor == F_FREEBSD9 ?
485                                     "%s%s expected %s found %s\n" :
486                                     "%s%s (0x%s, 0x%s)\n",
487                                     tab, SHA256KEY, s->sha256digest, digestbuf);
488                         }
489                         tab = "\t";
490                         free(digestbuf);
491                 }
492         }
493 #ifdef SHA384_DIGEST_LENGTH
494         if (s->flags & F_SHA384) {
495                 if ((digestbuf = dohash(F_SHA384, p->fts_accpath)) == NULL) {
496                         LABEL;
497                         printf("%s%s: %s: %s\n",
498                             tab, SHA384KEY, p->fts_accpath, strerror(errno));
499                         tab = "\t";
500                 } else {
501                         if (strcmp(s->sha384digest, digestbuf)) {
502                                 LABEL;
503                                 printf(flavor == F_FREEBSD9 ?
504                                     "%s%s expected %s found %s\n" :
505                                     "%s%s (0x%s, 0x%s)\n",
506                                     tab, SHA384KEY, s->sha384digest, digestbuf);
507                         }
508                         tab = "\t";
509                         free(digestbuf);
510                 }
511         }
512 #endif
513         if (s->flags & F_SHA512) {
514                 if ((digestbuf = dohash(F_SHA512, p->fts_accpath)) == NULL) {
515                         LABEL;
516                         printf("%s%s: %s: %s\n",
517                             tab, SHA512KEY, p->fts_accpath, strerror(errno));
518                         tab = "\t";
519                 } else {
520                         if (strcmp(s->sha512digest, digestbuf)) {
521                                 LABEL;
522                                 printf(flavor == F_FREEBSD9 ?
523                                     "%s%s expected %s found %s\n" :
524                                     "%s%s (0x%s, 0x%s)\n",
525                                     tab, SHA512KEY, s->sha512digest, digestbuf);
526                         }
527                         tab = "\t";
528                         free(digestbuf);
529                 }
530         }
531 #endif  /* ! NO_SHA */
532         if (s->flags & F_SLINK &&
533             strcmp(cp = rlink(p->fts_accpath), s->slink)) {
534                 LABEL;
535                 printf(flavor == F_FREEBSD9 ?
536                     "%slink ref expected %s found %s" :
537                     "%slink ref (%s, %s", tab, cp, s->slink);
538                 if (uflag) {
539                         if ((unlink(p->fts_accpath) == -1) ||
540                             (symlink(s->slink, p->fts_accpath) == -1) )
541                                 printf(", not modified: %s%s\n",
542                                     strerror(errno),
543                                     flavor == F_FREEBSD9 ? "" : ")");
544                         else
545                                 printf(", modified%s\n",
546                                     flavor == F_FREEBSD9 ? "" : ")");
547                 } else
548                         printf("%s\n", flavor == F_FREEBSD9 ? "" : ")");
549         }
550         return (label);
551 }
552
553 const char *
554 rlink(const char *name)
555 {
556         static char lbuf[MAXPATHLEN];
557         int len;
558
559         if ((len = readlink(name, lbuf, sizeof(lbuf) - 1)) == -1)
560                 mtree_err("%s: %s", name, strerror(errno));
561         lbuf[len] = '\0';
562         return (lbuf);
563 }