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