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