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
evaluate(struct base * bp,side_t srcdst,bool_t newrules)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
add_file_arg(struct base * bp,char * path)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
eval_file(struct base * bp,struct file * fp)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
walker(const char * name,const struct stat * sp,int type,struct FTW * ftwx)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
note_info(struct file * fp,const struct stat * sp,side_t which)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
do_update(struct fileinfo * np,struct fileinfo * ip)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
update_info(struct file * fp,side_t which)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
fakedata(struct file * fp,int which)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
check_inum(struct file * fp,int src)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
add_glob(struct base * bp,char * expr)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
add_run(struct base * bp,char * cmd)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