1 /* $NetBSD: compare.c,v 1.29 2001/10/09 04:50:00 lukem 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 #ifndef 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.29 2001/10/09 04:50:00 lukem Exp $"); 42 #endif 43 #endif /* not lint */ 44 45 #include <sys/param.h> 46 #include <sys/stat.h> 47 #include <sys/types.h> 48 49 #include <errno.h> 50 #include <fcntl.h> 51 #include <fts.h> 52 #include <md5.h> 53 #include <stdio.h> 54 #include <time.h> 55 #include <unistd.h> 56 57 #include "mtree.h" 58 #include "extern.h" 59 60 #define INDENTNAMELEN 8 61 #define MARK \ 62 do { \ 63 len = printf("%s: ", RP(p)); \ 64 if (len > INDENTNAMELEN) { \ 65 tab = "\t"; \ 66 (void)printf("\n"); \ 67 } else { \ 68 tab = ""; \ 69 (void)printf("%*s", INDENTNAMELEN - (int)len, ""); \ 70 } \ 71 } while (0) 72 #define LABEL if (!label++) MARK 73 74 #define CHANGEFLAGS(path, oflags) \ 75 if (flags != (oflags)) { \ 76 if (!label) { \ 77 MARK; \ 78 (void)printf("%sflags (\"%s\"", tab, \ 79 flags_to_string(p->fts_statp->st_flags, "none")); \ 80 } \ 81 if (chflags(path, flags)) { \ 82 label++; \ 83 (void)printf(", not modified: %s)\n", \ 84 strerror(errno)); \ 85 } else \ 86 (void)printf(", modified to \"%s\")\n", \ 87 flags_to_string(flags, "none")); \ 88 } 89 90 /* SETFLAGS: 91 * given pflags, additionally set those flags specified in sflags and 92 * selected by mask (the other flags are left unchanged). oflags is 93 * passed as reference to check if chflags is necessary. 94 */ 95 #define SETFLAGS(path, sflags, pflags, oflags, mask) \ 96 do { \ 97 flags = ((sflags) & (mask)) | (pflags); \ 98 CHANGEFLAGS(path, oflags); \ 99 } while (0) 100 101 /* CLEARFLAGS: 102 * given pflags, reset the flags specified in sflags and selected by mask 103 * (the other flags are left unchanged). oflags is 104 * passed as reference to check if chflags is necessary. 105 */ 106 #define CLEARFLAGS(path, sflags, pflags, oflags, mask) \ 107 do { \ 108 flags = (~((sflags) & (mask)) & CH_MASK) & (pflags); \ 109 CHANGEFLAGS(path, oflags); \ 110 } while (0) 111 112 int 113 compare(const char *name, NODE *s, FTSENT *p) 114 { 115 u_int32_t len, val, flags; 116 int fd, label; 117 const char *cp; 118 char *tab; 119 char md5buf[35]; 120 121 tab = NULL; 122 label = 0; 123 switch(s->type) { 124 case F_BLOCK: 125 if (!S_ISBLK(p->fts_statp->st_mode)) 126 goto typeerr; 127 break; 128 case F_CHAR: 129 if (!S_ISCHR(p->fts_statp->st_mode)) 130 goto typeerr; 131 break; 132 case F_DIR: 133 if (!S_ISDIR(p->fts_statp->st_mode)) 134 goto typeerr; 135 break; 136 case F_FIFO: 137 if (!S_ISFIFO(p->fts_statp->st_mode)) 138 goto typeerr; 139 break; 140 case F_FILE: 141 if (!S_ISREG(p->fts_statp->st_mode)) 142 goto typeerr; 143 break; 144 case F_LINK: 145 if (!S_ISLNK(p->fts_statp->st_mode)) 146 goto typeerr; 147 break; 148 case F_SOCK: 149 if (!S_ISSOCK(p->fts_statp->st_mode)) { 150 typeerr: LABEL; 151 (void)printf("\ttype (%s, %s)\n", 152 nodetype(s->type), inotype(p->fts_statp->st_mode)); 153 } 154 break; 155 } 156 if (iflag && !uflag) { 157 if (s->flags & F_FLAGS) 158 SETFLAGS(p->fts_accpath, s->st_flags, 159 p->fts_statp->st_flags, p->fts_statp->st_flags, 160 SP_FLGS); 161 return (label); 162 } 163 if (mflag && !uflag) { 164 if (s->flags & F_FLAGS) 165 CLEARFLAGS(p->fts_accpath, s->st_flags, 166 p->fts_statp->st_flags, p->fts_statp->st_flags, 167 SP_FLGS); 168 return (label); 169 } 170 /* Set the uid/gid first, then set the mode. */ 171 if (s->flags & (F_UID | F_UNAME) && s->st_uid != p->fts_statp->st_uid) { 172 LABEL; 173 (void)printf("%suser (%lu, %lu", 174 tab, (u_long)s->st_uid, (u_long)p->fts_statp->st_uid); 175 if (uflag) { 176 if (chown(p->fts_accpath, s->st_uid, -1)) 177 (void)printf(", not modified: %s)\n", 178 strerror(errno)); 179 else 180 (void)printf(", modified)\n"); 181 } else 182 (void)printf(")\n"); 183 tab = "\t"; 184 } 185 if (s->flags & (F_GID | F_GNAME) && s->st_gid != p->fts_statp->st_gid) { 186 LABEL; 187 (void)printf("%sgid (%lu, %lu", 188 tab, (u_long)s->st_gid, (u_long)p->fts_statp->st_gid); 189 if (uflag) { 190 if (chown(p->fts_accpath, -1, s->st_gid)) 191 (void)printf(", not modified: %s)\n", 192 strerror(errno)); 193 else 194 (void)printf(", modified)\n"); 195 } 196 else 197 (void)printf(")\n"); 198 tab = "\t"; 199 } 200 if (s->flags & F_MODE && 201 s->st_mode != (p->fts_statp->st_mode & MBITS)) { 202 if (lflag) { 203 mode_t tmode, mode; 204 205 tmode = s->st_mode; 206 mode = p->fts_statp->st_mode & MBITS; 207 /* 208 * if none of the suid/sgid/etc bits are set, 209 * then if the mode is a subset of the target, 210 * skip. 211 */ 212 if (!((tmode & ~(S_IRWXU|S_IRWXG|S_IRWXO)) || 213 (mode & ~(S_IRWXU|S_IRWXG|S_IRWXO)))) 214 if ((mode | tmode) == tmode) 215 goto skip; 216 } 217 218 LABEL; 219 (void)printf("%spermissions (%#lo, %#lo", 220 tab, (u_long)s->st_mode, 221 (u_long)p->fts_statp->st_mode & MBITS); 222 if (uflag) { 223 if (chmod(p->fts_accpath, s->st_mode)) 224 (void)printf(", not modified: %s)\n", 225 strerror(errno)); 226 else 227 (void)printf(", modified)\n"); 228 } 229 else 230 (void)printf(")\n"); 231 tab = "\t"; 232 skip: 233 } 234 if (s->flags & F_DEV && 235 (s->type == F_BLOCK || s->type == F_CHAR) && 236 s->st_rdev != p->fts_statp->st_rdev) { 237 LABEL; 238 (void)printf("%sdevice (%#x, %#x", 239 tab, s->st_rdev, p->fts_statp->st_rdev); 240 if (uflag) { 241 /* XXXLUKEM: unlink first ? */ 242 if (mknod(p->fts_accpath, s->st_mode, s->st_rdev)) 243 (void)printf(", not modified: %s)\n", 244 strerror(errno)); 245 else 246 (void)printf(", modified)\n"); 247 } else 248 (void)printf(")\n"); 249 tab = "\t"; 250 } 251 if (s->flags & F_NLINK && s->type != F_DIR && 252 s->st_nlink != p->fts_statp->st_nlink) { 253 LABEL; 254 (void)printf("%slink count (%lu, %lu)\n", 255 tab, (u_long)s->st_nlink, (u_long)p->fts_statp->st_nlink); 256 tab = "\t"; 257 } 258 if (s->flags & F_SIZE && s->st_size != p->fts_statp->st_size) { 259 LABEL; 260 (void)printf("%ssize (%lld, %lld)\n", 261 tab, (long long)s->st_size, 262 (long long)p->fts_statp->st_size); 263 tab = "\t"; 264 } 265 /* 266 * XXX 267 * Since utimes(2) only takes a timeval, there's no point in 268 * comparing the low bits of the timespec nanosecond field. This 269 * will only result in mismatches that we can never fix. 270 * 271 * Doesn't display microsecond differences. 272 */ 273 if (s->flags & F_TIME) { 274 struct timeval tv[2]; 275 struct stat *ps = p->fts_statp; 276 time_t smtime = s->st_mtimespec.tv_sec; 277 278 #ifdef BSD4_4 279 time_t pmtime = ps->st_mtimespec.tv_sec; 280 281 TIMESPEC_TO_TIMEVAL(&tv[1], &ps->st_mtimespec); 282 #else 283 time_t pmtime = (time_t)ps->st_mtime; 284 285 tv[1].tv_sec = ps->st_mtime; 286 tv[1].tv_usec = 0; 287 #endif 288 TIMESPEC_TO_TIMEVAL(&tv[0], &s->st_mtimespec); 289 290 if (tv[0].tv_sec != tv[1].tv_sec || 291 tv[0].tv_usec != tv[1].tv_usec) { 292 LABEL; 293 (void)printf("%smodification time (%.24s, ", 294 tab, ctime(&smtime)); 295 (void)printf("%.24s", ctime(&pmtime)); 296 if (tflag) { 297 tv[1] = tv[0]; 298 if (utimes(p->fts_accpath, tv)) 299 (void)printf(", not modified: %s)\n", 300 strerror(errno)); 301 else 302 (void)printf(", modified)\n"); 303 } else 304 (void)printf(")\n"); 305 tab = "\t"; 306 } 307 } 308 /* 309 * XXX 310 * since chflags(2) will reset file times, the utimes() above 311 * may have been useless! oh well, we'd rather have correct 312 * flags, rather than times? 313 */ 314 if ((s->flags & F_FLAGS) && ((s->st_flags != p->fts_statp->st_flags) 315 || mflag || iflag)) { 316 if (s->st_flags != p->fts_statp->st_flags) { 317 LABEL; 318 (void)printf("%sflags (\"%s\" is not ", tab, 319 flags_to_string(s->st_flags, "none")); 320 (void)printf("\"%s\"", 321 flags_to_string(p->fts_statp->st_flags, "none")); 322 } 323 if (uflag) { 324 if (iflag) 325 SETFLAGS(p->fts_accpath, s->st_flags, 326 0, p->fts_statp->st_flags, CH_MASK); 327 else if (mflag) 328 CLEARFLAGS(p->fts_accpath, s->st_flags, 329 0, p->fts_statp->st_flags, SP_FLGS); 330 else 331 SETFLAGS(p->fts_accpath, s->st_flags, 332 0, p->fts_statp->st_flags, 333 (~SP_FLGS & CH_MASK)); 334 } else 335 (void)printf(")\n"); 336 tab = "\t"; 337 } 338 if (s->flags & F_CKSUM) { 339 if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0) { 340 LABEL; 341 (void)printf("%scksum: %s: %s\n", 342 tab, p->fts_accpath, strerror(errno)); 343 tab = "\t"; 344 } else if (crc(fd, &val, &len)) { 345 (void)close(fd); 346 LABEL; 347 (void)printf("%scksum: %s: %s\n", 348 tab, p->fts_accpath, strerror(errno)); 349 tab = "\t"; 350 } else { 351 (void)close(fd); 352 if (s->cksum != val) { 353 LABEL; 354 (void)printf("%scksum (%lu, %lu)\n", 355 tab, s->cksum, (unsigned long)val); 356 } 357 tab = "\t"; 358 } 359 } 360 if (s->flags & F_MD5) { 361 if (MD5File(p->fts_accpath, md5buf) == NULL) { 362 LABEL; 363 (void)printf("%smd5: %s: %s\n", 364 tab, p->fts_accpath, strerror(errno)); 365 tab = "\t"; 366 } else { 367 if (strcmp(s->md5sum, md5buf)) { 368 LABEL; 369 (void)printf("%smd5 (0x%s, 0x%s)\n", 370 tab, s->md5sum, md5buf); 371 } 372 tab = "\t"; 373 } 374 } 375 376 if (s->flags & F_SLINK && strcmp(cp = rlink(name), s->slink)) { 377 LABEL; 378 (void)printf("%slink ref (%s, %s)\n", tab, cp, s->slink); 379 } 380 return (label); 381 } 382 383 const char * 384 inotype(u_int type) 385 { 386 387 return (nodetype(type & S_IFMT)); 388 } 389 390 const char * 391 nodetype(u_int type) 392 { 393 394 switch(type) { 395 case F_BLOCK: 396 case S_IFBLK: 397 return ("block"); 398 case F_CHAR: 399 case S_IFCHR: 400 return ("char"); 401 case F_DIR: 402 case S_IFDIR: 403 return ("dir"); 404 case F_FIFO: 405 case S_IFIFO: 406 return ("fifo"); 407 case F_FILE: 408 case S_IFREG: 409 return ("file"); 410 case F_LINK: 411 case S_IFLNK: 412 return ("link"); 413 case F_SOCK: 414 case S_IFSOCK: 415 return ("socket"); 416 default: 417 return ("unknown"); 418 } 419 /* NOTREACHED */ 420 } 421 422 const char * 423 rlink(const char *name) 424 { 425 static char lbuf[MAXPATHLEN]; 426 int len; 427 428 if ((len = readlink(name, lbuf, sizeof(lbuf))) == -1) 429 mtree_err("%s: %s", name, strerror(errno)); 430 lbuf[len] = '\0'; 431 return (lbuf); 432 } 433