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 #if HAVE_NBTOOL_CONFIG_H 34 #include "nbtool_config.h" 35 #endif 36 37 #include <sys/param.h> 38 #include <sys/stat.h> 39 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> 48 49 #ifndef NO_MD5 50 #include <openssl/md5.h> 51 #endif 52 #ifndef NO_RMD160 53 #include <openssl/ripemd.h> 54 #endif 55 #ifndef NO_SHA 56 #include <openssl/sha.h> 57 #endif 58 59 #include "extern.h" 60 61 #define INDENTNAMELEN 8 62 #define MARK \ 63 do { \ 64 if (flavor == F_FREEBSD9) { \ 65 len = printf("%s changed\n", RP(p)); \ 66 tab = "\t"; \ 67 } else { \ 68 len = printf("%s: ", RP(p)); \ 69 if (len > INDENTNAMELEN) { \ 70 tab = "\t"; \ 71 printf("\n"); \ 72 } else { \ 73 tab = ""; \ 74 printf("%*s", INDENTNAMELEN - (int)len, ""); \ 75 } \ 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 char *sf; \ 86 if (!label) { \ 87 MARK; \ 88 sf = flags_to_string(p->fts_statp->st_flags, "none"); \ 89 printf("%sflags (\"%s\"", tab, sf); \ 90 free(sf); \ 91 } \ 92 if (lchflags(p->fts_accpath, flags)) { \ 93 label++; \ 94 printf(", not modified: %s)\n", \ 95 strerror(errno)); \ 96 } else { \ 97 sf = flags_to_string(flags, "none"); \ 98 printf(", modified to \"%s\")\n", sf); \ 99 free(sf); \ 100 } \ 101 } 102 103 /* SETFLAGS: 104 * given pflags, additionally set those flags specified in s->st_flags and 105 * selected by mask (the other flags are left unchanged). 106 */ 107 #define SETFLAGS(pflags, mask) \ 108 do { \ 109 flags = (s->st_flags & (mask)) | (pflags); \ 110 CHANGEFLAGS; \ 111 } while (0) 112 113 /* CLEARFLAGS: 114 * given pflags, reset the flags specified in s->st_flags and selected by mask 115 * (the other flags are left unchanged). 116 */ 117 #define CLEARFLAGS(pflags, mask) \ 118 do { \ 119 flags = (~(s->st_flags & (mask)) & CH_MASK) & (pflags); \ 120 CHANGEFLAGS; \ 121 } while (0) 122 #endif /* HAVE_STRUCT_STAT_ST_FLAGS */ 123 124 int 125 compare(NODE *s, FTSENT *p) 126 { 127 u_int32_t len, val, flags; 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) && !defined(HAVE_NBTOOL_CONFIG_H) 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 } 564