1 /* $NetBSD: compare.c,v 1.11 1996/09/05 09:56:48 mycroft Exp $ */ 2 /* $OpenBSD: compare.c,v 1.27 2016/08/16 16:41:46 krw 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/stat.h> 34 #include <fcntl.h> 35 #include <fts.h> 36 #include <errno.h> 37 #include <stdio.h> 38 #include <time.h> 39 #include <unistd.h> 40 #include <limits.h> 41 #include <md5.h> 42 #include <rmd160.h> 43 #include <sha1.h> 44 #include <sha2.h> 45 #include "mtree.h" 46 #include "extern.h" 47 48 extern int lflag, tflag, uflag; 49 50 static char *ftype(u_int); 51 52 #define INDENTNAMELEN 8 53 #define LABEL \ 54 if (!label++) { \ 55 len = printf("%s: ", RP(p)); \ 56 if (len > INDENTNAMELEN) { \ 57 tab = "\t"; \ 58 (void)printf("\n"); \ 59 } else { \ 60 tab = ""; \ 61 (void)printf("%*s", INDENTNAMELEN - (int)len, ""); \ 62 } \ 63 } 64 65 #define REPLACE_COMMA(x) \ 66 do { \ 67 char *l; \ 68 for (l = x; *l; l++) { \ 69 if (*l == ',') \ 70 *l = ' '; \ 71 } \ 72 } while (0) \ 73 74 int 75 compare(char *name, NODE *s, FTSENT *p) 76 { 77 u_int32_t len, val; 78 int fd, label; 79 char *cp, *tab = ""; 80 81 label = 0; 82 switch(s->type) { 83 case F_BLOCK: 84 if (!S_ISBLK(p->fts_statp->st_mode)) 85 goto typeerr; 86 break; 87 case F_CHAR: 88 if (!S_ISCHR(p->fts_statp->st_mode)) 89 goto typeerr; 90 break; 91 case F_DIR: 92 if (!S_ISDIR(p->fts_statp->st_mode)) 93 goto typeerr; 94 break; 95 case F_FIFO: 96 if (!S_ISFIFO(p->fts_statp->st_mode)) 97 goto typeerr; 98 break; 99 case F_FILE: 100 if (!S_ISREG(p->fts_statp->st_mode)) 101 goto typeerr; 102 break; 103 case F_LINK: 104 if (!S_ISLNK(p->fts_statp->st_mode)) 105 goto typeerr; 106 break; 107 case F_SOCK: 108 if (!S_ISSOCK(p->fts_statp->st_mode)) { 109 typeerr: LABEL; 110 (void)printf("\ttype (%s, %s)\n", 111 ftype(s->type), inotype(p->fts_statp->st_mode)); 112 } 113 break; 114 } 115 /* Set the uid/gid first, then set the mode. */ 116 if (s->flags & (F_UID | F_UNAME) && s->st_uid != p->fts_statp->st_uid) { 117 LABEL; 118 (void)printf("%suser (%u, %u", 119 tab, s->st_uid, p->fts_statp->st_uid); 120 if (uflag) 121 if (chown(p->fts_accpath, s->st_uid, -1)) 122 (void)printf(", not modified: %s)\n", 123 strerror(errno)); 124 else 125 (void)printf(", modified)\n"); 126 else 127 (void)printf(")\n"); 128 tab = "\t"; 129 } 130 if (s->flags & (F_GID | F_GNAME) && s->st_gid != p->fts_statp->st_gid) { 131 LABEL; 132 (void)printf("%sgid (%u, %u", 133 tab, s->st_gid, p->fts_statp->st_gid); 134 if (uflag) 135 if (chown(p->fts_accpath, -1, s->st_gid)) 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_MODE && 145 s->st_mode != (p->fts_statp->st_mode & MBITS)) { 146 if (lflag) { 147 mode_t tmode, mode; 148 149 tmode = s->st_mode; 150 mode = p->fts_statp->st_mode & MBITS; 151 /* 152 * if none of the suid/sgid/etc bits are set, 153 * then if the mode is a subset of the target, 154 * skip. 155 */ 156 if (!((tmode & ~(S_IRWXU|S_IRWXG|S_IRWXO)) || 157 (mode & ~(S_IRWXU|S_IRWXG|S_IRWXO)))) 158 if ((mode | tmode) == tmode) 159 goto skip; 160 } 161 LABEL; 162 (void)printf("%spermissions (%#o, %#o", 163 tab, s->st_mode, p->fts_statp->st_mode & MBITS); 164 if (uflag) 165 if (chmod(p->fts_accpath, s->st_mode)) 166 (void)printf(", not modified: %s)\n", 167 strerror(errno)); 168 else 169 (void)printf(", modified)\n"); 170 else 171 (void)printf(")\n"); 172 tab = "\t"; 173 skip: 174 ; 175 } 176 if (s->flags & F_NLINK && s->type != F_DIR && 177 s->st_nlink != p->fts_statp->st_nlink) { 178 LABEL; 179 (void)printf("%slink count (%u, %u)\n", 180 tab, s->st_nlink, p->fts_statp->st_nlink); 181 tab = "\t"; 182 } 183 if (s->flags & F_SIZE && s->st_size != p->fts_statp->st_size) { 184 LABEL; 185 (void)printf("%ssize (%lld, %lld)\n", 186 tab, (long long)s->st_size, 187 (long long)p->fts_statp->st_size); 188 tab = "\t"; 189 } 190 /* 191 * XXX 192 * Since utimes(2) only takes a timeval, there's no point in 193 * comparing the low bits of the timespec nanosecond field. This 194 * will only result in mismatches that we can never fix. 195 * 196 * Doesn't display microsecond differences. 197 */ 198 if (s->flags & F_TIME) { 199 struct timeval tv[2]; 200 201 TIMESPEC_TO_TIMEVAL(&tv[0], &s->st_mtimespec); 202 TIMESPEC_TO_TIMEVAL(&tv[1], &p->fts_statp->st_mtimespec); 203 if (tv[0].tv_sec != tv[1].tv_sec || 204 tv[0].tv_usec != tv[1].tv_usec) { 205 LABEL; 206 (void)printf("%smodification time (%.24s, ", 207 tab, ctime(&s->st_mtimespec.tv_sec)); 208 (void)printf("%.24s", 209 ctime(&p->fts_statp->st_mtimespec.tv_sec)); 210 if (tflag) { 211 tv[1] = tv[0]; 212 if (utimes(p->fts_accpath, tv)) 213 (void)printf(", not modified: %s)\n", 214 strerror(errno)); 215 else 216 (void)printf(", modified)\n"); 217 } else 218 (void)printf(")\n"); 219 tab = "\t"; 220 } 221 } 222 if (s->flags & F_CKSUM) { 223 if ((fd = open(p->fts_accpath, MTREE_O_FLAGS, 0)) < 0) { 224 LABEL; 225 (void)printf("%scksum: %s: %s\n", 226 tab, p->fts_accpath, strerror(errno)); 227 tab = "\t"; 228 } else if (crc(fd, &val, &len)) { 229 (void)close(fd); 230 LABEL; 231 (void)printf("%scksum: %s: %s\n", 232 tab, p->fts_accpath, strerror(errno)); 233 tab = "\t"; 234 } else { 235 (void)close(fd); 236 if (s->cksum != val) { 237 LABEL; 238 (void)printf("%scksum (%u, %u)\n", 239 tab, s->cksum, val); 240 } 241 tab = "\t"; 242 } 243 } 244 if (s->flags & F_MD5) { 245 char *new_digest, buf[MD5_DIGEST_STRING_LENGTH]; 246 247 new_digest = MD5File(p->fts_accpath, buf); 248 if (!new_digest) { 249 LABEL; 250 printf("%sMD5File: %s: %s\n", tab, p->fts_accpath, 251 strerror(errno)); 252 tab = "\t"; 253 } else if (strcmp(new_digest, s->md5digest)) { 254 LABEL; 255 printf("%sMD5 (%s, %s)\n", tab, s->md5digest, 256 new_digest); 257 tab = "\t"; 258 } 259 } 260 if (s->flags & F_RMD160) { 261 char *new_digest, buf[RMD160_DIGEST_STRING_LENGTH]; 262 263 new_digest = RMD160File(p->fts_accpath, buf); 264 if (!new_digest) { 265 LABEL; 266 printf("%sRMD160File: %s: %s\n", tab, p->fts_accpath, 267 strerror(errno)); 268 tab = "\t"; 269 } else if (strcmp(new_digest, s->rmd160digest)) { 270 LABEL; 271 printf("%sRMD160 (%s, %s)\n", tab, s->rmd160digest, 272 new_digest); 273 tab = "\t"; 274 } 275 } 276 if (s->flags & F_SHA1) { 277 char *new_digest, buf[SHA1_DIGEST_STRING_LENGTH]; 278 279 new_digest = SHA1File(p->fts_accpath, buf); 280 if (!new_digest) { 281 LABEL; 282 printf("%sSHA1File: %s: %s\n", tab, p->fts_accpath, 283 strerror(errno)); 284 tab = "\t"; 285 } else if (strcmp(new_digest, s->sha1digest)) { 286 LABEL; 287 printf("%sSHA1 (%s, %s)\n", tab, s->sha1digest, 288 new_digest); 289 tab = "\t"; 290 } 291 } 292 if (s->flags & F_SHA256) { 293 char *new_digest, buf[SHA256_DIGEST_STRING_LENGTH]; 294 295 new_digest = SHA256File(p->fts_accpath, buf); 296 if (!new_digest) { 297 LABEL; 298 printf("%sSHA256File: %s: %s\n", tab, p->fts_accpath, 299 strerror(errno)); 300 tab = "\t"; 301 } else if (strcmp(new_digest, s->sha256digest)) { 302 LABEL; 303 printf("%sSHA256 (%s, %s)\n", tab, s->sha256digest, 304 new_digest); 305 tab = "\t"; 306 } 307 } 308 if (s->flags & F_SLINK && strcmp(cp = rlink(name), s->slink)) { 309 LABEL; 310 (void)printf("%slink ref (%s, %s)\n", tab, cp, s->slink); 311 } 312 if (s->flags & F_FLAGS && s->file_flags != p->fts_statp->st_flags) { 313 char *db_flags = NULL; 314 char *cur_flags = NULL; 315 316 if ((db_flags = fflagstostr(s->file_flags)) == NULL || 317 (cur_flags = fflagstostr(p->fts_statp->st_flags)) == NULL) { 318 LABEL; 319 (void)printf("%sflags: %s %s\n", tab, p->fts_accpath, 320 strerror(errno)); 321 tab = "\t"; 322 free(db_flags); 323 free(cur_flags); 324 } else { 325 LABEL; 326 REPLACE_COMMA(db_flags); 327 REPLACE_COMMA(cur_flags); 328 printf("%sflags (%s, %s", tab, (*db_flags == '\0') ? 329 "-" : db_flags, 330 (*cur_flags == '\0') ? 331 "-" : cur_flags); 332 tab = "\t"; 333 if (uflag) 334 if (chflags(p->fts_accpath, s->file_flags)) 335 (void)printf(", not modified: %s)\n", 336 strerror(errno)); 337 else 338 (void)printf(", modified)\n"); 339 else 340 (void)printf(")\n"); 341 tab = "\t"; 342 343 free(db_flags); 344 free(cur_flags); 345 } 346 } 347 return (label); 348 } 349 350 char * 351 inotype(u_int type) 352 { 353 switch(type & S_IFMT) { 354 case S_IFBLK: 355 return ("block"); 356 case S_IFCHR: 357 return ("char"); 358 case S_IFDIR: 359 return ("dir"); 360 case S_IFIFO: 361 return ("fifo"); 362 case S_IFREG: 363 return ("file"); 364 case S_IFLNK: 365 return ("link"); 366 case S_IFSOCK: 367 return ("socket"); 368 default: 369 return ("unknown"); 370 } 371 /* NOTREACHED */ 372 } 373 374 static char * 375 ftype(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(char *name) 400 { 401 static char lbuf[PATH_MAX]; 402 int len; 403 404 if ((len = readlink(name, lbuf, sizeof(lbuf)-1)) == -1) 405 error("%s: %s", name, strerror(errno)); 406 lbuf[len] = '\0'; 407 return (lbuf); 408 } 409