1 /* $NetBSD: compare.c,v 1.11 1996/09/05 09:56:48 mycroft Exp $ */ 2 /* $OpenBSD: compare.c,v 1.13 2001/10/01 16:48:18 jakob 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. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37 #ifndef lint 38 #if 0 39 static const char sccsid[] = "@(#)compare.c 8.1 (Berkeley) 6/6/93"; 40 #else 41 static const char rcsid[] = "$OpenBSD: compare.c,v 1.13 2001/10/01 16:48:18 jakob Exp $"; 42 #endif 43 #endif /* not lint */ 44 45 #include <sys/param.h> 46 #include <sys/stat.h> 47 #include <fcntl.h> 48 #include <fts.h> 49 #include <errno.h> 50 #include <stdio.h> 51 #include <time.h> 52 #include <unistd.h> 53 #include <md5.h> 54 #include <sha1.h> 55 #include <rmd160.h> 56 #include "mtree.h" 57 #include "extern.h" 58 59 extern int lflag, tflag, uflag; 60 61 static char *ftype __P((u_int)); 62 63 #define INDENTNAMELEN 8 64 #define LABEL \ 65 if (!label++) { \ 66 len = printf("%s: ", RP(p)); \ 67 if (len > INDENTNAMELEN) { \ 68 tab = "\t"; \ 69 (void)printf("\n"); \ 70 } else { \ 71 tab = ""; \ 72 (void)printf("%*s", INDENTNAMELEN - (int)len, ""); \ 73 } \ 74 } 75 76 #define REPLACE_COMMA(x) \ 77 do { \ 78 char *l; \ 79 for (l = x; *l; l++) { \ 80 if (*l == ',') \ 81 *l = ' '; \ 82 } \ 83 } while (0) \ 84 85 int 86 compare(name, s, p) 87 char *name; 88 register NODE *s; 89 register FTSENT *p; 90 { 91 u_int32_t len, val; 92 int fd, label; 93 char *cp, *tab = ""; 94 95 label = 0; 96 switch(s->type) { 97 case F_BLOCK: 98 if (!S_ISBLK(p->fts_statp->st_mode)) 99 goto typeerr; 100 break; 101 case F_CHAR: 102 if (!S_ISCHR(p->fts_statp->st_mode)) 103 goto typeerr; 104 break; 105 case F_DIR: 106 if (!S_ISDIR(p->fts_statp->st_mode)) 107 goto typeerr; 108 break; 109 case F_FIFO: 110 if (!S_ISFIFO(p->fts_statp->st_mode)) 111 goto typeerr; 112 break; 113 case F_FILE: 114 if (!S_ISREG(p->fts_statp->st_mode)) 115 goto typeerr; 116 break; 117 case F_LINK: 118 if (!S_ISLNK(p->fts_statp->st_mode)) 119 goto typeerr; 120 break; 121 case F_SOCK: 122 if (!S_ISSOCK(p->fts_statp->st_mode)) { 123 typeerr: LABEL; 124 (void)printf("\ttype (%s, %s)\n", 125 ftype(s->type), inotype(p->fts_statp->st_mode)); 126 } 127 break; 128 } 129 /* Set the uid/gid first, then set the mode. */ 130 if (s->flags & (F_UID | F_UNAME) && s->st_uid != p->fts_statp->st_uid) { 131 LABEL; 132 (void)printf("%suser (%u, %u", 133 tab, s->st_uid, p->fts_statp->st_uid); 134 if (uflag) 135 if (chown(p->fts_accpath, s->st_uid, -1)) 136 (void)printf(", not modified: %s)\n", 137 strerror(errno)); 138 else 139 (void)printf(", modified)\n"); 140 else 141 (void)printf(")\n"); 142 tab = "\t"; 143 } 144 if (s->flags & (F_GID | F_GNAME) && s->st_gid != p->fts_statp->st_gid) { 145 LABEL; 146 (void)printf("%sgid (%u, %u", 147 tab, s->st_gid, p->fts_statp->st_gid); 148 if (uflag) 149 if (chown(p->fts_accpath, -1, s->st_gid)) 150 (void)printf(", not modified: %s)\n", 151 strerror(errno)); 152 else 153 (void)printf(", modified)\n"); 154 else 155 (void)printf(")\n"); 156 tab = "\t"; 157 } 158 if (s->flags & F_MODE && 159 s->st_mode != (p->fts_statp->st_mode & MBITS)) { 160 if (lflag) { 161 mode_t tmode, mode; 162 163 tmode = s->st_mode; 164 mode = p->fts_statp->st_mode & MBITS; 165 /* 166 * if none of the suid/sgid/etc bits are set, 167 * then if the mode is a subset of the target, 168 * skip. 169 */ 170 if (!((tmode & ~(S_IRWXU|S_IRWXG|S_IRWXO)) || 171 (mode & ~(S_IRWXU|S_IRWXG|S_IRWXO)))) 172 if ((mode | tmode) == tmode) 173 goto skip; 174 } 175 LABEL; 176 (void)printf("%spermissions (%#o, %#o", 177 tab, s->st_mode, p->fts_statp->st_mode & MBITS); 178 if (uflag) 179 if (chmod(p->fts_accpath, s->st_mode)) 180 (void)printf(", not modified: %s)\n", 181 strerror(errno)); 182 else 183 (void)printf(", modified)\n"); 184 else 185 (void)printf(")\n"); 186 tab = "\t"; 187 skip: 188 } 189 if (s->flags & F_NLINK && s->type != F_DIR && 190 s->st_nlink != p->fts_statp->st_nlink) { 191 LABEL; 192 (void)printf("%slink count (%u, %u)\n", 193 tab, s->st_nlink, p->fts_statp->st_nlink); 194 tab = "\t"; 195 } 196 if (s->flags & F_SIZE && s->st_size != p->fts_statp->st_size) { 197 LABEL; 198 (void)printf("%ssize (%qd, %qd)\n", 199 tab, s->st_size, p->fts_statp->st_size); 200 tab = "\t"; 201 } 202 /* 203 * XXX 204 * Since utimes(2) only takes a timeval, there's no point in 205 * comparing the low bits of the timespec nanosecond field. This 206 * will only result in mismatches that we can never fix. 207 * 208 * Doesn't display microsecond differences. 209 */ 210 if (s->flags & F_TIME) { 211 struct timeval tv[2]; 212 213 TIMESPEC_TO_TIMEVAL(&tv[0], &s->st_mtimespec); 214 TIMESPEC_TO_TIMEVAL(&tv[1], &p->fts_statp->st_mtimespec); 215 if (tv[0].tv_sec != tv[1].tv_sec || 216 tv[0].tv_usec != tv[1].tv_usec) { 217 LABEL; 218 (void)printf("%smodification time (%.24s, ", 219 tab, ctime(&s->st_mtimespec.tv_sec)); 220 (void)printf("%.24s", 221 ctime(&p->fts_statp->st_mtimespec.tv_sec)); 222 if (tflag) { 223 tv[1] = tv[0]; 224 if (utimes(p->fts_accpath, tv)) 225 (void)printf(", not modified: %s)\n", 226 strerror(errno)); 227 else 228 (void)printf(", modified)\n"); 229 } else 230 (void)printf(")\n"); 231 tab = "\t"; 232 } 233 } 234 if (s->flags & F_CKSUM) { 235 if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0) { 236 LABEL; 237 (void)printf("%scksum: %s: %s\n", 238 tab, p->fts_accpath, strerror(errno)); 239 tab = "\t"; 240 } else if (crc(fd, &val, &len)) { 241 (void)close(fd); 242 LABEL; 243 (void)printf("%scksum: %s: %s\n", 244 tab, p->fts_accpath, strerror(errno)); 245 tab = "\t"; 246 } else { 247 (void)close(fd); 248 if (s->cksum != val) { 249 LABEL; 250 (void)printf("%scksum (%u, %u)\n", 251 tab, s->cksum, val); 252 } 253 tab = "\t"; 254 } 255 } 256 if (s->flags & F_MD5) { 257 char *new_digest, buf[33]; 258 259 new_digest = MD5File(p->fts_accpath, buf); 260 if (!new_digest) { 261 LABEL; 262 printf("%sMD5File: %s: %s\n", tab, p->fts_accpath, 263 strerror(errno)); 264 tab = "\t"; 265 } else if (strcmp(new_digest, s->md5digest)) { 266 LABEL; 267 printf("%sMD5 (%s, %s)\n", tab, s->md5digest, 268 new_digest); 269 tab = "\t"; 270 } 271 } 272 if (s->flags & F_RMD160) { 273 char *new_digest, buf[41]; 274 275 new_digest = RMD160File(p->fts_accpath, buf); 276 if (!new_digest) { 277 LABEL; 278 printf("%sRMD160File: %s: %s\n", tab, p->fts_accpath, 279 strerror(errno)); 280 tab = "\t"; 281 } else if (strcmp(new_digest, s->rmd160digest)) { 282 LABEL; 283 printf("%sRMD160 (%s, %s)\n", tab, s->rmd160digest, 284 new_digest); 285 tab = "\t"; 286 } 287 } 288 if (s->flags & F_SHA1) { 289 char *new_digest, buf[41]; 290 291 new_digest = SHA1File(p->fts_accpath, buf); 292 if (!new_digest) { 293 LABEL; 294 printf("%sSHA1File: %s: %s\n", tab, p->fts_accpath, 295 strerror(errno)); 296 tab = "\t"; 297 } else if (strcmp(new_digest, s->sha1digest)) { 298 LABEL; 299 printf("%sSHA1 (%s, %s)\n", tab, s->sha1digest, 300 new_digest); 301 tab = "\t"; 302 } 303 } 304 if (s->flags & F_SLINK && strcmp(cp = rlink(name), s->slink)) { 305 LABEL; 306 (void)printf("%slink ref (%s, %s)\n", tab, cp, s->slink); 307 } 308 if (s->flags & F_FLAGS && s->file_flags != p->fts_statp->st_flags) { 309 char *db_flags = NULL; 310 char *cur_flags = NULL; 311 312 if ((db_flags = fflagstostr(s->file_flags)) == NULL || 313 (cur_flags = fflagstostr(p->fts_statp->st_flags)) == NULL) { 314 LABEL; 315 (void)printf("%sflags: %s %s\n", tab, p->fts_accpath, 316 strerror(errno)); 317 tab = "\t"; 318 if (db_flags != NULL) 319 free(db_flags); 320 if (cur_flags != NULL) 321 free(cur_flags); 322 } else { 323 LABEL; 324 REPLACE_COMMA(db_flags); 325 REPLACE_COMMA(cur_flags); 326 printf("%sflags (%s, %s", tab, (*db_flags == '\0') ? 327 "-" : db_flags, 328 (*cur_flags == '\0') ? 329 "-" : cur_flags); 330 tab = "\t"; 331 if (uflag) 332 if (chflags(p->fts_accpath, s->file_flags)) 333 (void)printf(", not modified: %s)\n", 334 strerror(errno)); 335 else 336 (void)printf(", modified)\n"); 337 else 338 (void)printf(")\n"); 339 tab = "\t"; 340 341 free(db_flags); 342 free(cur_flags); 343 } 344 } 345 return (label); 346 } 347 348 char * 349 inotype(type) 350 u_int type; 351 { 352 switch(type & S_IFMT) { 353 case S_IFBLK: 354 return ("block"); 355 case S_IFCHR: 356 return ("char"); 357 case S_IFDIR: 358 return ("dir"); 359 case S_IFIFO: 360 return ("fifo"); 361 case S_IFREG: 362 return ("file"); 363 case S_IFLNK: 364 return ("link"); 365 case S_IFSOCK: 366 return ("socket"); 367 default: 368 return ("unknown"); 369 } 370 /* NOTREACHED */ 371 } 372 373 static char * 374 ftype(type) 375 u_int type; 376 { 377 switch(type) { 378 case F_BLOCK: 379 return ("block"); 380 case F_CHAR: 381 return ("char"); 382 case F_DIR: 383 return ("dir"); 384 case F_FIFO: 385 return ("fifo"); 386 case F_FILE: 387 return ("file"); 388 case F_LINK: 389 return ("link"); 390 case F_SOCK: 391 return ("socket"); 392 default: 393 return ("unknown"); 394 } 395 /* NOTREACHED */ 396 } 397 398 char * 399 rlink(name) 400 char *name; 401 { 402 static char lbuf[MAXPATHLEN]; 403 register int len; 404 405 if ((len = readlink(name, lbuf, sizeof(lbuf)-1)) == -1) 406 error("%s: %s", name, strerror(errno)); 407 lbuf[len] = '\0'; 408 return (lbuf); 409 } 410