1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 1995-2003 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * module: 29 * eval.c 30 * 31 * purpose: 32 * routines to ascertain the current status of all of the files 33 * described by a set of rules. Some of the routines that update 34 * file status information are also called later (during reconcilation) 35 * to reflect the changes that have been made to files. 36 * 37 * contents: 38 * evaluate top level - evaluate one side of one base 39 * add_file_arg (static) add a file to the list of files to evaluate 40 * eval_file (static) stat a specific file, recurse on directories 41 * walker (static) node visitor for recursive descent 42 * note_info update a file_info structure from a stat structure 43 * do_update (static) update one file_info structure from another 44 * update_info update the baseline file_info from the prevailng side 45 * fakedata (static) make it look like one side hasn't changed 46 * check_inum (static) sanity check to detect wrong-dir errors 47 * add_glob (static) expand a wildcard in an include rule 48 * add_run (static) run a program to generate an include list 49 * 50 * notes: 51 * pay careful attention to the use of the LISTED and EVALUATE 52 * flags in each file description structure. 53 */ 54 55 #pragma ident "%Z%%M% %I% %E% SMI" 56 57 #include <stdio.h> 58 #include <stdlib.h> 59 #include <libgen.h> 60 #include <unistd.h> 61 #include <string.h> 62 #include <glob.h> 63 #include <ftw.h> 64 #include <sys/mkdev.h> 65 #include <errno.h> 66 67 #include "filesync.h" 68 #include "database.h" 69 #include "messages.h" 70 #include "debug.h" 71 72 /* 73 * routines: 74 */ 75 static errmask_t eval_file(struct base *, struct file *); 76 static errmask_t add_file_arg(struct base *, char *); 77 static int walker(const char *, const struct stat *, int, struct FTW *); 78 static errmask_t add_glob(struct base *, char *); 79 static errmask_t add_run(struct base *, char *); 80 static void check_inum(struct file *, int); 81 static void fakedata(struct file *, int); 82 83 /* 84 * globals 85 */ 86 static bool_t usingsrc; /* this pass is on the source side */ 87 static int walk_errs; /* errors found in tree walk */ 88 static struct file *cur_dir; /* base directory for this pass */ 89 static struct base *cur_base; /* base pointer for this pass */ 90 91 /* 92 * routine: 93 * evaluate 94 * 95 * purpose: 96 * to build up a baseline description for all of the files 97 * under one side of one base pair (as specified by the rules 98 * for that base pair). 99 * 100 * parameters: 101 * pointer to the base to be evaluated 102 * source/destination indication 103 * are we restricted to new rules 104 * 105 * returns: 106 * error mask 107 * 108 * notes: 109 * we evaluate source and destination separately, and 110 * reinterpret the include rules on each side (since there 111 * may be wild cards and programs that must be evaluated 112 * in a specific directory context). Similarly the ignore 113 * rules must be interpreted anew for each base. 114 */ 115 errmask_t 116 evaluate(struct base *bp, side_t srcdst, bool_t newrules) 117 { errmask_t errs = 0; 118 char *dir; 119 struct rule *rp; 120 struct file *fp; 121 122 /* see if this base is still relevant */ 123 if ((bp->b_flags & F_LISTED) == 0) 124 return (0); 125 126 /* figure out what this pass is all about */ 127 usingsrc = (srcdst == OPT_SRC); 128 129 /* 130 * the ignore engine maintains considerable per-base-directory 131 * state, and so must be reset at the start of a new tree. 132 */ 133 ignore_reset(); 134 135 /* all evaluation must happen from the appropriate directory */ 136 dir = usingsrc ? bp->b_src_name : bp->b_dst_name; 137 if (chdir(dir) < 0) { 138 fprintf(stderr, gettext(ERR_chdir), dir); 139 140 /* 141 * if we have -n -o we are actually willing to 142 * pretend that nothing has changed on the missing 143 * side. This is actually useful on a disconnected 144 * notebook to ask what has been changed so far. 145 */ 146 if (opt_onesided == (usingsrc ? OPT_DST : OPT_SRC)) { 147 for (fp = bp->b_files; fp; fp = fp->f_next) 148 fakedata(fp, srcdst); 149 150 if (opt_debug & DBG_EVAL) 151 fprintf(stderr, "EVAL: FAKE DATA %s dir=%s\n", 152 usingsrc ? "SRC" : "DST", dir); 153 return (0); 154 } else 155 return (ERR_NOBASE); 156 } 157 158 if (opt_debug & DBG_EVAL) 159 fprintf(stderr, "EVAL: base=%d, %s dir=%s\n", 160 bp->b_ident, usingsrc ? "SRC" : "DST", dir); 161 162 /* assemble the include list */ 163 for (rp = bp->b_includes; rp; rp = rp->r_next) { 164 165 /* see if we are skipping old rules */ 166 if (newrules && ((rp->r_flags & R_NEW) == 0)) 167 continue; 168 169 if (rp->r_flags & R_PROGRAM) 170 errs |= add_run(bp, rp->r_file); 171 else if (rp->r_flags & R_WILD) 172 errs |= add_glob(bp, rp->r_file); 173 else 174 errs |= add_file_arg(bp, rp->r_file); 175 } 176 177 /* assemble the base-specific exclude list */ 178 for (rp = bp->b_excludes; rp; rp = rp->r_next) 179 if (rp->r_flags & R_PROGRAM) 180 ignore_pgm(rp->r_file); 181 else if (rp->r_flags & R_WILD) 182 ignore_expr(rp->r_file); 183 else 184 ignore_file(rp->r_file); 185 186 /* add in the global excludes */ 187 for (rp = omnibase.b_excludes; rp; rp = rp->r_next) 188 if (rp->r_flags & R_WILD) 189 ignore_expr(rp->r_file); 190 else 191 ignore_file(rp->r_file); 192 193 /* 194 * because of restriction lists and new-rules, the baseline 195 * may contain many more files than we are actually supposed 196 * to look at during the impending evaluation/analysis phases 197 * 198 * when LIST arguments are encountered within a rule, we turn 199 * on the LISTED flag for the associated files. We only evaluate 200 * files that have the LISTED flag. We turn the LISTED flag off 201 * after evaluating them because just because a file was enumerated 202 * in the source doesn't mean that will necessarily be enumerated 203 * in the destination. 204 */ 205 for (fp = bp->b_files; fp; fp = fp->f_next) 206 if (fp->f_flags & F_LISTED) { 207 errs |= eval_file(bp, fp); 208 fp->f_flags &= ~F_LISTED; 209 } 210 211 /* note that this base has been evaluated */ 212 bp->b_flags |= F_EVALUATE; 213 214 return (errs); 215 } 216 217 /* 218 * routine: 219 * add_file_arg 220 * 221 * purpose: 222 * to create file node(s) under a specified base for an explictly 223 * included file. 224 * 225 * parameters: 226 * pointer to associated base 227 * name of the file 228 * 229 * returns: 230 * error mask 231 * 232 * notes: 233 * the trick is that an include LIST argument need not be a file 234 * in the base directory, but may be a path passing through 235 * several intermediate directories. If this is the case we 236 * need to ensure that all of those directories are added to 237 * the tree SPARSELY since it is not intended that they be 238 * expanded during the course of evaluation. 239 * 240 * we ignore arguments that end in .. because they have the 241 * potential to walk out of the base tree, because it can 242 * result in different names for a single file, and because 243 * should never be necessary to specify files that way. 244 */ 245 static errmask_t 246 add_file_arg(struct base *bp, char *path) 247 { int i; 248 errmask_t errs = 0; 249 struct file *dp = 0; 250 struct file *fp; 251 char *s, *p; 252 char name[ MAX_NAME ]; 253 254 /* 255 * see if someone is trying to feed us a .. 256 */ 257 if (strcmp(path, "..") == 0 || prefix(path, "../") || 258 suffix(path, "/..") || contains(path, "/../")) { 259 fprintf(stderr, gettext(WARN_ignore), path); 260 return (ERR_MISSING); 261 } 262 263 /* 264 * strip off any trailing "/." or "/" 265 * since noone will miss these, it is safe to actually 266 * take them off the name. When we fall out of this 267 * loop, s will point where the null belongs. We don't 268 * actually null the end of string yet because we want 269 * to leave it pristine for error messages. 270 */ 271 for (s = path; *s; s++); 272 while (s > path) { 273 if (s[-1] == '/') { 274 s--; 275 continue; 276 } 277 if (s[-1] == '.' && s > &path[1] && s[-2] == '/') { 278 s -= 2; 279 continue; 280 } 281 break; 282 } 283 284 /* 285 * skip over leading "/" and "./" (but not over a lone ".") 286 */ 287 for (p = path; p < s; ) { 288 if (*p == '/') { 289 p++; 290 continue; 291 } 292 if (*p == '.' && s > &p[1] && p[1] == '/') { 293 p += 2; 294 continue; 295 } 296 break; 297 } 298 299 /* 300 * if there is nothing left, we're miffed, but done 301 */ 302 if (p >= s) { 303 fprintf(stderr, gettext(WARN_ignore), path); 304 return (ERR_MISSING); 305 } else { 306 /* 307 * this is actually storing a null into the argument, 308 * but it is OK to do this because the stuff we are 309 * truncating really is garbage that noone will ever 310 * want to see. 311 */ 312 *s = 0; 313 path = p; 314 } 315 316 /* 317 * see if there are any restrictions that would force 318 * us to ignore this argument 319 */ 320 if (check_restr(bp, path) == 0) 321 return (0); 322 323 while (*path) { 324 /* lex off the next name component */ 325 for (i = 0; path[i] && path[i] != '/'; i++) 326 name[i] = path[i]; 327 name[i] = 0; 328 329 /* add it into the database */ 330 fp = (dp == 0) ? add_file_to_base(bp, name) 331 : add_file_to_dir(dp, name); 332 333 /* see if this was an intermediate directory */ 334 if (path[i] == '/') { 335 fp->f_flags |= F_LISTED | F_SPARSE; 336 path += i+1; 337 } else { 338 fp->f_flags |= F_LISTED; 339 path += i; 340 } 341 342 dp = fp; 343 } 344 345 return (errs); 346 } 347 348 /* 349 * routine: 350 * eval_file 351 * 352 * purpose: 353 * to evaluate one named file under a particular directory 354 * 355 * parameters: 356 * pointer to base structure 357 * pointer to file structure 358 * 359 * returns: 360 * error mask 361 * filled in evaluations in the baseline 362 * 363 * note: 364 * due to new rules and other restrictions we may not be expected 365 * to evaluate the entire tree. We should only be called on files 366 * that are LISTed, and we should only invoke ourselves recursively 367 * on such files. 368 */ 369 static errmask_t 370 eval_file(struct base *bp, struct file *fp) 371 { errmask_t errs = 0; 372 int rc; 373 char *name; 374 struct file *cp; 375 struct stat statb; 376 377 if (opt_debug & DBG_EVAL) 378 fprintf(stderr, "EVAL: FILE, flags=%s, name=%s\n", 379 showflags(fileflags, fp->f_flags), fp->f_name); 380 381 /* stat the file and fill in the file structure information */ 382 name = get_name(fp); 383 384 #ifdef DBG_ERRORS 385 /* see if we should simulated a stat error on this file */ 386 if (opt_errors && (errno = dbg_chk_error(name, usingsrc ? 's' : 'S'))) 387 rc = -1; 388 else 389 #endif 390 rc = lstat(name, &statb); 391 392 if (rc < 0) { 393 if (opt_debug & DBG_EVAL) 394 fprintf(stderr, "EVAL: FAIL lstat, errno=%d\n", errno); 395 switch (errno) { 396 case EACCES: 397 fp->f_flags |= F_STAT_ERROR; 398 return (ERR_PERM); 399 case EOVERFLOW: 400 fp->f_flags |= F_STAT_ERROR; 401 return (ERR_UNRESOLVED); 402 default: 403 return (ERR_MISSING); 404 } 405 } 406 407 /* record the information we've just gained */ 408 note_info(fp, &statb, usingsrc ? OPT_SRC : OPT_DST); 409 410 /* 411 * checking for ACLs is expensive, so we only do it if we 412 * have been asked to, or if we have reason to believe that 413 * the file has an ACL 414 */ 415 if (opt_acls || fp->f_info[OPT_BASE].f_numacls) 416 (void) get_acls(name, 417 &fp->f_info[usingsrc ? OPT_SRC : OPT_DST]); 418 419 420 /* note that this file has been evaluated */ 421 fp->f_flags |= F_EVALUATE; 422 423 /* if it is not a directory, a simple stat will suffice */ 424 if ((statb.st_mode & S_IFMT) != S_IFDIR) 425 return (0); 426 427 /* 428 * as a sanity check, we look for changes in the I-node 429 * numbers associated with LISTed directories ... on the 430 * assumption that these are high-enough up on the tree 431 * that they aren't likely to change, and so a change 432 * might indicate trouble. 433 */ 434 if (fp->f_flags & F_LISTED) 435 check_inum(fp, usingsrc); 436 437 /* 438 * sparse directories are on the path between a base and 439 * a listed directory. As such, we don't walk these 440 * directories. Rather, we just enumerate the LISTed 441 * files. 442 */ 443 if (fp->f_flags & F_SPARSE) { 444 push_name(fp->f_name); 445 446 /* this directory isn't supposed to be fully walked */ 447 for (cp = fp->f_files; cp; cp = cp->f_next) 448 if (cp->f_flags & F_LISTED) { 449 errs |= eval_file(bp, cp); 450 cp->f_flags &= ~F_LISTED; 451 } 452 pop_name(); 453 } else { 454 /* fully walk the tree beneath this directory */ 455 walk_errs = 0; 456 cur_base = bp; 457 cur_dir = fp; 458 nftw(get_name(fp), &walker, MAX_DEPTH, FTW_PHYS|FTW_MOUNT); 459 errs |= walk_errs; 460 } 461 462 return (errs); 463 } 464 465 /* 466 * routine: 467 * walker 468 * 469 * purpose: 470 * node visitor for recursive directory enumeration 471 * 472 * parameters: 473 * name of file 474 * pointer to stat buffer for file 475 * file type 476 * FTW structure (base name offset, walk-depth) 477 * 478 * returns: 479 * 0 continue 480 * -1 stop 481 * 482 * notes: 483 * Ignoring files is easy, but ignoring directories is harder. 484 * Ideally we would just decline to walk the trees beneath 485 * ignored directories, but ftw doesn't allow the walker to 486 * tell it to "don't enter this directory, but continue". 487 * 488 * Instead, we have to set a global to tell us to ignore 489 * everything under that tree. The variable ignore_level 490 * is set to a level, below which, everything should be 491 * ignored. Once the enumeration rises above that level 492 * again, we clear it. 493 */ 494 static int 495 walker(const char *name, const struct stat *sp, int type, 496 struct FTW *ftwx) 497 { const char *path; 498 struct file *fp; 499 int level; 500 int which; 501 bool_t restr; 502 static struct file *dirstack[ MAX_DEPTH + 1 ]; 503 static int ignore_level = 0; 504 505 path = &name[ftwx->base]; 506 level = ftwx->level; 507 which = usingsrc ? OPT_SRC : OPT_DST; 508 509 /* 510 * see if we are ignoring all files in this sub-tree 511 */ 512 if (ignore_level > 0 && level >= ignore_level) { 513 if (opt_debug & DBG_EVAL) 514 fprintf(stderr, "EVAL: SKIP file=%s\n", name); 515 return (0); 516 } else 517 ignore_level = 0; /* we're through ignoring */ 518 519 #ifdef DBG_ERRORS 520 /* see if we should simulated a stat error on this file */ 521 if (opt_errors && dbg_chk_error(name, usingsrc ? 'n' : 'N')) 522 type = FTW_NS; 523 #endif 524 525 switch (type) { 526 case FTW_F: /* file */ 527 case FTW_SL: /* symbolic link */ 528 /* 529 * filter out files of inappropriate types 530 */ 531 switch (sp->st_mode & S_IFMT) { 532 default: /* anything else we ignore */ 533 return (0); 534 535 case S_IFCHR: 536 case S_IFBLK: 537 case S_IFREG: 538 case S_IFLNK: 539 if (opt_debug & DBG_EVAL) 540 fprintf(stderr, 541 "EVAL: WALK lvl=%d, file=%s\n", 542 level, path); 543 544 /* see if we were told to ignore this one */ 545 if (ignore_check(path)) 546 return (0); 547 548 fp = add_file_to_dir(dirstack[level-1], path); 549 note_info(fp, sp, which); 550 551 /* note that this file has been evaluated */ 552 fp->f_flags |= F_EVALUATE; 553 554 /* see if we should check ACLs */ 555 if ((sp->st_mode & S_IFMT) == S_IFLNK) 556 return (0); 557 558 if (fp->f_info[OPT_BASE].f_numacls || opt_acls) 559 (void) get_acls(name, 560 &fp->f_info[which]); 561 562 return (0); 563 } 564 565 case FTW_D: /* enter directory */ 566 if (opt_debug & DBG_EVAL) 567 fprintf(stderr, "EVAL: WALK lvl=%d, dir=%s\n", 568 level, name); 569 570 /* 571 * if we have been told to ignore this directory, we should 572 * ignore all files under it. Similarly, if we are outside 573 * of our restrictions, we should ignore the entire subtree 574 */ 575 restr = check_restr(cur_base, name); 576 if (restr == FALSE || ignore_check(path)) { 577 ignore_level = level + 1; 578 return (0); 579 } 580 581 fp = (level == 0) ? cur_dir : 582 add_file_to_dir(dirstack[level-1], path); 583 584 note_info(fp, sp, which); 585 586 /* see if we should be checking ACLs */ 587 if (opt_acls || fp->f_info[OPT_BASE].f_numacls) 588 (void) get_acls(name, &fp->f_info[which]); 589 590 /* note that this file has been evaluated */ 591 fp->f_flags |= F_EVALUATE; 592 593 /* note the parent of the children to come */ 594 dirstack[ level ] = fp; 595 596 /* 597 * PROBLEM: given the information that nftw provides us with, 598 * how do we know that we have confirmed the fact 599 * that a file no longer exists. Or to rephrase 600 * this in filesync terms, how do we know when to 601 * set the EVALUATE flag for a file we didn't find. 602 * 603 * if we are going to fully scan this directory (we 604 * are completely within our restrictions) then we 605 * will be confirming the non-existance of files that 606 * used to be here. Thus any file that was in the 607 * base line under this directory should be considered 608 * to have been evaluated (whether we found it or not). 609 * 610 * if, however, we are only willing to scan selected 611 * files (due to restrictions), or the file was not 612 * in the baseline, then we should not assume that this 613 * pass will evaluate it. 614 */ 615 if (restr == TRUE) 616 for (fp = fp->f_files; fp; fp = fp->f_next) { 617 if ((fp->f_flags & F_IN_BASELINE) == 0) 618 continue; 619 fp->f_flags |= F_EVALUATE; 620 } 621 622 return (0); 623 624 case FTW_DP: /* end of directory */ 625 dirstack[ level ] = 0; 626 break; 627 628 case FTW_DNR: /* unreadable directory */ 629 walk_errs |= ERR_PERM; 630 /* FALLTHROUGH */ 631 case FTW_NS: /* unstatable file */ 632 if (opt_debug & DBG_EVAL) 633 fprintf(stderr, "EVAL: walker can't stat/read %s\n", 634 name); 635 fp = (level == 0) ? cur_dir : 636 add_file_to_dir(dirstack[level-1], path); 637 fp->f_flags |= F_STAT_ERROR; 638 walk_errs |= ERR_UNRESOLVED; 639 break; 640 } 641 642 return (0); 643 } 644 645 /* 646 * routine: 647 * note_info 648 * 649 * purpose: 650 * to record information about a file in its file node 651 * 652 * parameters 653 * file node pointer 654 * stat buffer 655 * which file info structure to fill in (0-2) 656 * 657 * returns 658 * void 659 */ 660 void 661 note_info(struct file *fp, const struct stat *sp, side_t which) 662 { struct fileinfo *ip; 663 static int flags[3] = { F_IN_BASELINE, F_IN_SOURCE, F_IN_DEST }; 664 665 ip = &fp->f_info[ which ]; 666 667 ip->f_ino = sp->st_ino; 668 ip->f_d_maj = major(sp->st_dev); 669 ip->f_d_min = minor(sp->st_dev); 670 ip->f_type = sp->st_mode & S_IFMT; 671 ip->f_size = sp->st_size; 672 ip->f_mode = sp->st_mode & S_IAMB; 673 ip->f_uid = sp->st_uid; 674 ip->f_gid = sp->st_gid; 675 ip->f_modtime = sp->st_mtim.tv_sec; 676 ip->f_modns = sp->st_mtim.tv_nsec; 677 ip->f_nlink = sp->st_nlink; 678 ip->f_rd_maj = major(sp->st_rdev); 679 ip->f_rd_min = minor(sp->st_rdev); 680 681 /* indicate where this file has been found */ 682 fp->f_flags |= flags[which]; 683 684 if (opt_debug & DBG_STAT) 685 fprintf(stderr, 686 "STAT: list=%d, file=%s, mod=%08lx.%08lx, nacl=%d\n", 687 which, fp->f_name, ip->f_modtime, ip->f_modns, 688 ip->f_numacls); 689 } 690 691 /* 692 * routine: 693 * do_update 694 * 695 * purpose: 696 * to copy information from one side into the baseline in order 697 * to reflect the effects of recent reconciliation actions 698 * 699 * parameters 700 * fileinfo structure to be updated 701 * fileinfo structure to be updated from 702 * 703 * returns 704 * void 705 * 706 * note: 707 * we play fast and loose with the copying of acl chains 708 * here, but noone is going to free or reuse any of this 709 * memory anyway. None the less, I do feel embarassed. 710 */ 711 static void 712 do_update(struct fileinfo *np, struct fileinfo *ip) 713 { 714 /* get most of the fields from the designated "right" copy */ 715 np->f_type = ip->f_type; 716 np->f_size = ip->f_size; 717 np->f_mode = ip->f_mode; 718 np->f_uid = ip->f_uid; 719 np->f_gid = ip->f_gid; 720 np->f_rd_maj = ip->f_rd_maj; 721 np->f_rd_min = ip->f_rd_min; 722 723 /* see if facls have to be propagated */ 724 np->f_numacls = ip->f_numacls; 725 np->f_acls = ip->f_acls; 726 } 727 728 /* 729 * routine: 730 * update_info 731 * 732 * purpose: 733 * to update the baseline to reflect recent reconcliations 734 * 735 * parameters 736 * file node pointer 737 * which file info structure to trust (1/2) 738 * 739 * returns 740 * void 741 * 742 * note: 743 * after we update this I-node we run down the entire 744 * change list looking for links and update them too. 745 * This is to ensure that when subsequent links get 746 * reconciled, they are already found to be up-to-date. 747 */ 748 void 749 update_info(struct file *fp, side_t which) 750 { 751 /* first update the specified fileinfo structure */ 752 do_update(&fp->f_info[ OPT_BASE ], &fp->f_info[ which ]); 753 754 if (opt_debug & DBG_STAT) 755 fprintf(stderr, 756 "STAT: UPDATE from=%d, file=%s, mod=%08lx.%08lx\n", 757 which, fp->f_name, fp->f_info[ which ].f_modtime, 758 fp->f_info[ which ].f_modns); 759 } 760 761 /* 762 * routine: 763 * fakedata 764 * 765 * purpose: 766 * to populate a tree we cannot analyze with information from the baseline 767 * 768 * parameters: 769 * file to be faked 770 * which side to fake 771 * 772 * notes: 773 * We would never use this for real reconciliation, but it is useful 774 * if a disconnected notebook user wants to find out what has been 775 * changed so far. We only do this if we are notouch and oneway. 776 */ 777 static void 778 fakedata(struct file *fp, int which) 779 { struct file *lp; 780 781 /* pretend we actually found the file */ 782 fp->f_flags |= (which == OPT_SRC) ? F_IN_SOURCE : F_IN_DEST; 783 784 /* update the specified side from the baseline */ 785 do_update(&fp->f_info[ which ], &fp->f_info[ OPT_BASE ]); 786 fp->f_info[which].f_nlink = (which == OPT_SRC) ? fp->f_s_nlink : 787 fp->f_d_nlink; 788 fp->f_info[which].f_modtime = (which == OPT_SRC) ? fp->f_s_modtime : 789 fp->f_d_modtime; 790 791 for (lp = fp->f_files; lp; lp = lp->f_next) 792 fakedata(lp, which); 793 } 794 795 /* 796 * routine: 797 * check_inum 798 * 799 * purpose: 800 * sanity check inode #s on directories that are unlikely to change 801 * 802 * parameters: 803 * pointer to file node 804 * are we using the source 805 * 806 * note: 807 * the purpose of this sanity check is to catch a case where we 808 * have somehow been pointed at a directory that is not the one 809 * we expected to be reconciling against. It could happen if a 810 * variable wasn't properly set, or if we were in a new domain 811 * where an old path no longer worked. This could result in 812 * bazillions of inappropriate propagations and deletions. 813 */ 814 void 815 check_inum(struct file *fp, int src) 816 { struct fileinfo *ip; 817 818 /* 819 * we validate the inode number and the major device numbers ... minor 820 * device numbers for NFS devices are arbitrary 821 */ 822 if (src) { 823 ip = &fp->f_info[ OPT_SRC ]; 824 if (ip->f_ino == fp->f_s_inum && ip->f_d_maj == fp->f_s_maj) 825 return; 826 827 /* if file was newly created/deleted, this isn't warnable */ 828 if (fp->f_s_inum == 0 || ip->f_ino == 0) 829 return; 830 831 if (opt_verbose) 832 fprintf(stdout, V_change, fp->f_name, TXT_src, 833 fp->f_s_maj, fp->f_s_min, fp->f_s_inum, 834 ip->f_d_maj, ip->f_d_min, ip->f_ino); 835 } else { 836 ip = &fp->f_info[ OPT_DST ]; 837 if (ip->f_ino == fp->f_d_inum && ip->f_d_maj == fp->f_d_maj) 838 return; 839 840 /* if file was newly created/deleted, this isn't warnable */ 841 if (fp->f_d_inum == 0 || ip->f_ino == 0) 842 return; 843 844 if (opt_verbose) 845 fprintf(stdout, V_change, fp->f_name, TXT_dst, 846 fp->f_d_maj, fp->f_d_min, fp->f_d_inum, 847 ip->f_d_maj, ip->f_d_min, ip->f_ino); 848 } 849 850 /* note that something has changed */ 851 inum_changes++; 852 } 853 854 /* 855 * routine: 856 * add_glob 857 * 858 * purpose: 859 * to evaluate a wild-carded expression into names, and add them 860 * to the evaluation list. 861 * 862 * parameters: 863 * base 864 * expression 865 * 866 * returns: 867 * error mask 868 * 869 * notes: 870 * we don't want to allow any patterns to expand to a . because 871 * that could result in re-evaluation of a tree under a different 872 * name. The real thing we are worried about here is ".*" which 873 * is meant to pick up . files, but shouldn't pick up . and .. 874 */ 875 static errmask_t 876 add_glob(struct base *bp, char *expr) 877 { int i; 878 errmask_t errs = 0; 879 #ifndef BROKEN_GLOB 880 glob_t gt; 881 char *s; 882 883 /* expand the regular expression */ 884 i = glob(expr, GLOB_NOSORT, 0, >); 885 if (i == GLOB_NOMATCH) 886 return (ERR_MISSING); 887 if (i) { 888 /* this shouldn't happen, so it's cryptic message time */ 889 fprintf(stderr, "EVAL: add_glob globfail expr=%s, ret=%d\n", 890 expr, i); 891 return (ERR_OTHER); 892 } 893 894 for (i = 0; i < gt.gl_pathc; i++) { 895 /* make sure we don't let anything expand to a . */ 896 s = basename(gt.gl_pathv[i]); 897 if (strcmp(s, ".") == 0) { 898 fprintf(stderr, gettext(WARN_ignore), gt.gl_pathv[i]); 899 errs |= ERR_MISSING; 900 continue; 901 } 902 903 errs |= add_file_arg(bp, gt.gl_pathv[i]); 904 } 905 906 globfree(>); 907 #else 908 /* 909 * in 2.4 the glob function was completely broken. The 910 * easiest way to get around this problem is to just ask 911 * the shell to do the work for us. This is much slower 912 * but produces virtually identical results. Given that 913 * the 2.4 version is internal use only, I probably won't 914 * worry about the performance difference (less than 2 915 * seconds for a typical filesync command, and no hit 916 * at all if they don't use regular expressions in 917 * their LIST rules). 918 */ 919 char cmdbuf[MAX_LINE]; 920 921 sprintf(cmdbuf, "ls -d %s 2> /dev/null", expr); 922 errs |= add_run(bp, cmdbuf); 923 #endif 924 925 return (errs); 926 } 927 928 929 /* 930 * routine: 931 * add_run 932 * 933 * purpose: 934 * to run a command and capture the names it outputs in the 935 * evaluation list. 936 * 937 * parameters 938 * base 939 * command 940 * 941 * returns: 942 * error mask 943 */ 944 static errmask_t 945 add_run(struct base *bp, char *cmd) 946 { char *s, *p; 947 FILE *fp; 948 char inbuf[ MAX_LINE ]; 949 errmask_t errs = 0; 950 int added = 0; 951 952 if (opt_debug & DBG_EVAL) 953 fprintf(stderr, "EVAL: RUN %s\n", cmd); 954 955 /* run the command and collect its ouput */ 956 fp = popen(cmd, "r"); 957 if (fp == NULL) { 958 fprintf(stderr, gettext(ERR_badrun), cmd); 959 return (ERR_OTHER); 960 } 961 962 while (fgets(inbuf, sizeof (inbuf), fp) != 0) { 963 /* strip off any trailing newline */ 964 for (s = inbuf; *s && *s != '\n'; s++); 965 *s = 0; 966 967 /* skip any leading white space */ 968 for (s = inbuf; *s == ' ' || *s == '\t'; s++); 969 970 /* make sure we don't let anything expand to a . */ 971 p = basename(s); 972 if (strcmp(p, ".") == 0) { 973 fprintf(stderr, gettext(WARN_ignore), s); 974 errs |= ERR_MISSING; 975 continue; 976 } 977 978 /* add this file to the list */ 979 if (*s) { 980 errs |= add_file_arg(bp, s); 981 added++; 982 } 983 } 984 985 pclose(fp); 986 987 #ifdef BROKEN_GLOB 988 /* 989 * if we are being used to simulate libc glob, and we didn't 990 * return anything, we should probably assume that the regex 991 * was unable to match anything 992 */ 993 if (added == 0) 994 errs |= ERR_MISSING; 995 #endif 996 return (errs); 997 }