1 /* $NetBSD: compare.c,v 1.40 2002/02/08 18:15:12 tv 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. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 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.40 2002/02/08 18:15:12 tv 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 <rmd160.h> 59 #endif 60 #ifndef NO_SHA1 61 #include <sha1.h> 62 #endif 63 64 #include "extern.h" 65 66 #define INDENTNAMELEN 8 67 #define MARK \ 68 do { \ 69 len = printf("%s: ", RP(p)); \ 70 if (len > INDENTNAMELEN) { \ 71 tab = "\t"; \ 72 printf("\n"); \ 73 } else { \ 74 tab = ""; \ 75 printf("%*s", INDENTNAMELEN - (int)len, ""); \ 76 } \ 77 } while (0) 78 #define LABEL if (!label++) MARK 79 80 #define CHANGEFLAGS(path, oflags) \ 81 if (flags != (oflags)) { \ 82 if (!label) { \ 83 MARK; \ 84 printf("%sflags (\"%s\"", tab, \ 85 flags_to_string(p->fts_statp->st_flags, "none")); \ 86 } \ 87 if (lchflags(path, flags)) { \ 88 label++; \ 89 printf(", not modified: %s)\n", \ 90 strerror(errno)); \ 91 } else \ 92 printf(", modified to \"%s\")\n", \ 93 flags_to_string(flags, "none")); \ 94 } 95 96 /* SETFLAGS: 97 * given pflags, additionally set those flags specified in sflags and 98 * selected by mask (the other flags are left unchanged). oflags is 99 * passed as reference to check if lchflags is necessary. 100 */ 101 #define SETFLAGS(path, sflags, pflags, oflags, mask) \ 102 do { \ 103 flags = ((sflags) & (mask)) | (pflags); \ 104 CHANGEFLAGS(path, oflags); \ 105 } while (0) 106 107 /* CLEARFLAGS: 108 * given pflags, reset the flags specified in sflags and selected by mask 109 * (the other flags are left unchanged). oflags is 110 * passed as reference to check if lchflags is necessary. 111 */ 112 #define CLEARFLAGS(path, sflags, pflags, oflags, mask) \ 113 do { \ 114 flags = (~((sflags) & (mask)) & CH_MASK) & (pflags); \ 115 CHANGEFLAGS(path, oflags); \ 116 } while (0) 117 118 int 119 compare(NODE *s, FTSENT *p) 120 { 121 u_int32_t len, val, flags; 122 int fd, label; 123 const char *cp, *tab; 124 #if !defined(NO_MD5) || !defined(NO_RMD160) || !defined(NO_SHA1) 125 char digestbuf[41]; /* large enough for {MD5,RMD160,SHA1}File() */ 126 #endif 127 128 tab = NULL; 129 label = 0; 130 switch(s->type) { 131 case F_BLOCK: 132 if (!S_ISBLK(p->fts_statp->st_mode)) 133 goto typeerr; 134 break; 135 case F_CHAR: 136 if (!S_ISCHR(p->fts_statp->st_mode)) 137 goto typeerr; 138 break; 139 case F_DIR: 140 if (!S_ISDIR(p->fts_statp->st_mode)) 141 goto typeerr; 142 break; 143 case F_FIFO: 144 if (!S_ISFIFO(p->fts_statp->st_mode)) 145 goto typeerr; 146 break; 147 case F_FILE: 148 if (!S_ISREG(p->fts_statp->st_mode)) 149 goto typeerr; 150 break; 151 case F_LINK: 152 if (!S_ISLNK(p->fts_statp->st_mode)) 153 goto typeerr; 154 break; 155 case F_SOCK: 156 if (!S_ISSOCK(p->fts_statp->st_mode)) { 157 typeerr: LABEL; 158 printf("\ttype (%s, %s)\n", 159 nodetype(s->type), inotype(p->fts_statp->st_mode)); 160 return (label); 161 } 162 break; 163 } 164 if (Wflag) 165 goto afterpermwhack; 166 #if HAVE_STRUCT_STAT_ST_FLAGS 167 if (iflag && !uflag) { 168 if (s->flags & F_FLAGS) 169 SETFLAGS(p->fts_accpath, s->st_flags, 170 p->fts_statp->st_flags, p->fts_statp->st_flags, 171 SP_FLGS); 172 return (label); 173 } 174 if (mflag && !uflag) { 175 if (s->flags & F_FLAGS) 176 CLEARFLAGS(p->fts_accpath, s->st_flags, 177 p->fts_statp->st_flags, p->fts_statp->st_flags, 178 SP_FLGS); 179 return (label); 180 } 181 #endif 182 if (s->flags & F_DEV && 183 (s->type == F_BLOCK || s->type == F_CHAR) && 184 s->st_rdev != p->fts_statp->st_rdev) { 185 LABEL; 186 printf("%sdevice (%#x, %#x", 187 tab, s->st_rdev, p->fts_statp->st_rdev); 188 if (uflag) { 189 if ((unlink(p->fts_accpath) == -1) || 190 (mknod(p->fts_accpath, 191 s->st_mode | nodetoino(s->type), 192 s->st_rdev) == -1) || 193 (lchown(p->fts_accpath, p->fts_statp->st_uid, 194 p->fts_statp->st_gid) == -1) ) 195 printf(", not modified: %s)\n", 196 strerror(errno)); 197 else 198 printf(", modified)\n"); 199 } else 200 printf(")\n"); 201 tab = "\t"; 202 } 203 /* Set the uid/gid first, then set the mode. */ 204 if (s->flags & (F_UID | F_UNAME) && s->st_uid != p->fts_statp->st_uid) { 205 LABEL; 206 printf("%suser (%lu, %lu", 207 tab, (u_long)s->st_uid, (u_long)p->fts_statp->st_uid); 208 if (uflag) { 209 if (lchown(p->fts_accpath, s->st_uid, -1)) 210 printf(", not modified: %s)\n", 211 strerror(errno)); 212 else 213 printf(", modified)\n"); 214 } else 215 printf(")\n"); 216 tab = "\t"; 217 } 218 if (s->flags & (F_GID | F_GNAME) && s->st_gid != p->fts_statp->st_gid) { 219 LABEL; 220 printf("%sgid (%lu, %lu", 221 tab, (u_long)s->st_gid, (u_long)p->fts_statp->st_gid); 222 if (uflag) { 223 if (lchown(p->fts_accpath, -1, s->st_gid)) 224 printf(", not modified: %s)\n", 225 strerror(errno)); 226 else 227 printf(", modified)\n"); 228 } 229 else 230 printf(")\n"); 231 tab = "\t"; 232 } 233 if (s->flags & F_MODE && 234 s->st_mode != (p->fts_statp->st_mode & MBITS)) { 235 if (lflag) { 236 mode_t tmode, mode; 237 238 tmode = s->st_mode; 239 mode = p->fts_statp->st_mode & MBITS; 240 /* 241 * if none of the suid/sgid/etc bits are set, 242 * then if the mode is a subset of the target, 243 * skip. 244 */ 245 if (!((tmode & ~(S_IRWXU|S_IRWXG|S_IRWXO)) || 246 (mode & ~(S_IRWXU|S_IRWXG|S_IRWXO)))) 247 if ((mode | tmode) == tmode) 248 goto skip; 249 } 250 251 LABEL; 252 printf("%spermissions (%#lo, %#lo", 253 tab, (u_long)s->st_mode, 254 (u_long)p->fts_statp->st_mode & MBITS); 255 if (uflag) { 256 #if HAVE_LCHMOD 257 if (lchmod(p->fts_accpath, s->st_mode)) 258 #else 259 if (S_ISLNK(p->fts_statp->st_mode)) 260 printf(", not modified: no lchmod call\n"); 261 else if (chmod(p->fts_accpath, s->st_mode)) 262 #endif 263 printf(", not modified: %s)\n", 264 strerror(errno)); 265 else 266 printf(", modified)\n"); 267 } 268 else 269 printf(")\n"); 270 tab = "\t"; 271 skip: ; 272 } 273 if (s->flags & F_NLINK && s->type != F_DIR && 274 s->st_nlink != p->fts_statp->st_nlink) { 275 LABEL; 276 printf("%slink count (%lu, %lu)\n", 277 tab, (u_long)s->st_nlink, (u_long)p->fts_statp->st_nlink); 278 tab = "\t"; 279 } 280 if (s->flags & F_SIZE && s->st_size != p->fts_statp->st_size) { 281 LABEL; 282 printf("%ssize (%lld, %lld)\n", 283 tab, (long long)s->st_size, 284 (long long)p->fts_statp->st_size); 285 tab = "\t"; 286 } 287 /* 288 * XXX 289 * Since utimes(2) only takes a timeval, there's no point in 290 * comparing the low bits of the timespec nanosecond field. This 291 * will only result in mismatches that we can never fix. 292 * 293 * Doesn't display microsecond differences. 294 */ 295 if (s->flags & F_TIME) { 296 struct timeval tv[2]; 297 struct stat *ps = p->fts_statp; 298 time_t smtime = s->st_mtimespec.tv_sec; 299 300 #ifdef BSD4_4 301 time_t pmtime = ps->st_mtimespec.tv_sec; 302 303 TIMESPEC_TO_TIMEVAL(&tv[0], &s->st_mtimespec); 304 TIMESPEC_TO_TIMEVAL(&tv[1], &ps->st_mtimespec); 305 #else 306 time_t pmtime = (time_t)ps->st_mtime; 307 308 tv[0].tv_sec = smtime; 309 tv[0].tv_usec = 0; 310 tv[1].tv_sec = pmtime; 311 tv[1].tv_usec = 0; 312 #endif 313 314 if (tv[0].tv_sec != tv[1].tv_sec || 315 tv[0].tv_usec != tv[1].tv_usec) { 316 LABEL; 317 printf("%smodification time (%.24s, ", 318 tab, ctime(&smtime)); 319 printf("%.24s", ctime(&pmtime)); 320 if (tflag) { 321 tv[1] = tv[0]; 322 if (utimes(p->fts_accpath, tv)) 323 printf(", not modified: %s)\n", 324 strerror(errno)); 325 else 326 printf(", modified)\n"); 327 } else 328 printf(")\n"); 329 tab = "\t"; 330 } 331 } 332 #if HAVE_STRUCT_STAT_ST_FLAGS 333 /* 334 * XXX 335 * since lchflags(2) will reset file times, the utimes() above 336 * may have been useless! oh well, we'd rather have correct 337 * flags, rather than times? 338 */ 339 if ((s->flags & F_FLAGS) && ((s->st_flags != p->fts_statp->st_flags) 340 || mflag || iflag)) { 341 if (s->st_flags != p->fts_statp->st_flags) { 342 LABEL; 343 printf("%sflags (\"%s\" is not ", tab, 344 flags_to_string(s->st_flags, "none")); 345 printf("\"%s\"", 346 flags_to_string(p->fts_statp->st_flags, "none")); 347 } 348 if (uflag) { 349 if (iflag) 350 SETFLAGS(p->fts_accpath, s->st_flags, 351 0, p->fts_statp->st_flags, CH_MASK); 352 else if (mflag) 353 CLEARFLAGS(p->fts_accpath, s->st_flags, 354 0, p->fts_statp->st_flags, SP_FLGS); 355 else 356 SETFLAGS(p->fts_accpath, s->st_flags, 357 0, p->fts_statp->st_flags, 358 (~SP_FLGS & CH_MASK)); 359 } else 360 printf(")\n"); 361 tab = "\t"; 362 } 363 #endif 364 365 /* 366 * from this point, no more permission checking or whacking 367 * occurs, only checking of stuff like checksums and symlinks. 368 */ 369 afterpermwhack: 370 if (s->flags & F_CKSUM) { 371 if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0) { 372 LABEL; 373 printf("%scksum: %s: %s\n", 374 tab, p->fts_accpath, strerror(errno)); 375 tab = "\t"; 376 } else if (crc(fd, &val, &len)) { 377 close(fd); 378 LABEL; 379 printf("%scksum: %s: %s\n", 380 tab, p->fts_accpath, strerror(errno)); 381 tab = "\t"; 382 } else { 383 close(fd); 384 if (s->cksum != val) { 385 LABEL; 386 printf("%scksum (%lu, %lu)\n", 387 tab, s->cksum, (unsigned long)val); 388 } 389 tab = "\t"; 390 } 391 } 392 #ifndef NO_MD5 393 if (s->flags & F_MD5) { 394 if (MD5File(p->fts_accpath, digestbuf) == NULL) { 395 LABEL; 396 printf("%smd5: %s: %s\n", 397 tab, p->fts_accpath, strerror(errno)); 398 tab = "\t"; 399 } else { 400 if (strcmp(s->md5digest, digestbuf)) { 401 LABEL; 402 printf("%smd5 (0x%s, 0x%s)\n", 403 tab, s->md5digest, digestbuf); 404 } 405 tab = "\t"; 406 } 407 } 408 #endif /* ! NO_MD5 */ 409 #ifndef NO_RMD160 410 if (s->flags & F_RMD160) { 411 if (RMD160File(p->fts_accpath, digestbuf) == NULL) { 412 LABEL; 413 printf("%srmd160: %s: %s\n", 414 tab, p->fts_accpath, strerror(errno)); 415 tab = "\t"; 416 } else { 417 if (strcmp(s->rmd160digest, digestbuf)) { 418 LABEL; 419 printf("%srmd160 (0x%s, 0x%s)\n", 420 tab, s->rmd160digest, digestbuf); 421 } 422 tab = "\t"; 423 } 424 } 425 #endif /* ! NO_RMD160 */ 426 #ifndef NO_SHA1 427 if (s->flags & F_SHA1) { 428 if (SHA1File(p->fts_accpath, digestbuf) == NULL) { 429 LABEL; 430 printf("%ssha1: %s: %s\n", 431 tab, p->fts_accpath, strerror(errno)); 432 tab = "\t"; 433 } else { 434 if (strcmp(s->sha1digest, digestbuf)) { 435 LABEL; 436 printf("%ssha1 (0x%s, 0x%s)\n", 437 tab, s->sha1digest, digestbuf); 438 } 439 tab = "\t"; 440 } 441 } 442 #endif /* ! NO_SHA1 */ 443 if (s->flags & F_SLINK && 444 strcmp(cp = rlink(p->fts_accpath), s->slink)) { 445 LABEL; 446 printf("%slink ref (%s, %s", tab, cp, s->slink); 447 if (uflag) { 448 if ((unlink(p->fts_accpath) == -1) || 449 (symlink(s->slink, p->fts_accpath) == -1) ) 450 printf(", not modified: %s)\n", 451 strerror(errno)); 452 else 453 printf(", modified)\n"); 454 } else 455 printf(")\n"); 456 } 457 return (label); 458 } 459 460 const char * 461 rlink(const char *name) 462 { 463 static char lbuf[MAXPATHLEN]; 464 int len; 465 466 if ((len = readlink(name, lbuf, sizeof(lbuf) - 1)) == -1) 467 mtree_err("%s: %s", name, strerror(errno)); 468 lbuf[len] = '\0'; 469 return (lbuf); 470 } 471