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