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