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